使用“ go / parser”检查表达式是否为自定义类型

Situation

writing a code generator that checks fields of a struct and add validation functions using struct tags

Problem

Here I need to check if the type field is a custom type or not

ie,

Following are not custom types

int, []int,*int,[]Integer,map[string]PhoneNumber 

But the following are custom type

Integer,PhoneNumber,*PhoneNumber

I think I can do it using functions like the following that looks for exact match and may add map,[] support

func isBuiltInType(typ string) bool {
    switch typ {
            case "bool", "byte", "complex128", "complex64", "error": 
            case "float32", "float64":
            case "int", "int16", "int32", "int64", "int8":
            case "rune", "string":
            case "uint", "uint16", "uint32", "uint64", "uint8", "uintptr":
            default:
                return false
    }
    return true
}

But is there a better way to do it using parse.ParseExpr etc

For any kind of reliable result you will want to involve Go's type checker using the go/types package. It is not trivial to use, but there is a helpful introductory article at https://golang.org/s/types-tutorial.

I threw together an example program, so you can see what to expect. The important bit is the isBasic function, the rest is just boilerplate to make it executable. In particlar, the AST traversal is hardcoded for the specific sample source code. I presume you already have your own code in place for that.

The key point is that the types.Info structure contains all the type information you need to implement your own logic.

I found github.com/fatih/astrewrite and golang.org/x/tools/go/loader helpful when dealing with code generation and/or parsing (the loader package is kind of required for complete type information).

https://play.golang.org/p/A9hdPy-Oy-

package main

import (
    "bufio"
    "fmt"
    "go/ast"
    "go/parser"
    "go/token"
    "go/types"
    "log"
    "strings"
)

var src = strings.TrimSpace(`
package main

type T struct{}

func f() {
    var _ T
    var _ *T

    var _ int
    var _ *int
    var _ **int
    var _ []int
    var _ []T
    var _ map[string]int
    var _ map[string]T
}
`)

func main() {
    // Parse source
    fset := token.NewFileSet()
    f, err := parser.ParseFile(fset, "src.go", src, 0)
    if err != nil {
            log.Fatal(err)
    }

    // Run type checker
    info := types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}

    _, err = (&types.Config{}).Check("mypkg", fset, []*ast.File{f}, &info)
    if err != nil {
            log.Fatal(err)
    }

    printSrc()

    // Inspect variable types in f()
    for _, varDecl := range f.Decls[1].(*ast.FuncDecl).Body.List {
            value := varDecl.(*ast.DeclStmt).Decl.(*ast.GenDecl).Specs[0].(*ast.ValueSpec)

            pos := fset.Position(value.Type.Pos())
            typ := info.Types[value.Type].Type

            fmt.Println(pos, "basic:", isBasic(typ))
    }
}

func isBasic(t types.Type) bool {
    switch x := t.(type) {
    case *types.Basic:
            return true
    case *types.Slice:
            return true
    case *types.Map:
            return true
    case *types.Pointer:
            return isBasic(x.Elem())
    default:
            return false
    }
}

func printSrc() {
    s := bufio.NewScanner(strings.NewReader(src))
    for i := 1; s.Scan(); i++ {
            fmt.Printf("%2d: %s
", i, s.Text())
    }
    fmt.Println("")
}