在进行go文件解析经常需要对Ident对象的类型/值进行比较
但go/ast只对单文件进行解析,并不适合用来比较类型,所以这个时候就需要用到另一个库go/types
先来看看全部代码
代码也可以通过这里下载,建议在看教程的同时运行这个demo代码
// implicitly import import “fmt”
import . “unsafe”
type Product struct { Name string }
func (p Product) String() string { return “Product:” + p.Name }
func ImplicitlyNode() { var d interface{} = 5 switch x := d.(type) { case int: fmt.Println(x) default: fmt.Println(x) } var e func(int) fmt.Println(e) }
func SelectionNode() { p := Product{Name: “t011”} fmt.Println(p.Name) fmt.Println(p.String()) fmt.Println(Product.String(p)) fmt.Println(Offsetof(p.Name)) }
const MaxRoutines = 100
var CurrentRoutines = 1
func main() { //test1 fmt.Println(“Hello, World!") //test2 a := []int{1, 2, 3} fmt.Println(a) b := map[int]string{ 1: “a”, 2: “b”, } fmt.Println(b)
d := make(chan int, 5)
fmt.Println(d)
fmt.Println(MaxRoutines)
}
</details>
<details>
<summary>
使用types分析库并打印其中各种信息
</summary>
```golang
package main
import (
"fmt"
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"reflect"
"sort"
)
// 排序规则order by Pos(), End()
func sortNodes(nodes []ast.Node) {
sort.Slice(nodes, func(i, j int) bool {
if nodes[i].Pos() == nodes[j].Pos() {
return nodes[i].End() < nodes[j].End()
}
return nodes[i].Pos() < nodes[j].Pos()
})
}
// map中的元素是无序的,对key排序打印更好查看
func getSortedKeys(m interface{}) []ast.Node {
mValue := reflect.ValueOf(m)
nodes := make([]ast.Node, mValue.Len())
keys := mValue.MapKeys()
for i := range keys {
nodes[i] = keys[i].Interface().(ast.Node)
}
sortNodes(nodes)
return nodes
}
func main() {
fset := token.NewFileSet() // 位置是相对于节点
// 用ParseFile把文件解析成*ast.File节点
f, err := parser.ParseFile(fset, "data/src.go", nil, 0)
if err != nil {
panic(err)
}
// 使用types check
// 构造config
config := types.Config{
// 加载包的方式,可以通过源码或编译好的包,其中编译好的包分为gc和gccgo,前者应该是
Importer: importer.For("source", nil),
// 表示允许包里面加载c库 import "c"
FakeImportC: true,
}
info := &types.Info{
// 表达式对应的类型
Types: make(map[ast.Expr]types.TypeAndValue),
// 被定义的标示符
Defs: make(map[*ast.Ident]types.Object),
// 被使用的标示符
Uses: make(map[*ast.Ident]types.Object),
// 隐藏节点,匿名import包,type-specific时的case对应的当前类型,声明函数的匿名参数如var func(int)
Implicits: make(map[ast.Node]types.Object),
// 选择器,只能针对类型/对象.字段/method的选择,package.API这种不会记录在这里
Selections: make(map[*ast.SelectorExpr]*types.Selection),
// scope 记录当前库scope下的所有域,*ast.File/*ast.FuncType/... 都属于scope,详情看Scopes说明
// scope关系: 最外层Universe scope,之后Package scope,其他子scope
Scopes: make(map[ast.Node]*types.Scope),
// 记录所有package级的初始化值
InitOrder: make([]*types.Initializer, 0, 0),
}
// 这里第一个path参数觉得当前pkg前缀,和FileSet的文件路径是无关的
pkg, err := config.Check("", fset, []*ast.File{f}, info)
if err != nil {
panic(err)
}
// 打印types
fmt.Println("------------ types -----------")
for _, node := range getSortedKeys(info.Types) {
expr := node.(ast.Expr)
typeValue := info.Types[expr]
fmt.Printf("%s - %s %T it's value: %v type: %s\n",
fset.Position(expr.Pos()),
fset.Position(expr.End()),
expr,
typeValue.Value,
typeValue.Type.String(),
)
if typeValue.Assignable() {
fmt.Print("assignable ")
}
if typeValue.Addressable() {
fmt.Print("addressable ")
}
if typeValue.IsNil() {
fmt.Print("nil ")
}
if typeValue.HasOk() {
fmt.Print("has ok ")
}
if typeValue.IsBuiltin() {
fmt.Print("builtin ")
}
if typeValue.IsType() {
fmt.Print("is type ")
}
if typeValue.IsValue() {
fmt.Print("is value ")
}
if typeValue.IsVoid() {
fmt.Print("void ")
}
fmt.Println()
}
// 打印defs
fmt.Println("------------ def -----------")
for _, node := range getSortedKeys(info.Defs) {
ident := node.(*ast.Ident)
object := info.Defs[ident]
fmt.Printf("%s - %s %T",
fset.Position(ident.Pos()),
fset.Position(ident.End()),
object,
)
if object != nil {
fmt.Printf(" it's object: %s type: %s",
object,
object.Type().String(),
)
}
fmt.Println()
}
// 打印Uses
fmt.Println("------------ uses -----------")
for _, node := range getSortedKeys(info.Uses) {
ident := node.(*ast.Ident)
object := info.Uses[ident]
fmt.Printf("%s - %s %T",
fset.Position(ident.Pos()),
fset.Position(ident.End()),
object,
)
if object != nil {
fmt.Printf(" it's object: %s type: %s",
object,
object.Type().String(),
)
}
fmt.Println()
}
// 打印Implicits
fmt.Println("------------ implicits -----------")
for _, node := range getSortedKeys(info.Implicits) {
object := info.Implicits[node]
fmt.Printf("%s - %s %T it's object: %s\n",
fset.Position(node.Pos()),
fset.Position(node.End()),
node,
object,
)
}
// 打印Selections
fmt.Println("------------ selections -----------")
for _, node := range getSortedKeys(info.Selections) {
sel := node.(*ast.SelectorExpr)
typeSel := info.Selections[sel]
fmt.Printf("%s - %s it's selection: %s\n",
fset.Position(sel.Pos()),
fset.Position(sel.End()),
typeSel.String(),
)
fmt.Printf("receive: %s index: %v obj: %s\n", typeSel.Recv(), typeSel.Index(), typeSel.Obj())
}
// 打印Scopes
fmt.Println("------------ scopes -----------")
//打印package scope
fmt.Printf("package level scope %s\n",
pkg.Scope().String(),
)
// 打印宇宙级scope
fmt.Printf("universe level scope %s\n",
pkg.Scope().Parent().String(),
)
for _, node := range getSortedKeys(info.Scopes) {
scope := info.Scopes[node]
fmt.Printf("%s - %s %T it's scope %s\n",
fset.Position(node.Pos()),
fset.Position(node.End()),
node,
scope.String(),
)
}
// 打印InitOrder
fmt.Println("------------ init -----------")
for _, init := range info.InitOrder {
fmt.Printf("init %s\n",
init.String(),
)
}
}
构建types.Config
types.Config决定如何去解析go代码,这里**importer.For(“source”, nil)**表示通过源码的方式解析go库,FakeImportC表示允许加载C库
创建types.Info
types.Info决定要解析哪些信息,给字段赋值后在Config.Check是会对这部分信息进行解析
Types保存表达式对应的类型,
Defs保存所有被定义的标示符,包括package name(包名)和带名字的加载库(import _ "package" / import . "package")
Uses保存所有被使用的标示符
Implicits保存三种隐藏节点,匿名import 的库(import "package"), type-specific时的case对应类型(switch t := x.(type){case int:}中case节点映射的t类型)
Selections保存所有类选择器,只能针对类型/对象.字段/method的选择,package.API这种不会记录在这里
Scopes保存当前库scope下的所有域,*ast.File/*ast.FuncType/... 都属于scope,最外层Universe scope,之后Package scope,其他子scope
InitOrder保存所有最外部初始化的值
config.Check
使用config.Check会填充types.Info的内容
这里第一个path参数决定当前pkg前缀,和FileSet的文件路径是无关的,[]*ast.File{f}是要解析的go文件,这些go文件必须是同一个pkg的文件
pkg, err := config.Check("", fset, []*ast.File{f}, info)
pkg包含顶级scope和包名信息,err则是解析文件的语法错误
config.Check过后我们就可以通过types.Info来读取go文件信息了
types.Info.Types
types.Info.Types 映射ast.Expr(不包括在types.Info.Defs和types.Info.Users中的*ast.Ident)到types.TypeAndValue类型
types.TypeAndValue解析expr的类型和它的额外属性,如果expr是一个常量值,TypeAndValue也会解析它的Value
我们来打印一下types.Info.Types的内容
fmt.Println("------------ types -----------")
for _, node := range getSortedKeys(info.Types) {
expr := node.(ast.Expr)
typeValue := info.Types[expr]
fmt.Printf("%s - %s %T it's value: %v type: %s\n",
fset.Position(expr.Pos()),
fset.Position(expr.End()),
expr,
typeValue.Value,
typeValue.Type.String(),
)
if typeValue.Assignable() {
fmt.Print("assignable ")
}
if typeValue.Addressable() {
fmt.Print("addressable ")
}
if typeValue.IsNil() {
fmt.Print("nil ")
}
if typeValue.HasOk() {
fmt.Print("has ok ")
}
if typeValue.IsBuiltin() {
fmt.Print("builtin ")
}
if typeValue.IsType() {
fmt.Print("is type ")
}
if typeValue.IsValue() {
fmt.Print("is value ")
}
if typeValue.IsVoid() {
fmt.Print("void ")
}
fmt.Println()
}
types.Info.Defs
Defs保存所有定义的*ast.Ident,并映射为types.Object
types.Object可以是一个包名,函数,常量,变量,或者标签的接口
当前包的package name也保存在Defs表中,不过它的Object是nil,不知道是不是bug
打印一下Defs的内容
fmt.Println("------------ def -----------")
for _, node := range getSortedKeys(info.Defs) {
ident := node.(*ast.Ident)
object := info.Defs[ident]
fmt.Printf("%s - %s %T",
fset.Position(ident.Pos()),
fset.Position(ident.End()),
object,
)
if object != nil {
fmt.Printf(" it's object: %s type: %s",
object,
object.Type().String(),
)
}
fmt.Println()
}
types.Info.Uses
Uses保存所有使用的*ast.Ident,并映射为types.Object
基本在Uses中的Ident也会出现在Types里,这部分逻辑很迷
打印Uses
fmt.Println("------------ uses -----------")
for _, node := range getSortedKeys(info.Uses) {
ident := node.(*ast.Ident)
object := info.Uses[ident]
fmt.Printf("%s - %s %T",
fset.Position(ident.Pos()),
fset.Position(ident.End()),
object,
)
if object != nil {
fmt.Printf(" it's object: %s type: %s",
object,
object.Type().String(),
)
}
fmt.Println()
}
types.Info.Implicits
Implicits保存各种隐藏声明的节点,以下节点可能出现在Implicits表中
没有包名的import
type-specific的swtich语句
声明函数时的匿名参数
这个example已经把实现了所有隐藏声明代码,来看看哪些节点会出现在这里
fmt.Println("------------ implicits -----------")
for _, node := range getSortedKeys(info.Implicits) {
object := info.Implicits[node]
fmt.Printf("%s - %s %T it's object: %s\n",
fset.Position(node.Pos()),
fset.Position(node.End()),
node,
object,
)
}
types.Info.Selections
Selections保存所有结构体选择表达式像 x.f,它会把*ast.SelectorExpr映射成*types.Selection,只有类成员的使用表达式会被映射,库成员的使用则不会
types.Selection有3中类型,对应3种调用方式(这里参考了types源码)
// type T struct{ x int; E }
// type E struct{}
// func (e E) m() {}
// var p *T
//
// the following relations exist:
//
// Selector Kind Recv Obj Type Index Indirect
//
// p.x FieldVal T x int {0} true
// p.m MethodVal *T m func (e *T) m() {1, 0} true
// T.m MethodExpr T m func m(_ T) {1, 0} false
//
fmt.Println("------------ selections -----------")
for _, node := range getSortedKeys(info.Selections) {
sel := node.(*ast.SelectorExpr)
typeSel := info.Selections[sel]
fmt.Printf("%s - %s it's selection: %s\n",
fset.Position(sel.Pos()),
fset.Position(sel.End()),
typeSel.String(),
)
fmt.Printf("receive: %s index: %v obj: %s\n", typeSel.Recv(), typeSel.Index(), typeSel.Obj())
}
types.Info.Scope
scope 记录当前库scope下的所有域,*ast.File/*ast.FuncType/… 都属于scope,详情看Scopes说明
scope关系: 最外层Universe scope,之后Package scope,其他子scope
打印Universe scope的内容和Package scope的内容
// 打印宇宙级scope
//打印package scope
fmt.Printf("package level scope %s\n",
pkg.Scope().String(),
)
// 打印宇宙级scope
fmt.Printf("universe level scope %s\n",
pkg.Scope().Parent().String(),
)
打印所有scope(types.Info.Scope不会包含Package scope和Universe scope)
for _, node := range getSortedKeys(info.Scopes) {
scope := info.Scopes[node]
fmt.Printf("%s - %s %T it's scope: %s\n",
fset.Position(node.Pos()),
fset.Position(node.End()),
node,
scope.String(),
)
}
data/src.go:19:2 - data/src.go:20:17 *ast.CaseClause it’s scope: case scope 0xc421389360 { . var x int } data/src.go:21:2 - data/src.go:22:17 *ast.CaseClause it’s scope: case scope 0xc421389400 { . var x interface{} } data/src.go:24:8 - data/src.go:24:17 *ast.FuncType it’s scope: function scope 0xc4213894f0 {}
data/src.go:28:1 - data/src.go:28:21 *ast.FuncType it’s scope: function scope 0xc421389180 { . var p Product } data/src.go:40:1 - data/src.go:40:12 *ast.FuncType it’s scope: function scope 0xc4213891d0 { . var a []int . var b map[int]string . var d chan int }
</details>
#### types.Info.InitOrder
InitOrder保存当前库最外层的变量
打印当前所有InitOrder内容
```golang
fmt.Println("------------ init -----------")
for _, init := range info.InitOrder {
fmt.Printf("init %s\n",
init.String(),
)
}
types.Object比较
可以使用Object.Pos()模块查找这个Object的声明时的位置,从而比较哪些Object属于同一个对象
types.Type比较
可以通过Type.String()来比较这2个Type是不是相同的类型
总结
以上就是就是go/types的全部内容了,下一章继续介绍go/types中实现的Type类型