Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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: 已导出

快速参考

声明类型

类型用途关键字段
GenDeclvar/const/type 声明Tok, Specs
FuncDecl函数/方法声明Recv, Name, Type, Body

Spec 类型

类型用途关键字段
ValueSpecvar/const 值声明Names, Type, Values
TypeSpectype 类型声明Name, Assign, Type
ImportSpecimport 导入声明Name, Path

语句类型

类型用途关键字段
BlockStmt代码块List
IfStmtif 语句Init, Cond, Body, Else
ForStmtfor 循环Init, Cond, Post, Body
RangeStmtrange 循环Key, Value, X, Body
SwitchStmtswitch 语句Init, Tag, Body
TypeSwitchStmt类型 switchInit, Assign, Body
SelectStmtselect 语句Body
AssignStmt赋值语句Lhs, Tok, Rhs
ReturnStmtreturn 语句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
MapTypeMap 类型Key, Value
StructType结构体类型Fields
FuncType函数类型Params, Results
InterfaceType接口类型Methods
ChanTypeChannel 类型Dir, Value

遍历方法

函数说明适用场景
Walk使用 Visitor 遍历需要维护状态的复杂遍历
Inspect使用函数遍历简单遍历
Print打印 AST 结构调试和分析

最后更新:2026-04-04
Go 版本:Go 1.23+