On Tuesday, October 22, 2019 at 7:26:04 AM UTC+2, Frew Schmidt wrote: > > Hello Gophers! > > I'm trying to migrate some code that uses `reflect.Type` at runtime to > `types.Type` from `go/ast` built ahead of time. I got a pretty good way > (converted all the code and got it to successfully compile,) until I > realized that I hadn't been clearly thinking about the > `reflect.Type.Name()`/`reflect.Type.PkgPath()` > methods. As far as I can tell, the closest similarity in `go/types` is > `*types.Named`. I figured that the `ast` would give me values wrapped with > `*types.Named`, but it doesn't seem like it does. At first I thought that > maybe I should be wrapping values with `types.NewNamed`, but I'd have to be > doing that recursively; it seems hard enough that I must be missing > something. > > Below is some example code I'm using to experiment with this: > [...omitted...] >
Hello Frew, the `types.Type` API is quite complicated and significantly different from `reflect.Type` API, as you probably already realized: `reflect.Type` is a single interface with lots of methods, including the `reflect.Type.Name()` you are interested in. Depending on the `reflect.Kind` stored in the `Type`, some methods can or cannot be called. In practice, it represents a union of all the possible kinds (basic type, pointer, slice, array, map chan, struct, function...) `types.Type` follows the opposite approach: it is an interface with only two little-used methods, `String()` and `Underlying()`. In order to access additional methods, you must type-assert it to one of several concrete types: `*types.Basic`, `*types.Pointer`, `*types.Slice`, `*types.Array`, `*types.Map`, `*types.Chan`, `*types.Struct`, `*types.Func` .... and, most importantly for your case, `*types.Named` which can wrap any of the others. so your visitor function could be something like: ```Go v = func(n ast.Node) ast.Visitor { s, ok := n.(*ast.TypeSpec) if !ok { return v } e := s.Type t := i.Types[e].Type if n, ok := t.(*types.Named) { fmt.Printf("found named type: %s, its underlying type is %T\n", n.Obj().Name(), n.Underlying()) } else { fmt.Printf("found unnamed type %T: %v\n", t, t) } return v } ``` Alas, it's still not enough. You are examining `pkgs[0].TypesInfo` which contains the type of every expression, statement and declaration. The `ast.TypeSpec` you examine are type declarations i.e. they create new types - including named types. But your `i.Types[e].Type` retrieves the *underlying* type, which is (almost) always unnamed. What you want is probably "the type information for the package's exported symbols" i.e. the pkg.Config flag `packages.NeedTypes` which fills `pkgs[0].Types`. a full example is ```Go package main import ( "fmt" "go/types" "os" "golang.org/x/tools/go/packages" ) type Foo struct { A string } func main() { cfg := &packages.Config{Mode: packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo} pkgs, err := packages.Load(cfg, "pattern=./...") if err != nil { fmt.Fprintf(os.Stderr, "load: %v\n", err) os.Exit(1) } if packages.PrintErrors(pkgs) > 0 { os.Exit(1) } fmt.Println(pkgs) pkg := pkgs[0].Types scope := pkg.Scope() for _, name := range scope.Names() { obj := scope.Lookup(name) t := obj.Type() if n, ok := t.(*types.Named); ok { fmt.Printf("exported symbol %s\thas named type %s\t and underlying type %s\n", obj.Name(), n.Obj().Name(), t.Underlying()) } else { fmt.Printf("exported symbol %s\thas unnamed type %s\n", obj.Name(), t) } } } ``` -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/e43858ff-797b-4603-afaf-4ec3acbd8527%40googlegroups.com.