| // Copyright 2026 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| //go:build ignore | |
| // The objectpath command is a debugging tool that prints | |
| // the object path of each symbol declared in a package. | |
| // | |
| // An object path is a unique identifier for an exported Go language symbol | |
| // (function, type, variable, etc) relative to its declaring package. | |
| // See [golang.org/x/tools/go/types/objectpath] for details. | |
| // | |
| // Usage: go run ./go/types/objectpath/main.go [packages...] | |
| package main | |
| import ( | |
| "cmp" | |
| "flag" | |
| "fmt" | |
| "go/types" | |
| "log" | |
| "maps" | |
| "os" | |
| "path/filepath" | |
| "slices" | |
| "strconv" | |
| "golang.org/x/tools/go/packages" | |
| "golang.org/x/tools/go/types/objectpath" | |
| "golang.org/x/tools/internal/typesinternal" | |
| ) | |
| func main() { | |
| log.SetPrefix("objectpath: ") | |
| log.SetFlags(0) | |
| flag.Parse() | |
| exitcode := 0 | |
| cfg := &packages.Config{Mode: packages.LoadSyntax} | |
| pkgs, err := packages.Load(cfg, flag.Args()...) | |
| if err != nil { | |
| log.Fatalf("packages.Load: %v", err) | |
| } | |
| if packages.PrintErrors(pkgs) > 0 { | |
| exitcode = 1 | |
| } | |
| for _, pkg := range pkgs { | |
| var ( | |
| fset = pkg.Fset | |
| objectpathFor = new(objectpath.Encoder).For | |
| ) | |
| // objString formats a symbol relative to this package. | |
| objString := func(obj types.Object) string { | |
| return types.ObjectString(obj, func(p *types.Package) string { | |
| if p == pkg.Types { | |
| return "" | |
| } | |
| return p.Name() | |
| }) | |
| } | |
| // print prints a symbol's name, declared location, and object path. | |
| print := func(obj types.Object) { | |
| var got string | |
| path, err := objectpathFor(obj) | |
| if err != nil { | |
| if mayFail(obj) { | |
| return | |
| } | |
| exitcode = 1 | |
| got = "error: " + err.Error() | |
| } else { | |
| got = strconv.Quote(string(path)) | |
| } | |
| posn := fset.Position(obj.Pos()) | |
| posn.Filename = filepath.Base(posn.Filename) | |
| fmt.Printf("%20s: %v = %s\n", posn.String(), objString(obj), got) | |
| } | |
| // Gather all objects declared in this package, in source order. | |
| objs := slices.Collect(maps.Values(pkg.TypesInfo.Defs)) | |
| objs = slices.DeleteFunc(objs, func(obj types.Object) bool { | |
| return obj == nil | |
| }) | |
| slices.SortFunc(objs, func(x, y types.Object) int { | |
| return cmp.Compare(x.Pos(), y.Pos()) | |
| }) | |
| // Show each object's path. | |
| for _, obj := range objs { | |
| if obj != nil { | |
| print(obj) | |
| } | |
| } | |
| } | |
| if exitcode != 0 { | |
| log.Printf("there were errors") | |
| } | |
| os.Exit(exitcode) | |
| } | |
| // mayFail reports whether [objectpath.Encoder.For] is | |
| // permitted to fail for this kind of symbol. | |
| func mayFail(obj types.Object) bool { | |
| // "The For function guarantees to return a path only for the following objects: | |
| // - package-level types | |
| // - exported package-level non-types | |
| // - methods | |
| // - parameter and result variables | |
| // - struct fields" | |
| if typesinternal.IsPackageLevel(obj) && is[*types.TypeName](obj) { | |
| return false // package-level types | |
| } | |
| if obj.Exported() && typesinternal.IsPackageLevel(obj) && !is[*types.TypeName](obj) { | |
| return false // exported package-level non-types | |
| } | |
| // The remaining rules are inaccurate: clearly, | |
| // - local interface methods, | |
| // - params/results of local functions, and | |
| // - fields of local structs | |
| // cannot have object paths. | |
| // So don't enforce them for now. | |
| // TODO(adonovan): clarify the documentation and update this check. | |
| // | |
| // if fn, ok := obj.(*types.Func); ok && fn.Signature().Recv() != nil { | |
| // return false // methods | |
| // } | |
| // if v, ok := obj.(*types.Var); ok { | |
| // case types.ParamVar, types.ResultVar, types.RecvVar: | |
| // return false // parameter and result variables | |
| // case types.FieldVar: | |
| // return false // struct fields | |
| // } | |
| // } | |
| return true | |
| } | |
| func is[T any](x any) bool { | |
| _, ok := x.(T) | |
| return ok | |
| } |