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.

Reply via email to