Skip to content

go/parser: deprecate parser.ParseDir (which returns deprecated type ast.Package) #71122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
soerenkoehler opened this issue Jan 4, 2025 · 19 comments

Comments

@soerenkoehler
Copy link

soerenkoehler commented Jan 4, 2025

Go version

1.23.4

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/soeren/.cache/go-build'
GOENV='/home/soeren/.config/go/env'
GOEXE=''
GOEXPERIMENT='nocoverageredesign'
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/soeren/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/soeren/go'
GOPRIVATE=''
GOPROXY='direct'
GOROOT='/opt/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.23.4'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/soeren/.config/go/telemetry'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/soeren/usrdev/go-util-mutation/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2925151345=/tmp/go-build -gno-record-gcc-switches'

What did you do?

Write some simple code using the go/ast and go/parser:

func createAST() {
	ast := map[string]*ast.Package{}
	fset := token.NewFileSet()
	chain := &util.ChainContext{}

	chain.Chain(func() {
		ast, chain.Err = parser.ParseDir(fset, ".", nil, 0)
	}).Chain(func() {
		chain.Err = format.Node(os.Stdout, fset, ast)
	}).ChainFatal("Error")
}

What did you see happen?

The IDE marks ast.Package as deprecated conforming to the comment on the type. But parser.ParseDir() nevertheless references ast.Package in its return type.

What did you expect to see?

Either:

  • Having deprecated types consistently replaced in the stdlib. Thus, let ParseDir() use the recommended replacement type.

Or:

  • Having ParseDir() marked as deprecated too.
@mateusz834 mateusz834 changed the title [consistency] type ast.Package is deprecated but nevertheless used by parser.ParseDir() go/parser: type ast.Package is deprecated but nevertheless used by parser.ParseDir() Jan 4, 2025
@gabyhelp
Copy link

gabyhelp commented Jan 4, 2025

Related Issues

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

@adonovan
Copy link
Member

adonovan commented Jan 4, 2025

What did you expect to see?
Either: Having deprecated types consistently replaced in the stdlib. Thus, let ParseDir() use the recommended replacement type.

ParseDir cannot be compatibly changed to return the recommended replacement type.

Or: Having ParseDir() marked as deprecated to.

I think parser.ParseDir is almost never the best solution to a problem, and it returns deprecated data structures, so it does make sense to deprecate it. A more robust solution is to use packages.Load, which invokes go list (or the appropriate build metadata query tool) to get the correct answer.

@adonovan adonovan changed the title go/parser: type ast.Package is deprecated but nevertheless used by parser.ParseDir() go/parser: deprecate parser.ParseDir (which returns deprecated type ast.Package) Jan 4, 2025
@adonovan adonovan changed the title go/parser: deprecate parser.ParseDir (which returns deprecated type ast.Package) proposal: go/parser: deprecate parser.ParseDir (which returns deprecated type ast.Package) Jan 4, 2025
@gopherbot gopherbot added this to the Proposal milestone Jan 4, 2025
@adonovan adonovan moved this to Incoming in Proposals Jan 4, 2025
@rsc
Copy link
Contributor

rsc commented Feb 5, 2025

It sounds like ast.Package is deprecated, parser.ParseDir returns ast.Package, and we agree that people should not use parser.ParseDir anymore. So it seems like we should as a matter of logic also deprecate parser.ParseDir.

@rsc
Copy link
Contributor

rsc commented Feb 5, 2025

Have all remaining concerns about this proposal been addressed?

The proposal is to mark parser.ParseDir deprecated. It will still exist and be usable, but it returns ast.Package which is already deprecated.

@rsc rsc moved this from Incoming to Active in Proposals Feb 5, 2025
@rsc
Copy link
Contributor

rsc commented Feb 5, 2025

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
— rsc for the proposal review group

@willfaught
Copy link
Contributor

It seems a shame to lose “parse directory” functionality in the stdlib. Package wasn’t that useful, but I’d rather un-deprecate it if it meant keeping ParseDir. Perhaps an alternative is to make a ParseFiles function that returns a slice of Files that ParseDir calls under the hood.

@adonovan
Copy link
Member

adonovan commented Feb 7, 2025

