使用 "go/parser" 检查表达式是否为自定义类型
check if an expression is a custom type using "go/parser"
情况
编写检查结构字段并使用结构标签添加验证函数的代码生成器
问题
这里需要检查type字段是否为自定义类型
即
以下不是自定义类型
int, []int,*int,[]Integer,map[string]PhoneNumber
但以下为自定义类型
Integer,PhoneNumber,*PhoneNumber
我想我可以使用像下面这样的函数来查找精确匹配并可以添加地图,[]支持
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
}
但是有没有更好的方法使用 parse.ParseExpr
等等
对于任何一种可靠的结果,您都希望使用 go/types 包来涉及 Go 的类型检查器。使用起来并不简单,但在 https://golang.org/s/types-tutorial.
有一篇有用的介绍性文章
我拼凑了一个示例程序,所以您可以看到会发生什么。重要的一点是 isBasic
函数,其余只是使其可执行的样板。特别是,AST 遍历是针对特定示例源代码进行硬编码的。我想您已经为此准备好了自己的代码。
关键是 types.Info
结构包含了您实现自己的逻辑所需的所有类型信息。
我发现 github.com/fatih/astrewrite and golang.org/x/tools/go/loader 在处理代码生成 and/or 解析时很有帮助(加载程序包是完整类型信息所必需的)。
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\n", i, s.Text())
}
fmt.Println("")
}
情况
编写检查结构字段并使用结构标签添加验证函数的代码生成器
问题
这里需要检查type字段是否为自定义类型
即
以下不是自定义类型
int, []int,*int,[]Integer,map[string]PhoneNumber
但以下为自定义类型
Integer,PhoneNumber,*PhoneNumber
我想我可以使用像下面这样的函数来查找精确匹配并可以添加地图,[]支持
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
}
但是有没有更好的方法使用 parse.ParseExpr
等等
对于任何一种可靠的结果,您都希望使用 go/types 包来涉及 Go 的类型检查器。使用起来并不简单,但在 https://golang.org/s/types-tutorial.
有一篇有用的介绍性文章我拼凑了一个示例程序,所以您可以看到会发生什么。重要的一点是 isBasic
函数,其余只是使其可执行的样板。特别是,AST 遍历是针对特定示例源代码进行硬编码的。我想您已经为此准备好了自己的代码。
关键是 types.Info
结构包含了您实现自己的逻辑所需的所有类型信息。
我发现 github.com/fatih/astrewrite and golang.org/x/tools/go/loader 在处理代码生成 and/or 解析时很有帮助(加载程序包是完整类型信息所必需的)。
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\n", i, s.Text())
}
fmt.Println("")
}