Skip to content

Commit

Permalink
gotypes: stop directing people to golang.org/x/tools/go/loader
Browse files Browse the repository at this point in the history
It is very obsolete. Use go/packages instead.

Also, add a go generate command to weave the README.

Fixes golang/go#60593

Change-Id: Ifaf3ffa588dc3e65fb1e96b39b249a9084d66451
Reviewed-on: https://go-review.googlesource.com/c/example/+/534695
Reviewed-by: Robert Findley <[email protected]>
Run-TryBot: Robert Findley <[email protected]>
Auto-Submit: Alan Donovan <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
Commit-Queue: Alan Donovan <[email protected]>
  • Loading branch information
adonovan authored and gopherbot committed Oct 13, 2023
1 parent d9923f6 commit 1d6d240
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 143 deletions.
8 changes: 6 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ module golang.org/x/example

go 1.18

require golang.org/x/tools v0.0.0-20210112183307-1e6ecd4bf1b0
require golang.org/x/tools v0.14.0

require gopkg.in/yaml.v3 v3.0.1 // indirect
require (
golang.org/x/mod v0.13.0 // indirect
golang.org/x/sys v0.13.0 // indirect
gopkg.in/yaml.v3 v3.0.1
)
32 changes: 8 additions & 24 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,27 +1,11 @@
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20210112183307-1e6ecd4bf1b0 h1:iZhiQWrjyEuXG495d9MXkcmhrlxbQyGp0uNBY+YBZDk=
golang.org/x/tools v0.0.0-20210112183307-1e6ecd4bf1b0/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
108 changes: 60 additions & 48 deletions gotypes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ constant expressions, as we'll see in