It seems a shame to lose “parse directory” functionality in the stdlib. Package wasn’t that useful, but I’d rather un-deprecate it if it meant keeping ParseDir. Perhaps an alternative is to make a ParseFiles function that returns a slice of Files that ParseDir calls under the hood.

We won't be losing any functionality; the function will still be there. But users will be informed that it's really not a satisfactory solution to most problems.

@willfaught
Copy link
Contributor

I know, I meant non-deprecated functionality. Aside from the container type the Files are returned in, it’s a perfectly satisfactory solution to parsing a directory. If there’s an argument that it’s harmful because people should use the packages package instead, that would be interesting, but I haven’t heard it.

@ianlancetaylor
Copy link
Member

parser.ParseDir doesn't respect go:build tags or build tags derived from file names. packages.Load handles them the same way that the go tool does. Or you can handle build tags yourself using the go/build/constraint package, and use parser.ParseFile for each file rather than parser.ParseDir. In any case the point is that ParseDir is a bit of a footgun that will work for many packages but also fail for many.

parser.ParseDir reads the Go files in a single directory, but returns a slice of packages. In today's Go world, that is weird; a directory holds a single package.

@willfaught
Copy link
Contributor

Should the “packages” package’s functionality be added to the “parser” package to provide a working package parser in the stdlib?

@jimmyfrasche
Copy link
Member

The parser works. But it doesn't know which files to parse. ParseDir is based on outdated build logic that may work for most packages (most being simple) but it doesn't handle lots of edge cases. There's go/build which handles more cases but basically at this point you need to get the list of what files to parse from the go command as that is what ultimately handles all the build logic. This is what go/packages does.

(It would be nice if there were a golang.org/x lib for the go command that handled all the encoding decoding and executing of all the subcommands as that's tedious to do and makes it hard to keep up with changes... cough cough)

@adonovan
Copy link
Member

adonovan commented Feb 8, 2025

Should the “packages” package’s functionality be added to the “parser” package to provide a working package parser in the stdlib?

No, because they have different responsibilities: the parser parses the content of a file; the go/packages package loads and optionally parses and type-checks a set of packages, and perhaps their dependencies. The implementation of go/packages is fairly substantial.

It would be nice if there were a golang.org/x lib for the go command

I agree. x/tools has an internal one but it's not nearly in a state to share.

@rsc
Copy link
Contributor

rsc commented Feb 12, 2025

It's fine to wish it was better, but it's not. This is marking it with the truth that it's not that great.
The better thing is packages.Load.

@rsc
Copy link
Contributor

rsc commented Feb 13, 2025

Based on the discussion above, this proposal seems like a likely accept.
— rsc for the proposal review group

The proposal is to mark parser.ParseDir deprecated. It will still exist and be usable, but it returns ast.Package which is already deprecated.

@rsc rsc moved this from Active to Likely Accept in Proposals Feb 13, 2025
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/649275 mentions this issue: go/parser: deprecate parser.ParseDir

@aclements
Copy link
Member

No change in consensus, so accepted. 🎉
This issue now tracks the work of implementing the proposal.
— aclements for the proposal review group

The proposal is to mark parser.ParseDir deprecated. It will still exist and be usable, but it returns ast.Package which is already deprecated.

@aclements aclements moved this from Likely Accept to Accepted in Proposals Feb 19, 2025
@aclements aclements changed the title proposal: go/parser: deprecate parser.ParseDir (which returns deprecated type ast.Package) go/parser: deprecate parser.ParseDir (which returns deprecated type ast.Package) Feb 19, 2025
@aclements aclements modified the milestones: Proposal, Backlog Feb 19, 2025
@mateusz834
Copy link
Member

go/ast also has few functions that accept *ast.Package. Should we deprecate them also?

func FilterPackage(pkg *Package, f Filter) bool
func PackageExports(pkg *Package) bool
func MergePackageFiles(pkg *Package, mode MergeMode) *File

@mateusz834
Copy link
Member

Also in go/doc:

func New(pkg *ast.Package, importPath string, mode Mode) *Package

Seems like NewFromFiles should be used now instead.

@adonovan
Copy link
Member

I agree that doc.New should be deprecated, but I don't think we can retroactively sneak it into this proposal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Accepted
Development

No branches or pull requests