如何将AST解析器中的类型解析为基本类型?

huangapple go评论94阅读模式
英文:

How to resolve types into a primitives in AST parser

问题

我想提取函数的签名,以便能够在其上生成一些包装方法。为此,我正在使用golang.org/x/tools/go/packages,它提供了读取AST的可能性。

例如,对于函数func MyFunc(param int)的定义,你会得到一些内容:

ast.FuncDecl{
	Type: *FieldList{
		List: []*Field{
			{
				Names: []*Ident{ /*...*/ },
				Type:  nil, /*...*/
			},
		},
	},
}

其中Type表示类型。

我想为所有int参数生成一些特殊代码,但是int也可以通过某些类型声明进行隐藏:

type MyType int

如何将ast类型转换为编译器实际使用的真实类型?

英文:

I want to extract signatures of functions to be able generate some wrapper methods over them. For this, I'm using
golang.org/x/tools/go/packages which provides me a possibility to read AST.

For example, for the function func MyFunc(param int) definition, you receive some

ast.FuncDecl{
	Type: *FieldList{
		List: []*Field{
			{
				Names: []*Ident{ /*...*/ },
				Type:  nil, /*...*/
			},
		},
	},
}

Where Type represents a type.

I want to generate some special code for all int parameters, but int can be also hidden with some type declaration

type MyType int

How can I convert ast type to the real one that the compiler has?

答案1

得分: 2

packages.NeedTypespackages.NeedTypesInfo添加到加载模式中。这样,每个加载的包都将初始化其TypesInfo字段,而该字段的类型*types.Info具有一个名为Types的字段,它将AST表达式映射到类型。您可以按照以下方式使用它:

func main() {
	loadConfig := new(packages.Config)
	loadConfig.Mode = packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo
	loadConfig.Fset = token.NewFileSet()
	pkgs, err := packages.Load(loadConfig, "syscall")
	if err != nil {
		panic(err)
	}

	for _, pkg := range pkgs {
		for _, syn := range pkg.Syntax {
			for _, dec := range syn.Decls {
				if fd, ok := dec.(*ast.FuncDecl); ok && fd.Name.Name == "Kill" {
					x1 := fd.Type.Params.List[0].Type // int
					x2 := fd.Type.Params.List[1].Type // syscall.Signal

					tv1 := pkg.TypesInfo.Types[x1]
					tv2 := pkg.TypesInfo.Types[x2]

					if basic, ok := tv1.Type.(*types.Basic); ok {
						fmt.Printf("%#v\n", basic) // int
					}

					if named, ok := tv2.Type.(*types.Named); ok {
						fmt.Printf("%v\n", named.Obj())         // *types.TypeName (Signal)
						fmt.Printf("%#v\n", named.Underlying()) // *types.Basic (int)
					}
				}
			}
		}
	}
}
英文:

Add packages.NeedTypes and packages.NeedTypesInfo to the load mode. With that each loaded package will have its TypesInfo field initialized, and that field's type *types.Info has a field called Types which maps ast expressions to types. You can use that in the following way:

func main() {
	loadConfig := new(packages.Config)
	loadConfig.Mode = packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo
	loadConfig.Fset = token.NewFileSet()
	pkgs, err := packages.Load(loadConfig, "syscall")
	if err != nil {
		panic(err)
	}

	for _, pkg := range pkgs {
		for _, syn := range pkg.Syntax {
			for _, dec := range syn.Decls {
				if fd, ok := dec.(*ast.FuncDecl); ok && fd.Name.Name == "Kill" {
					x1 := fd.Type.Params.List[0].Type // int
					x2 := fd.Type.Params.List[1].Type // syscall.Signal

					tv1 := pkg.TypesInfo.Types[x1]
					tv2 := pkg.TypesInfo.Types[x2]

					if basic, ok := tv1.Type.(*types.Basic); ok {
						fmt.Printf("%#v\n", basic) // int
					}

					if named, ok := tv2.Type.(*types.Named); ok {
						fmt.Printf("%v\n", named.Obj())         // *types.TypeName (Signal)
						fmt.Printf("%#v\n", named.Underlying()) // *types.Basic (int)
					}
				}
			}
		}
	}
}

huangapple
  • 本文由 发表于 2021年10月3日 16:11:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/69423134.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定