The [`golang.org/x/tools/go/loader` package](https://pkg.go.dev/golang.org/x/tools/go/loader)
The [`golang.org/x/tools/go/packages` package](https://pkg.go.dev/golang.org/x/tools/go/packages)
from the `x/tools` repository is a client of the type
checker that loads, parses, and type-checks a complete Go program from
source code.
Expand Down Expand Up @@ -2190,13 +2190,11 @@ programs.
```
var bytesFlag = flag.Int("bytes", 48, "maximum parameter size in bytes")
var sizeof = (&types.StdSizes{8, 8}).Sizeof // the sizeof function
func PrintHugeParams(fset *token.FileSet, info *types.Info, files []*ast.File) {
func PrintHugeParams(fset *token.FileSet, info *types.Info, sizes types.Sizes, files []*ast.File) {
checkTuple := func(descr string, tuple *types.Tuple) {
for i := 0; i < tuple.Len(); i++ {
v := tuple.At(i)
if sz := sizeof(v.Type()); sz > int64(*bytesFlag) {
if sz := sizes.Sizeof(v.Type()); sz > int64(*bytesFlag) {
fmt.Printf("%s: %q %s: %s = %d bytes\n",
fset.Position(v.Pos()),
v.Name(), descr, v.Type(), sz)
Expand Down Expand Up @@ -2296,25 +2294,28 @@ ran a `go install` or `go build -i` command.



The [`golang.org/tools/x/go/loader` package](https://pkg.go.dev/golang.org/x/tools/go/loader)
provides an alternative `Importer` that addresses
some of these problems.
It loads a complete program from source, performing
[`cgo`](https://golang.org/cmd/cgo/cgo) preprocessing if
necessary, followed by parsing and type-checking.
The [`golang.org/tools/x/go/packages`
package](https://pkg.go.dev/golang.org/x/tools/go/packages) provides
a comprehensive means of loading packages from source.
It runs `go list` to query the project metadata,
performs [`cgo`](https://golang.org/cmd/cgo/cgo) preprocessing if necessary,
reads and parses the source files,
and optionally type-checks each package.
It can load a whole program from source, or load just the initial
packages from source and load all their dependencies from export data.
It loads independent packages in parallel to hide I/O latency, and
detects and reports import cycles.
For each package, it provides the `types.Package` containing the
package's lexical environment, the list of `ast.File` syntax
trees for each file in the package, the `types.Info` containing
type information for each syntax node, and a list of type errors
associated with that package.
(Please be aware that the `go/loader` package's API is likely to
change before it finally stabilizes.)



The `doc` program below demonstrates a simple use of the loader.
type information for each syntax node, a list of type errors
associated with that package, and other information too.
Since some of this information is more costly to compute,
the API allows you to select which parts you need,
but since this is a tutorial we'll generally request complete
information so that it is easier to explore.

The `doc` program below demonstrates a simple use of `go/packages`.
It is a rudimentary implementation of `go doc` that prints the type,
methods, and documentation of the package-level object specified on
the command line.
Expand All @@ -2324,10 +2325,10 @@ Here's an example:
```
$ ./doc net/http File
type net/http.File interface{Readdir(count int) ([]os.FileInfo, error); Seek(offset int64, whence int) (int64, error); Stat() (os.FileInfo, error); io.Closer; io.Reader}
/go/src/io/io.go:92:2: method (net/http.File) Close() error
/go/src/io/io.go:71:2: method (net/http.File) Read(p []byte) (n int, err error)
$GOROOT/src/io/io.go:92:2: method (net/http.File) Close() error
$GOROOT/src/io/io.go:71:2: method (net/http.File) Read(p []byte) (n int, err error)
/go/src/net/http/fs.go:65:2: method (net/http.File) Readdir(count int) ([]os.FileInfo, error)
/go/src/net/http/fs.go:66:2: method (net/http.File) Seek(offset int64, whence int) (int64, error)
$GOROOT/src/net/http/fs.go:66:2: method (net/http.File) Seek(offset int64, whence int) (int64, error)
/go/src/net/http/fs.go:67:2: method (net/http.File) Stat() (os.FileInfo, error)
A File is returned by a FileSystem's Open method and can be
Expand All @@ -2340,33 +2341,39 @@ The methods should behave the same as those on an *os.File.
Observe that it prints the correct location of each method
declaration, even though, due to embedding, some of
`http.File`'s methods were declared in another package.
Here's the first part of the program, showing how to load an entire
program starting from the single package, `pkgpath`:
Here's the first part of the program, showing how to load
complete type information including typed syntax,
for a single package `pkgpath`,
plus exported type information for its dependencies.


// go get golang.org/x/example/gotypes/doc

```
pkgpath, name := os.Args[1], os.Args[2]
// The loader loads a complete Go program from source code.
conf := loader.Config{ParserMode: parser.ParseComments}
conf.Import(pkgpath)
lprog, err := conf.Load()
// Load complete type information for the specified packages,
// along with type-annotated syntax.
// Types for dependencies are loaded from export data.
conf := &packages.Config{Mode: packages.LoadSyntax}
pkgs, err := packages.Load(conf, pkgpath)
if err != nil {
log.Fatal(err) // load error
log.Fatal(err) // failed to load anything
}
if packages.PrintErrors(pkgs) > 0 {
os.Exit(1) // some packages contained errors
}
// Find the package and package-level object.
pkg := lprog.Package(pkgpath).Pkg
obj := pkg.Scope().Lookup(name)
pkg := pkgs[0]
obj := pkg.Types.Scope().Lookup(name)
if obj == nil {
log.Fatalf("%s.%s not found", pkg.Path(), name)
log.Fatalf("%s.%s not found", pkg.Types.Path(), name)
}
```


Notice that we instructed the parser to retain comments during parsing.
By default, `go/packages`, instructs the parser to retain comments during parsing.
The rest of the program prints the output:


Expand All @@ -2376,20 +2383,26 @@ The rest of the program prints the output:
// Print the object and its methods (incl. location of definition).
fmt.Println(obj)
for _, sel := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
fmt.Printf("%s: %s\n", lprog.Fset.Position(sel.Obj().Pos()), sel)
fmt.Printf("%s: %s\n", pkg.Fset.Position(sel.Obj().Pos()), sel)
}
// Find the path from the root of the AST to the object's position.
// Walk up to the enclosing ast.Decl for the doc comment.
_, path, _ := lprog.PathEnclosingInterval(obj.Pos(), obj.Pos())
for _, n := range path {
switch n := n.(type) {
case *ast.GenDecl:
fmt.Println("\n", n.Doc.Text())
return
case *ast.FuncDecl:
fmt.Println("\n", n.Doc.Text())
return
for _, file := range pkg.Syntax {
pos := obj.Pos()
if !(file.FileStart <= pos && pos < file.FileEnd) {
continue // not in this file
}
path, _ := astutil.PathEnclosingInterval(file, pos, pos)
for _, n := range path {
switch n := n.(type) {
case *ast.GenDecl:
fmt.Println("\n", n.Doc.Text())
return
case *ast.FuncDecl:
fmt.Println("\n", n.Doc.Text())
return
}
}
}
```
Expand Down Expand Up @@ -2535,11 +2548,10 @@ helper function
[`astutil.PathEnclosingInterval`](https://pkg.go.dev/golang.org/x/tools/go/ast/astutil#PathEnclosingInterval).
It returns the enclosing `ast.Node`, and all its ancestors up to
the root of the file.
You must know which file `*ast.File` the `token.Pos` belongs to.
Alternatively, you can search an entire program loaded by the
`loader` package, using
[`(*loader.Program).PathEnclosingInterval`](https://pkg.go.dev/golang.org/x/tools/go/loader#Program.PathEnclosingInterval).

If you don't know which file `*ast.File` the `token.Pos` belongs to,
you can iterate over the parsed files of the package and quickly test
whether its position falls within the file's range,
from `File.FileStart` to `File.FileEnd`.


To map **from an `Object` to its declaring syntax**, call
Expand Down
60 changes: 36 additions & 24 deletions gotypes/doc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ package main
import (
"fmt"
"go/ast"
"go/parser"
"log"
"os"

// TODO: these will use std go/types after Feb 2016
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/types/typeutil"
)

Expand All @@ -20,53 +19,66 @@ func main() {
//!+part1
pkgpath, name := os.Args[1], os.Args[2]

// The loader loads a complete Go program from source code.
conf := loader.Config{ParserMode: parser.ParseComments}
conf.Import(pkgpath)
lprog, err := conf.Load()
// Load complete type information for the specified packages,
// along with type-annotated syntax.
// Types for dependencies are loaded from export data.
conf := &packages.Config{Mode: packages.LoadSyntax}
pkgs, err := packages.Load(conf, pkgpath)
if err != nil {
log.Fatal(err) // load error
log.Fatal(err) // failed to load anything
}
if packages.PrintErrors(pkgs) > 0 {
os.Exit(1) // some packages contained errors
}

// Find the package and package-level object.
pkg := lprog.Package(pkgpath).Pkg
obj := pkg.Scope().Lookup(name)
pkg := pkgs[0]
obj := pkg.Types.Scope().Lookup(name)
if obj == nil {
log.Fatalf("%s.%s not found", pkg.Path(), name)
log.Fatalf("%s.%s not found", pkg.Types.Path(), name)
}
//!-part1
//!+part2

// Print the object and its methods (incl. location of definition).
fmt.Println(obj)
for _, sel := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
fmt.Printf("%s: %s\n", lprog.Fset.Position(sel.Obj().Pos()), sel)
fmt.Printf("%s: %s\n", pkg.Fset.Position(sel.Obj().Pos()), sel)
}

// Find the path from the root of the AST to the object's position.
// Walk up to the enclosing ast.Decl for the doc comment.
_, path, _ := lprog.PathEnclosingInterval(obj.Pos(), obj.Pos())
for _, n := range path {
switch n := n.(type) {
case *ast.GenDecl:
fmt.Println("\n", n.Doc.Text())
return
case *ast.FuncDecl:
fmt.Println("\n", n.Doc.Text())
return
for _, file := range pkg.Syntax {
pos := obj.Pos()
if !(file.FileStart <= pos && pos < file.FileEnd) {
continue // not in this file
}
path, _ := astutil.PathEnclosingInterval(file, pos, pos)
for _, n := range path {
switch n := n.(type) {
case *ast.GenDecl:
fmt.Println("\n", n.Doc.Text())
return
case *ast.FuncDecl:
fmt.Println("\n", n.Doc.Text())
return
}
}
}
//!-part2
}

// (The $GOROOT below is the actual string that appears in file names
// loaded from export data for packages in the standard library.)

/*
//!+output
$ ./doc net/http File
type net/http.File interface{Readdir(count int) ([]os.FileInfo, error); Seek(offset int64, whence int) (int64, error); Stat() (os.FileInfo, error); io.Closer; io.Reader}
/go/src/io/io.go:92:2: method (net/http.File) Close() error
/go/src/io/io.go:71:2: method (net/http.File) Read(p []byte) (n int, err error)
$GOROOT/src/io/io.go:92:2: method (net/http.File) Close() error
$GOROOT/src/io/io.go:71:2: method (net/http.File) Read(p []byte) (n int, err error)
/go/src/net/http/fs.go:65:2: method (net/http.File) Readdir(count int) ([]os.FileInfo, error)
/go/src/net/http/fs.go:66:2: method (net/http.File) Seek(offset int64, whence int) (int64, error)
$GOROOT/src/net/http/fs.go:66:2: method (net/http.File) Seek(offset int64, whence int) (int64, error)
/go/src/net/http/fs.go:67:2: method (net/http.File) Stat() (os.FileInfo, error)
A File is returned by a FileSystem's Open method and can be
Expand Down
3 changes: 3 additions & 0 deletions gotypes/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//go:generate bash -c "go run ../internal/cmd/weave/weave.go ./go-types.md > README.md"

package gotypes
Loading

0 comments on commit 1d6d240

Please sign in to comment.