douquan9826 2017-09-28 19:09
浏览 44
已采纳

使用“ 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

  • 写回答

1条回答 默认 最新

  • dongrui6787 2017-09-29 15:15
    关注

    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("")
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 chaquopy python 安卓
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 有没有帮写代码做实验仿真的
  • ¥15 報錯:Person is not mapped,如何解決?
  • ¥30 vmware exsi重置后登不上
  • ¥15 易盾点选的cb参数怎么解啊
  • ¥15 MATLAB运行显示错误,如何解决?
  • ¥15 c++头文件不能识别CDialog
  • ¥15 Excel发现不可读取的内容
  • ¥15 关于#stm32#的问题:CANOpen的PDO同步传输问题