go/ast - 抽象语法树
go/ast 包提供了 Go 语言抽象语法树(AST)的声明和数据结构,是 Go 代码分析工具的核心基础。
概述
go/ast 包用于表示和操作 Go 程序的抽象语法树结构。
包导入:
import (
"go/ast"
"go/parser"
"go/token"
)
基本使用:
// 1. 创建 FileSet
fset := token.NewFileSet()
// 2. 解析源码生成 AST
file, err := parser.ParseFile(fset, "main.go", src, 0)
if err != nil {
panic(err)
}
// 3. 遍历 AST
ast.Inspect(file, func(n ast.Node) bool {
if ident, ok := n.(*ast.Ident); ok {
fmt.Println("标识符:", ident.Name)
}
return true
})
典型示例:
示例 1:解析文件并打印 AST:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
src := `
package main
import "fmt"
func main() {
x := 42
fmt.Println(x)
}
`
// 创建 FileSet
fset := token.NewFileSet()
// 解析源码
file, err := parser.ParseFile(fset, "example.go", src, 0)
if err != nil {
panic(err)
}
// 打印 AST
ast.Print(fset, file)
}
运行:
$ go run main.go
0 *ast.File {
1 . Package: 2
2 . Name: *ast.Ident {
3 . . NamePos: 9
4 . . Name: "main"
5 . }
6 . Decls: []ast.Decl {
7 . . 0: *ast.GenDecl {
8 . . . TokPos: 15
9 . . . Tok: import
10 . . . Specs: []ast.Spec {
11 . . . . 0: *ast.ImportSpec {
12 . . . . . Path: *ast.BasicLit {
13 . . . . . . ValuePos: 22
14 . . . . . . Kind: STRING
15 . . . . . . Value: "\"fmt\""
16 . . . . . }
17 . . . . }
18 . . . }
19 . . }
20 . . 1: *ast.FuncDecl {
21 . . . Name: *ast.Ident {
22 . . . . NamePos: 35
23 . . . . Name: "main"
24 . . . }
25 . . . Type: *ast.FuncType {
26 . . . . Params: *ast.FieldList {
27 . . . . . Opening: 39
28 . . . . . Closing: 40
29 . . . . }
30 . . . }
31 . . . Body: *ast.BlockStmt {
32 . . . . Lbrace: 42
33 . . . . List: []ast.Stmt {
34 . . . . . 0: *ast.AssignStmt {
35 . . . . . . Lhs: []ast.Expr {
36 . . . . . . . 0: *ast.Ident {
37 . . . . . . . . NamePos: 45
38 . . . . . . . . Name: "x"
39 . . . . . . . }
40 . . . . . . }
41 . . . . . . TokPos: 47
42 . . . . . . Tok: :=
43 . . . . . . Rhs: []ast.Expr {
44 . . . . . . . 0: *ast.BasicLit {
45 . . . . . . . . ValuePos: 50
46 . . . . . . . . Kind: INT
47 . . . . . . . . Value: "42"
48 . . . . . . . }
49 . . . . . . }
50 . . . . . }
51 . . . . . 1: *ast.ExprStmt {
52 . . . . . . X: *ast.CallExpr {
53 . . . . . . . Fun: *ast.SelectorExpr {
54 . . . . . . . . X: *ast.Ident {
55 . . . . . . . . . NamePos: 58
56 . . . . . . . . . Name: "fmt"
57 . . . . . . . . }
58 . . . . . . . . Sel: *ast.Ident {
59 . . . . . . . . . NamePos: 62
60 . . . . . . . . . Name: "Println"
61 . . . . . . . . }
62 . . . . . . . }
63 . . . . . . . Lparen: 69
64 . . . . . . . Args: []ast.Expr {
65 . . . . . . . . 0: *ast.Ident {
66 . . . . . . . . . NamePos: 70
67 . . . . . . . . . Name: "x"
68 . . . . . . . . }
69 . . . . . . . }
70 . . . . . . . Rparen: 71
71 . . . . . . }
72 . . . . . }
73 . . . . }
74 . . . . Rbrace: 74
75 . . . }
76 . . }
77 . }
78 }
示例 2:查找所有函数声明:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
src := `
package main
func Add(a, b int) int {
return a + b
}
func Sub(a, b int) int {
return a - b
}
func main() {
fmt.Println(Add(1, 2))
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "example.go", src, 0)
// 查找所有函数
ast.Inspect(file, func(n ast.Node) bool {
if fn, ok := n.(*ast.FuncDecl); ok {
fmt.Printf("函数:%s\n", fn.Name.Name)
// 打印参数
if fn.Type.Params != nil {
fmt.Print(" 参数:")
for i, param := range fn.Type.Params.List {
if i > 0 {
fmt.Print(", ")
}
for j, name := range param.Names {
if j > 0 {
fmt.Print(", ")
}
fmt.Print(name.Name)
}
fmt.Print(" ")
printType(fset, param.Type)
}
fmt.Println()
}
// 打印返回值
if fn.Type.Results != nil {
fmt.Print(" 返回值:")
for i, result := range fn.Type.Results.List {
if i > 0 {
fmt.Print(", ")
}
for j, name := range result.Names {
if j > 0 {
fmt.Print(", ")
}
fmt.Print(name.Name)
}
fmt.Print(" ")
printType(fset, result.Type)
}
fmt.Println()
}
}
return true
})
}
func printType(fset *token.FileSet, expr ast.Expr) {
switch t := expr.(type) {
case *ast.Ident:
fmt.Print(t.Name)
case *ast.StarExpr:
fmt.Print("*")
printType(fset, t.X)
default:
fmt.Print("<complex>")
}
}
运行:
$ go run main.go
函数:Add
参数:a int, b int
返回值:int
函数:Sub
参数:a int, b int
返回值:int
函数:main
参数:
返回值:
一、Node 接口
所有 AST 节点的基础接口
Node
定义:
type Node interface {
Pos() token.Pos // 节点起始位置
End() token.Pos // 节点结束位置
}
说明:
- 所有 AST 节点都实现此接口
Pos():返回节点第一个 token 的位置End():返回节点最后一个 token 之后的位置- 位置类型为
token.Pos,需配合token.FileSet转换为实际行列号
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
src := `package main; func main() {}`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if n != nil {
pos := fset.Position(n.Pos())
end := fset.Position(n.End())
fmt.Printf("%T: 行%d-%d\n", n, pos.Line, end.Line)
}
return true
})
}
运行:
$ go run main.go
*ast.File: 行 1-1
*ast.Ident: 行 1-1
*ast.FuncDecl: 行 1-1
*ast.Ident: 行 1-1
*ast.FuncType: 行 1-1
*ast.BlockStmt: 行 1-1
二、声明(Declarations)
通用声明(var/const/type)
GenDecl
定义:
type GenDecl struct {
Doc *CommentGroup // 声明文档
TokPos token.Pos // token 位置(var/const/type)
Tok token.Token // token 类型(VAR/CONST/TYPE)
Lparen token.Pos // 左括号位置(如为 nil 则表示无括号)
Specs []Spec // 声明规范列表
Rparen token.Pos // 右括号位置
}
方法:
func (d *GenDecl) Pos() token.Pos
func (d *GenDecl) End() token.Pos
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
src := `
package main
var (
name string
age int
)
const Pi = 3.14
type Person struct {
Name string
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if gen, ok := n.(*ast.GenDecl); ok {
fmt.Printf("声明类型:%s\n", gen.Tok)
fmt.Printf(" 规范数量:%d\n", len(gen.Specs))
for _, spec := range gen.Specs {
switch s := spec.(type) {
case *ast.ValueSpec:
fmt.Printf(" 值:%v\n", s.Names)
case *ast.TypeSpec:
fmt.Printf(" 类型:%s\n", s.Name.Name)
}
}
}
return true
})
}
运行:
$ go run main.go
声明类型:var
规范数量:2
值:[name age]
声明类型:const
规范数量:1
值:[Pi]
声明类型:type
规范数量:1
类型:Person
函数声明
FuncDecl
定义:
type FuncDecl struct {
Doc *CommentGroup // 函数文档
Recv *FieldList // 接收者(方法),普通函数为 nil
Name *Ident // 函数名
Type *FuncType // 函数类型(参数和返回值)
Body *BlockStmt // 函数体(接口方法为 nil)
}
方法:
func (d *FuncDecl) Pos() token.Pos
func (d *FuncDecl) End() token.Pos
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
src := `
package main
func Add(a, b int) int {
return a + b
}
func (s *Server) Serve() error {
return nil
}
type Interface interface {
Method() error
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if fn, ok := n.(*ast.FuncDecl); ok {
fmt.Printf("函数:%s\n", fn.Name.Name)
if fn.Recv != nil {
fmt.Println(" 类型:方法")
} else {
fmt.Println(" 类型:函数")
}
if fn.Body == nil {
fmt.Println(" 说明:接口方法声明")
}
}
return true
})
}
运行:
$ go run main.go
函数:Add
类型:函数
函数:Serve
类型:方法
函数:Method
类型:方法
说明:接口方法声明
三、Spec 类型(声明规范)
值声明(var/const)
ValueSpec
定义:
type ValueSpec struct {
Doc *CommentGroup // 文档注释
Names []*Ident // 变量/常量名列表
Type Expr // 类型(可选,可从初始化推断)
Values []Expr // 初始化值列表
Comment *CommentGroup // 行尾注释
}
方法:
func (s *ValueSpec) Pos() token.Pos
func (s *ValueSpec) End() token.Pos
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
src := `
package main
var (
count int = 10
name = "Go"
)
const MaxSize = 1024
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if gen, ok := n.(*ast.GenDecl); ok && gen.Tok == token.VAR {
for _, spec := range gen.Specs {
vs := spec.(*ast.ValueSpec)
for _, name := range vs.Names {
fmt.Printf("变量:%s", name.Name)
if vs.Type != nil {
fmt.Print(" (有类型)")
}
if len(vs.Values) > 0 {
fmt.Print(" (有初始化)")
}
fmt.Println()
}
}
}
return true
})
}
运行:
$ go run main.go
变量:count (有类型) (有初始化)
变量:name (有初始化)
类型声明
TypeSpec
定义:
type TypeSpec struct {
Doc *CommentGroup // 文档注释
Name *Ident // 类型名
Assign token.Pos // `=` 位置(如为 nil 表示定义新类型,否则表示类型别名)
Type Expr // 类型定义
}
方法:
func (s *TypeSpec) Pos() token.Pos
func (s *TypeSpec) End() token.Pos
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
src := `
package main
// 定义新类型
type MyInt int
type Person struct {
Name string
}
// 类型别名(Go 1.9+)
type Integer = int
type StringMap = map[string]string
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if gen, ok := n.(*ast.GenDecl); ok && gen.Tok == token.TYPE {
for _, spec := range gen.Specs {
ts := spec.(*ast.TypeSpec)
fmt.Printf("类型:%s", ts.Name.Name)
if ts.Assign != 0 {
fmt.Print(" (别名)")
} else {
fmt.Print(" (定义)")
}
fmt.Println()
}
}
return true
})
}
运行:
$ go run main.go
类型:MyInt (定义)
类型:Person (定义)
类型:Integer (别名)
类型:StringMap (别名)
导入声明
ImportSpec
定义:
type ImportSpec struct {
Doc *CommentGroup // 文档注释
Name *Ident // 别名(如为 nil 则表示无别名)
Path *BasicLit // 导入路径(字符串字面量)
Comment *CommentGroup // 行尾注释
EndPos token.Pos // 结束位置
}
方法:
func (s *ImportSpec) Pos() token.Pos
func (s *ImportSpec) End() token.Pos
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
"strings"
)
func main() {
src := `
package main
import (
"fmt"
f "fmt"
. "fmt"
_ "unsafe"
)
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, parser.ParseComments)
for _, imp := range file.Imports {
path := strings.Trim(imp.Path.Value, "\"")
if imp.Name != nil {
fmt.Printf("别名:%s -> %s\n", imp.Name.Name, path)
} else {
fmt.Printf("普通:%s\n", path)
}
}
}
运行:
$ go run main.go
普通:fmt
别名:f -> fmt
别名:. -> fmt
别名:_ -> unsafe
四、语句(Statements)
块语句
BlockStmt
定义:
type BlockStmt struct {
Lbrace token.Pos // 左花括号位置
List []Stmt // 语句列表
Rbrace token.Pos // 右花括号位置
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func main() {
{
x := 1
y := 2
}
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if block, ok := n.(*ast.BlockStmt); ok {
fmt.Printf("块语句:%d 个语句\n", len(block.List))
}
return true
})
}
if 语句
IfStmt
定义:
type IfStmt struct {
Init Stmt // 初始化语句(可选)
Cond Expr // 条件表达式
Body *BlockStmt // if 分支
Else Stmt // else 分支(可以是 *IfStmt 或 *BlockStmt)
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
if x > 0 {
println("positive")
} else if x < 0 {
println("negative")
} else {
println("zero")
}
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
count := 0
ast.Inspect(file, func(n ast.Node) bool {
if _, ok := n.(*ast.IfStmt); ok {
count++
}
return true
})
fmt.Printf("if 语句数量:%d\n", count)
}
运行:
$ go run main.go
if 语句数量:3
for 语句
ForStmt
定义:
type ForStmt struct {
Init Stmt // 初始化语句(可选)
Cond Expr // 条件表达式(可选)
Post Stmt // 后处理语句(可选)
Body *BlockStmt // 循环体
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
for i := 0; i < 10; i++ {
println(i)
}
for x < 10 {
x++
}
for {
break
}
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if forStmt, ok := n.(*ast.ForStmt); ok {
if forStmt.Init != nil {
fmt.Println("完整 for 循环")
} else if forStmt.Cond != nil {
fmt.Println("while 风格循环")
} else {
fmt.Println("无限循环")
}
}
return true
})
}
运行:
$ go run main.go
完整 for 循环
while 风格循环
无限循环
range 语句
RangeStmt
定义:
type RangeStmt struct {
Key Expr // key 变量(可选)
Value Expr // value 变量(可选)
TokPos token.Pos // `:=` 或 `=` 位置
Tok token.Token // ASSIGN 或 DEFINE
X Expr // 被 range 的表达式
Body *BlockStmt // 循环体
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test(slice []int, m map[string]int) {
for i, v := range slice {
println(i, v)
}
for k := range m {
println(k)
}
for _, v := range slice {
println(v)
}
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if rangeStmt, ok := n.(*ast.RangeStmt); ok {
if rangeStmt.Key != nil && rangeStmt.Value != nil {
fmt.Println("key 和 value")
} else if rangeStmt.Key != nil {
fmt.Println("只有 key")
} else {
fmt.Println("无 key 和 value(或都使用空白标识符)")
}
}
return true
})
}
运行:
$ go run main.go
key 和 value
只有 key
key 和 value
switch 语句
SwitchStmt
定义:
type SwitchStmt struct {
Init Stmt // 初始化语句(可选)
Tag Expr // switch 表达式(可选)
Body *BlockStmt // case 子句列表
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test(x int) {
switch x {
case 1:
println("one")
default:
println("other")
}
switch {
case x > 10:
println("large")
}
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if sw, ok := n.(*ast.SwitchStmt); ok {
if sw.Tag != nil {
fmt.Println("带表达式的 switch")
} else {
fmt.Println("无条件 switch(类似 if-else if)")
}
}
return true
})
}
运行:
$ go run main.go
带表达式的 switch
无条件 switch(类似 if-else if)
类型 switch 语句
TypeSwitchStmt
定义:
type TypeSwitchStmt struct {
Init Stmt // 初始化语句(可选)
Assign Stmt // 类型断言(*AssignStmt,形式:x := y.(type))
Body *BlockStmt // case 子句列表
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test(iface interface{}) {
switch v := iface.(type) {
case int:
println("int:", v)
case string:
println("string:", v)
}
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if _, ok := n.(*ast.TypeSwitchStmt); ok {
fmt.Println("找到类型 switch")
}
return true
})
}
运行:
$ go run main.go
找到类型 switch
select 语句
SelectStmt
定义:
type SelectStmt struct {
Select token.Pos // select 关键字位置
Body *BlockStmt // case 子句列表
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test(ch chan int) {
select {
case msg := <-ch:
println(msg)
case ch <- 1:
println("sent")
default:
println("no channel ready")
}
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if _, ok := n.(*ast.SelectStmt); ok {
fmt.Println("找到 select 语句")
}
return true
})
}
运行:
$ go run main.go
找到 select 语句
赋值语句
AssignStmt
定义:
type AssignStmt struct {
Lhs []Expr // 左值
TokPos token.Pos // `:=` 或 `=` 等位置
Tok token.Token // 赋值类型
Rhs []Expr // 右值
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
src := `
package main
func test() {
x := 10
y = 20
z += 5
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if assign, ok := n.(*ast.AssignStmt); ok {
fmt.Printf("赋值类型:%s\n", assign.Tok)
}
return true
})
}
运行:
$ go run main.go
赋值类型::=
赋值类型:=
赋值类型:+=
其他语句类型
GoStmt(go 语句):
type GoStmt struct {
Call *CallExpr // 函数调用
}
DeferStmt(defer 语句):
type DeferStmt struct {
Call *CallExpr // 函数调用
}
ReturnStmt(返回语句):
type ReturnStmt struct {
Results []Expr // 返回值列表
}
BreakStmt、ContinueStmt、GotoStmt:
type BreakStmt struct {
Label *Ident // 标签(可选)
}
type ContinueStmt struct {
Label *Ident // 标签(可选)
}
type GotoStmt struct {
Label *Ident // 标签
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
go fn()
defer cleanup()
return 42
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
var goCount, deferCount, returnCount int
ast.Inspect(file, func(n ast.Node) bool {
switch n.(type) {
case *ast.GoStmt:
goCount++
case *ast.DeferStmt:
deferCount++
case *ast.ReturnStmt:
returnCount++
}
return true
})
fmt.Printf("go: %d, defer: %d, return: %d\n", goCount, deferCount, returnCount)
}
运行:
$ go run main.go
go: 1, defer: 1, return: 1
五、表达式(Expressions)
标识符
Ident
定义:
type Ident struct {
NamePos token.Pos // 标识符位置
Name string // 标识符名称
Obj *Object // 关联对象(可选,通常由类型检查器填充)
}
方法:
func (x *Ident) Pos() token.Pos
func (x *Ident) End() token.Pos
func (x *Ident) String() string // 返回 Name
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
x := 42
fmt.Println(x)
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if ident, ok := n.(*ast.Ident); ok {
fmt.Printf("标识符:%s\n", ident.Name)
}
return true
})
}
运行:
$ go run main.go
标识符:main
标识符:test
标识符:x
标识符:fmt
标识符:Println
标识符:x
基本字面量
BasicLit
定义:
type BasicLit struct {
ValuePos token.Pos // 字面量位置
Kind token.Token // 字面量类型(INT, FLOAT, STRING, CHAR)
Value string // 字面量值(包含引号)
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
x := 42
y := 3.14
s := "hello"
c := 'x'
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if lit, ok := n.(*ast.BasicLit); ok {
fmt.Printf("字面量:%s (%s)\n", lit.Value, lit.Kind)
}
return true
})
}
运行:
$ go run main.go
字面量:42 (INT)
字面量:3.14 (FLOAT)
字面量:"hello" (STRING)
字面量:'x' (CHAR)
函数字面量
FuncLit
定义:
type FuncLit struct {
Type *FuncType // 函数类型
Body *BlockStmt // 函数体
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
fn := func(x, y int) int {
return x + y
}
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if _, ok := n.(*ast.FuncLit); ok {
fmt.Println("找到函数字面量(匿名函数)")
}
return true
})
}
运行:
$ go run main.go
找到函数字面量(匿名函数)
复合字面量
CompositeLit
定义:
type CompositeLit struct {
Type Expr // 类型(可为 nil)
Lbrace token.Pos // 左花括号位置
Elts []Expr // 元素列表(*KeyValueExpr 或 Expr)
Rbrace token.Pos // 右花括号位置
Incomplete bool // 是否有解析错误
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
slice := []int{1, 2, 3}
m := map[string]int{"a": 1}
p := Point{X: 1, Y: 2}
}
type Point struct {
X, Y int
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if lit, ok := n.(*ast.CompositeLit); ok {
fmt.Printf("复合字面量:%d 个元素\n", len(lit.Elts))
}
return true
})
}
运行:
$ go run main.go
复合字面量:3 个元素
复合字面量:1 个元素
复合字面量:2 个元素
选择器表达式
SelectorExpr
定义:
type SelectorExpr struct {
X Expr // 操作数
Sel *Ident // 被选择的字段/方法名
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
fmt.Println("hello")
user.Name = "Go"
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if sel, ok := n.(*ast.SelectorExpr); ok {
fmt.Printf("选择器:%v.%s\n", sel.X, sel.Sel.Name)
}
return true
})
}
运行:
$ go run main.go
选择器:fmt.Println
选择器:user.Name
调用表达式
CallExpr
定义:
type CallExpr struct {
Fun Expr // 被调用的函数
Lparen token.Pos // 左括号位置
Args []Expr // 参数列表
Ellipsis token.Pos // `...` 位置(可变参数展开)
Rparen token.Pos // 右括号位置
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test(args ...int) {
fmt.Println("hello")
fn(args...)
make([]int, 10)
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if call, ok := n.(*ast.CallExpr); ok {
if call.Ellipsis != 0 {
fmt.Printf("调用:%d 个参数(可变参数展开)\n", len(call.Args))
} else {
fmt.Printf("调用:%d 个参数\n", len(call.Args))
}
}
return true
})
}
运行:
$ go run main.go
调用:1 个参数
调用:1 个参数(可变参数展开)
调用:2 个参数
一元表达式
UnaryExpr
定义:
type UnaryExpr struct {
OpPos token.Pos // 操作符位置
Op token.Token // 操作符(+、-、!、^、*、&、<-)
X Expr // 操作数
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
x := -5
b := !true
p := &value
v := *ptr
msg := <-ch
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if unary, ok := n.(*ast.UnaryExpr); ok {
fmt.Printf("一元操作:%s\n", unary.Op)
}
return true
})
}
运行:
$ go run main.go
一元操作:-
一元操作:!
一元操作:&
一元操作:*
一元操作:<-
二元表达式
BinaryExpr
定义:
type BinaryExpr struct {
X Expr // 左操作数
OpPos token.Pos // 操作符位置
Op token.Token // 操作符
Y Expr // 右操作数
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
x := a + b
y := x > 10
z := a && b
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if binary, ok := n.(*ast.BinaryExpr); ok {
fmt.Printf("二元操作:%s\n", binary.Op)
}
return true
})
}
运行:
$ go run main.go
二元操作:+
二元操作:>
二元操作:&&
类型断言表达式
TypeAssertExpr
定义:
type TypeAssertExpr struct {
X Expr // 被断言的表达式
Lparen token.Pos // 左括号位置
Type Expr // 目标类型(如为 nil 表示 x.(type))
Rparen token.Pos // 右括号位置
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test(iface interface{}) {
v := iface.(int)
switch x := iface.(type) {
case int:
println(x)
}
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if ta, ok := n.(*ast.TypeAssertExpr); ok {
if ta.Type == nil {
fmt.Println("类型 switch:x.(type)")
} else {
fmt.Println("类型断言:x.(Type)")
}
}
return true
})
}
运行:
$ go run main.go
类型断言:x.(Type)
类型 switch:x.(type)
索引和切片表达式
IndexExpr(索引表达式):
type IndexExpr struct {
X Expr // 被索引的表达式
Lbrack token.Pos // 左方括号位置
Index Expr // 索引值
Rbrack token.Pos // 右方括号位置
}
SliceExpr(切片表达式):
type SliceExpr struct {
X Expr // 被切片的表达式
Lbrack token.Pos // 左方括号位置
Low Expr // 下限(可选)
High Expr // 上限(可选)
Max Expr // 最大容量(可选)
Rbrack token.Pos // 右方括号位置
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
x := arr[0]
y := slice[1:5]
z := slice[1:5:10]
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
switch expr := n.(type) {
case *ast.IndexExpr:
fmt.Println("索引表达式")
case *ast.SliceExpr:
if expr.Max != nil {
fmt.Println("完整切片表达式(3 个索引)")
} else {
fmt.Println("切片表达式(2 个索引)")
}
}
return true
})
}
运行:
$ go run main.go
索引表达式
切片表达式(2 个索引)
切片表达式(完整切片表达式(3 个索引))
六、类型表达式
数组、切片、Map 类型
ArrayType:
type ArrayType struct {
Lbrack token.Pos // 左方括号位置
Len Expr // 长度(如为 nil 表示 [...]T)
Elt Expr // 元素类型
}
SliceType:
type SliceType struct {
Lbrack token.Pos // 左方括号位置
Elt Expr // 元素类型
}
MapType:
type MapType struct {
Map token.Pos // map 关键字位置
Key Expr // 键类型
Value Expr // 值类型
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
var a [10]int
var s []int
var m map[string]int
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
switch t := n.(type) {
case *ast.ArrayType:
if t.Len != nil {
fmt.Println("数组类型")
} else {
fmt.Println("切片类型")
}
case *ast.MapType:
fmt.Println("Map 类型")
}
return true
})
}
运行:
$ go run main.go
数组类型
切片类型
Map 类型
结构体类型
StructType
定义:
type StructType struct {
Struct token.Pos // struct 关键字位置
Fields *FieldList // 字段列表
Incomplete bool // 是否有解析错误
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
type Person struct {
Name string
Age int
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if st, ok := n.(*ast.StructType); ok {
fmt.Printf("结构体:%d 个字段\n", len(st.Fields.List))
}
return true
})
}
运行:
$ go run main.go
结构体:2 个字段
接口类型
InterfaceType
定义:
type InterfaceType struct {
Interface token.Pos // interface 关键字位置
Methods *FieldList // 方法列表
Incomplete bool // 是否有解析错误
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
type Reader interface {
Read(p []byte) (n int, err error)
Close() error
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if it, ok := n.(*ast.InterfaceType); ok {
fmt.Printf("接口:%d 个方法\n", len(it.Methods.List))
}
return true
})
}
运行:
$ go run main.go
接口:2 个方法
函数类型
FuncType
定义:
type FuncType struct {
Func token.Pos // func 关键字位置
Params *FieldList // 参数列表
Results *FieldList // 返回值列表(可选)
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func Add(a, b int) int {
return a + b
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if ft, ok := n.(*ast.FuncType); ok {
paramCount := 0
if ft.Params != nil {
paramCount = len(ft.Params.List)
}
resultCount := 0
if ft.Results != nil {
resultCount = len(ft.Results.List)
}
fmt.Printf("函数类型:%d 个参数,%d 个返回值\n", paramCount, resultCount)
}
return true
})
}
运行:
$ go run main.go
函数类型:2 个参数,1 个返回值
Channel 类型
ChanType
定义:
type ChanType struct {
Begin token.Pos // `chan` 关键字或 `<-` 位置
Arrow token.Pos // `<-` 位置(如为 nil 表示双向)
Dir ChanDir // Channel 方向
Value Expr // 元素类型
}
type ChanDir int
const (
SEND_RECV ChanDir = iota // bidirectional chan
SEND // send-only chan
RECV // receive-only chan
)
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
var c1 chan int
var c2 chan<- int
var c3 <-chan int
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if ct, ok := n.(*ast.ChanType); ok {
switch ct.Dir {
case ast.SEND_RECV:
fmt.Println("双向 channel")
case ast.SEND:
fmt.Println("发送 channel")
case ast.RECV:
fmt.Println("接收 channel")
}
}
return true
})
}
运行:
$ go run main.go
双向 channel
发送 channel
接收 channel
七、注释处理
Comment 和 CommentGroup
Comment:
type Comment struct {
Slash token.Pos // 注释起始位置(/或//)
Text string // 注释文本(包含 // 或 /* */)
}
CommentGroup:
type CommentGroup struct {
List []*Comment // 注释列表
}
方法:
func (c *CommentGroup) Pos() token.Pos
func (c *CommentGroup) End() token.Pos
func (c *CommentGroup) Text() string // 提取注释文本(去除 // 和 /* */)
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
// Add 计算两个数的和
// 参数:a, b
// 返回:和
func Add(a, b int) int {
return a + b // 返回结果
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, parser.ParseComments)
// 提取函数文档
ast.Inspect(file, func(n ast.Node) bool {
if fn, ok := n.(*ast.FuncDecl); ok {
if fn.Doc != nil {
fmt.Printf("函数 %s 的文档:\n%s\n",
fn.Name.Name, fn.Doc.Text())
}
}
return true
})
}
运行:
$ go run main.go
函数 Add 的文档:
Add 计算两个数的和
参数:a, b
返回:和
Field 和 FieldList
Field:
type Field struct {
Doc *CommentGroup // 字段文档注释
Names []*Ident // 字段名列表(可为 nil,如结构体匿名嵌入)
Type Expr // 字段类型
Tag *BasicLit // 字段标签(可选)
Comment *CommentGroup // 字段行尾注释
}
FieldList:
type FieldList struct {
Opening token.Pos // 左括号位置(如为 nil 则表示无括号)
List []*Field // 字段列表
Closing token.Pos // 右括号位置
}
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
type Person struct {
// Name 是姓名
Name string ` + "`" + `json:"name"` + "`" + `
// Age 是年龄
Age int ` + "`" + `json:"age"` + "`" + `
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, parser.ParseComments)
ast.Inspect(file, func(n ast.Node) bool {
if st, ok := n.(*ast.StructType); ok {
for _, field := range st.Fields.List {
if field.Doc != nil {
for _, name := range field.Names {
fmt.Printf("字段:%s\n", name.Name)
fmt.Printf(" 文档:%s\n", field.Doc.Text())
if field.Tag != nil {
fmt.Printf(" 标签:%s\n", field.Tag.Value)
}
}
}
}
}
return true
})
}
运行:
$ go run main.go
字段:Name
文档:Name 是姓名
标签:`json:"name"`
字段:Age
文档:Age 是年龄
标签:`json:"age"`
八、Visitor 模式和遍历
Visitor 接口
Visitor
定义:
type Visitor interface {
Visit(node Node) (w Visitor)
}
说明:
ast.Walk函数会调用Visitor.Visit(node)- 如果
Visit返回非 nil 的Visitor,则继续遍历该节点的子节点 - 如果返回 nil,则停止遍历该分支
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
// 自定义 Visitor
type CodeStats struct {
FuncCount int
VarCount int
CallCount int
IfCount int
ForCount int
}
func (v *CodeStats) Visit(node ast.Node) ast.Visitor {
if node == nil {
return nil
}
switch node.(type) {
case *ast.FuncDecl:
v.FuncCount++
case *ast.GenDecl:
gen := node.(*ast.GenDecl)
if gen.Tok == token.VAR {
v.VarCount += len(gen.Specs)
}
case *ast.CallExpr:
v.CallCount++
case *ast.IfStmt:
v.IfCount++
case *ast.ForStmt:
v.ForCount++
}
return v // 继续遍历子节点
}
func main() {
src := `
package main
var count int
func Add(a, b int) int {
if a > 0 {
return a + b
}
return 0
}
func main() {
for i := 0; i < 10; i++ {
if i%2 == 0 {
count += Add(i, 1)
}
}
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
stats := &CodeStats{}
ast.Walk(stats, file)
fmt.Printf("函数数量:%d\n", stats.FuncCount)
fmt.Printf("变量声明:%d\n", stats.VarCount)
fmt.Printf("函数调用:%d\n", stats.CallCount)
fmt.Printf("if 语句:%d\n", stats.IfCount)
fmt.Printf("for 循环:%d\n", stats.ForCount)
}
运行:
$ go run main.go
函数数量:3
变量声明:1
函数调用:2
if 语句:2
for 循环:1
Walk 函数
Walk
定义:
func Walk(v Visitor, node Node)
说明:
- 深度优先遍历 AST
- 对每个节点调用
v.Visit(node) - 根据返回值决定是否继续遍历子节点
Inspect 函数
Inspect
定义:
func Inspect(node Node, f func(Node) bool)
说明:
Walk的简化版本- 对每个节点调用函数
f - 如果
f返回 true,继续遍历子节点;否则停止
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
src := `
package main
func test() {
x := 42
fmt.Println(x)
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
// 使用 Inspect 遍历
ast.Inspect(file, func(n ast.Node) bool {
if ident, ok := n.(*ast.Ident); ok {
fmt.Printf("标识符:%s\n", ident.Name)
}
return true // 继续遍历
})
}
九、核心类型
File(源文件)
定义:
type File struct {
Doc *CommentGroup // 文件文档
Package token.Pos // package 关键字位置
Name *Ident // 包名
Decls []Decl // 声明列表
Comments []*CommentGroup // 文件注释
GoVersion string // Go 版本
}
方法:
func (f *File) Pos() token.Pos
func (f *File) End() token.Pos
示例:
// 通过 parser.ParseFile 获取 File
file, err := parser.ParseFile(fset, "main.go", src, parser.ParseComments)
// 访问文件信息
fmt.Printf("包名:%s\n", file.Name.Name)
fmt.Printf("声明数量:%d\n", len(file.Decls))
fmt.Printf("注释数量:%d\n", len(file.Comments))
Decl(声明接口)
定义:
type Decl interface {
Node
declNode()
}
// 实现 Decl 接口的类型:
// - *GenDecl (var/const/type)
// - *FuncDecl (func)
Stmt(语句接口)
定义:
type Stmt interface {
Node
stmtNode()
}
// 实现 Stmt 接口的类型:
// - *BlockStmt, *IfStmt, *ForStmt, *RangeStmt
// - *SwitchStmt, *TypeSwitchStmt, *SelectStmt
// - *AssignStmt, *GoStmt, *DeferStmt, *ReturnStmt
// - *BreakStmt, *ContinueStmt, *GotoStmt, *EmptyStmt
Expr(表达式接口)
定义:
type Expr interface {
Node
exprNode()
}
// 实现 Expr 接口的类型:
// - *Ident, *BasicLit, *FuncLit, *CompositeLit
// - *ParenExpr, *SelectorExpr, *IndexExpr, *SliceExpr
// - *TypeAssertExpr, *CallExpr, *StarExpr, *UnaryExpr
// - *BinaryExpr, *KeyValueExpr
// - *ArrayType, *SliceType, *MapType, *StructType
// - *FuncType, *InterfaceType, *ChanType
Spec(声明规范接口)
定义:
type Spec interface {
Node
specNode()
}
// 实现 Spec 接口的类型:
// - *ValueSpec (var/const)
// - *TypeSpec (type)
// - *ImportSpec (import)
十、包级别变量
预声明的标识符检查
IsExported
定义:
func IsExported(name string) bool
说明:
- 检查标识符是否已导出(首字母大写)
示例:
package main
import (
"fmt"
"go/ast"
)
func main() {
fmt.Println(ast.IsExported("Public")) // true
fmt.Println(ast.IsExported("private")) // false
}
运行:
$ go run main.go
true
false
IsExported
定义:
func IsExported(name string) bool
说明:
- 检查标识符是否已导出(首字母大写)
- 用于判断标识符是否可被其他包访问
示例:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
src := `
package main
type Person struct {
Name string // 已导出
age int // 未导出
Address string // 已导出
}
`
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "", src, 0)
ast.Inspect(file, func(n ast.Node) bool {
if st, ok := n.(*ast.StructType); ok {
for _, field := range st.Fields.List {
for _, name := range field.Names {
if ast.IsExported(name.Name) {
fmt.Printf("%s: 已导出\n", name.Name)
} else {
fmt.Printf("%s: 未导出\n", name.Name)
}
}
}
}
return true
})
}
运行:
$ go run main.go
Name: 已导出
age: 未导出
Address: 已导出
快速参考
声明类型
| 类型 | 用途 | 关键字段 |
|---|---|---|
| GenDecl | var/const/type 声明 | Tok, Specs |
| FuncDecl | 函数/方法声明 | Recv, Name, Type, Body |
Spec 类型
| 类型 | 用途 | 关键字段 |
|---|---|---|
| ValueSpec | var/const 值声明 | Names, Type, Values |
| TypeSpec | type 类型声明 | Name, Assign, Type |
| ImportSpec | import 导入声明 | Name, Path |
语句类型
| 类型 | 用途 | 关键字段 |
|---|---|---|
| BlockStmt | 代码块 | List |
| IfStmt | if 语句 | Init, Cond, Body, Else |
| ForStmt | for 循环 | Init, Cond, Post, Body |
| RangeStmt | range 循环 | Key, Value, X, Body |
| SwitchStmt | switch 语句 | Init, Tag, Body |
| TypeSwitchStmt | 类型 switch | Init, Assign, Body |
| SelectStmt | select 语句 | Body |
| AssignStmt | 赋值语句 | Lhs, Tok, Rhs |
| ReturnStmt | return 语句 | Results |
表达式类型
| 类型 | 用途 | 关键字段 |
|---|---|---|
| Ident | 标识符 | Name |
| BasicLit | 基本字面量 | Kind, Value |
| CallExpr | 函数调用 | Fun, Args |
| SelectorExpr | 选择器 | X, Sel |
| IndexExpr | 索引 | X, Index |
| SliceExpr | 切片 | X, Low, High, Max |
| BinaryExpr | 二元运算 | X, Op, Y |
| UnaryExpr | 一元运算 | Op, X |
类型表达式
| 类型 | 用途 | 关键字段 |
|---|---|---|
| ArrayType | 数组类型 | Len, Elt |
| SliceType | 切片类型 | Elt |
| MapType | Map 类型 | Key, Value |
| StructType | 结构体类型 | Fields |
| FuncType | 函数类型 | Params, Results |
| InterfaceType | 接口类型 | Methods |
| ChanType | Channel 类型 | Dir, Value |
遍历方法
| 函数 | 说明 | 适用场景 |
|---|---|---|
| Walk | 使用 Visitor 遍历 | 需要维护状态的复杂遍历 |
| Inspect | 使用函数遍历 | 简单遍历 |
| 打印 AST 结构 | 调试和分析 |
最后更新:2026-04-04
Go 版本:Go 1.23+