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/doc - 包文档提取

go/doc 包提供了从 Go AST 中提取文档注释的功能,用于生成包的文档信息。

概述

go/doc 包用于从 Go 源代码的 AST 中提取和组织文档信息,是 godoc 工具的核心组件。

包导入

import (
    "go/doc"
    "go/ast"
    "go/token"
    "fmt"
)

基本使用

// 1. 解析源文件
fset := token.NewFileSet()
file, _ := parser.ParseFile(fset, "main.go", src, parser.ParseComments)

// 2. 提取包文档
pkgDoc := doc.New(file, "main", doc.AllDecls)

// 3. 访问文档信息
fmt.Printf("包名:%s\n", pkgDoc.Name)
fmt.Printf("文档:%s\n", pkgDoc.Doc)
fmt.Printf("函数数量:%d\n", len(pkgDoc.Funcs))

典型示例

示例 1:提取包的完整文档

package main

import (
    "fmt"
    "go/ast"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
// Package math 提供基本的数学常量和函数。
package math

// Pi 是圆周率。
const Pi = 3.14159

// E 是自然对数的底。
const E = 2.71828

// Add 计算两个整数的和。
func Add(a, b int) int {
    return a + b
}

// Person 表示一个人。
type Person struct {
    Name string
    Age  int
}

// SayHello 打印问候语。
func (p *Person) SayHello() {
    fmt.Println("Hello, I'm", p.Name)
}
`

    // 解析源文件
    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "math.go", src, parser.ParseComments)

    // 提取包文档
    pkgDoc := doc.New(file, "math", doc.AllDecls)

    // 打印包信息
    fmt.Printf("包名:%s\n", pkgDoc.Name)
    fmt.Printf("包文档:%s\n", pkgDoc.Doc)
    fmt.Printf("\n常量:\n")
    for _, c := range pkgDoc.Consts {
        fmt.Printf("  %s: %s\n", c.Names[0], c.Doc)
    }
    fmt.Printf("\n函数:\n")
    for _, f := range pkgDoc.Funcs {
        fmt.Printf("  %s: %s\n", f.Name, f.Doc)
    }
    fmt.Printf("\n类型:\n")
    for _, t := range pkgDoc.Types {
        fmt.Printf("  %s: %s\n", t.Name, t.Doc)
        for _, m := range t.Methods {
            fmt.Printf("    方法:%s: %s\n", m.Name, m.Doc)
        }
    }
}

运行

$ go run main.go
包名:math
包文档:Package math 提供基本的数学常量和函数。

常量:
  E: E 是自然对数的底。
  Pi: Pi 是圆周率。

函数:
  Add: Add 计算两个整数的和。

类型:
  Person: Person 表示一个人。
    方法:SayHello: SayHello 打印问候语。

示例 2:使用不同选项提取文档

package main

import (
    "fmt"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
package example

// PublicFunc 是导出的函数。
func PublicFunc() {}

func privateFunc() {}

// PublicVar 是导出的变量。
var PublicVar int

var privateVar int
`

    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "example.go", src, parser.ParseComments)

    // 只提取导出的内容
    pkgDoc1 := doc.New(file, "example", doc.AllDecls)
    fmt.Println("=== 所有声明 ===")
    fmt.Printf("函数:%d\n", len(pkgDoc1.Funcs))
    fmt.Printf("变量:%d\n", len(pkgDoc1.Vars))

    // 过滤未导出的内容
    pkgDoc2 := doc.New(file, "example", doc.AllDecls|doc.PreserveAST)
    fmt.Println("\n=== 保留 AST ===")
    fmt.Printf("AST: %v\n", pkgDoc2.Decls != nil)
}

运行

$ go run main.go
=== 所有声明 ===
函数:1
变量:1

=== 保留 AST ===
AST: true

一、包级别常量

模式标志

Mode

定义

type Mode uint

说明

  • 控制文档提取的行为
  • 使用位掩码组合多个选项

包含所有声明

AllDecls

定义

const AllDecls Mode = 1 << iota

说明

  • 包含所有声明(包括未导出的)
  • 默认只包含导出的声明

示例

package main

import (
    "fmt"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
package example

// Public 是导出的函数。
func Public() {}

func private() {}
`

    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "example.go", src, parser.ParseComments)

    // 默认模式(只导出)
    pkgDoc1 := doc.New(file, "example", 0)
    fmt.Printf("默认模式 - 函数数:%d\n", len(pkgDoc1.Funcs))

    // 包含所有声明
    pkgDoc2 := doc.New(file, "example", doc.AllDecls)
    fmt.Printf("AllDecls 模式 - 函数数:%d\n", len(pkgDoc2.Funcs))
}

运行

$ go run main.go
默认模式 - 函数数:1
AllDecls 模式 - 函数数:2

保留 AST

PreserveAST

定义

const PreserveAST Mode = 1 << iota

说明

  • 保留对 AST 的引用
  • 用于需要进一步分析 AST 的场景

示例

package main

import (
    "fmt"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
package example

func Test() {}
`

    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "example.go", src, parser.ParseComments)

    // 不保留 AST
    pkgDoc1 := doc.New(file, "example", 0)
    fmt.Printf("默认 - Decls: %v\n", pkgDoc1.Decls == nil)

    // 保留 AST
    pkgDoc2 := doc.New(file, "example", doc.PreserveAST)
    fmt.Printf("PreserveAST - Decls: %v\n", pkgDoc2.Decls != nil)
}

运行

$ go run main.go
默认 - Decls: true
PreserveAST - Decls: false

过滤方法

FilterFuncs

定义

const FilterFuncs Mode = 1 << iota

说明

  • 只包含函数(过滤掉类型、变量、常量)
  • 用于只关心函数的场景

包含方法

AllMethods

定义

const AllMethods Mode = 1 << iota

说明

  • 包含所有方法(包括未导出的)
  • 默认只包含导出的方法

示例

package main

import (
    "fmt"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
package example

type MyType struct{}

// PublicMethod 是导出的方法。
func (m MyType) PublicMethod() {}

func (m MyType) privateMethod() {}
`

    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "example.go", src, parser.ParseComments)

    // 默认模式
    pkgDoc1 := doc.New(file, "example", 0)
    if len(pkgDoc1.Types) > 0 {
        fmt.Printf("默认模式 - 方法数:%d\n", len(pkgDoc1.Types[0].Methods))
    }

    // 包含所有方法
    pkgDoc2 := doc.New(file, "example", doc.AllMethods)
    if len(pkgDoc2.Types) > 0 {
        fmt.Printf("AllMethods 模式 - 方法数:%d\n", len(pkgDoc2.Types[0].Methods))
    }
}

运行

$ go run main.go
默认模式 - 方法数:1
AllMethods 模式 - 方法数:2

二、核心结构体

包文档结构体

Package

定义

type Package struct {
    Doc        *ast.CommentGroup // 包文档注释
    ImportPath string            // 导入路径
    Name       string            // 包名
    Notes      map[string][]*CommentGroup // 注释(如 BUGs、TODOs)
    
    // 导出内容
    Consts []*Value    // 常量列表
    Vars   []*Value    // 变量列表
    Types  []*Type     // 类型列表
    Funcs  []*Func     // 函数列表
    
    // AST 相关
    Files    []*ast.File    // 源文件列表
    Decls    []ast.Decl     // 声明列表
    Bugs     []*CommentGroup // BUG 注释
    Deprecated string        // 弃用说明
}

说明

  • 表示一个包的完整文档信息
  • 包含所有导出的常量、变量、类型和函数
  • 可选择性地保留 AST 信息

示例

package main

import (
    "fmt"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
// Package example 提供示例功能。
package example

// Version 是版本号。
const Version = "1.0.0"

// Config 是配置结构。
type Config struct {
    Name string
}

// NewConfig 创建配置。
func NewConfig() *Config {
    return &Config{}
}
`

    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "example.go", src, parser.ParseComments)

    pkgDoc := doc.New(file, "example", doc.AllDecls)

    fmt.Printf("包名:%s\n", pkgDoc.Name)
    fmt.Printf("包文档:%s\n", pkgDoc.Doc.Text())
    fmt.Printf("常量数:%d\n", len(pkgDoc.Consts))
    fmt.Printf("类型数:%d\n", len(pkgDoc.Types))
    fmt.Printf("函数数:%d\n", len(pkgDoc.Funcs))
}

运行

$ go run main.go
包名:example
包文档:Package example 提供示例功能。
常量数:1
类型数:1
函数数:1

值文档结构体

Value

定义

type Value struct {
    Doc   *ast.CommentGroup // 值的文档注释
    Names []string          // 值名列表(对于 iota 组)
    Type  ast.Expr          // 类型表达式
    Decl  *ast.GenDecl      // 声明节点
}

说明

  • 表示常量或变量的文档
  • 支持 iota 枚举组(多个名字共享一个类型)

示例

package main

import (
    "fmt"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
package example

// 星期枚举
const (
    Sunday    = iota // 星期日
    Monday           // 星期一
    Tuesday          // 星期二
)

// MaxSize 是最大尺寸。
const MaxSize = 1024
`

    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "example.go", src, parser.ParseComments)

    pkgDoc := doc.New(file, "example", doc.AllDecls)

    for _, c := range pkgDoc.Consts {
        fmt.Printf("常量:%v\n", c.Names)
        fmt.Printf("  文档:%s\n", c.Doc.Text())
    }
}

运行

$ go run main.go
常量:[Sunday Monday Tuesday]
  文档:星期枚举
常量:[MaxSize]
  文档:MaxSize 是最大尺寸。

类型文档结构体

Type

定义

type Type struct {
    Doc   *ast.CommentGroup // 类型的文档注释
    Name  string            // 类型名
    Decl  *ast.GenDecl      // 声明节点
    Const []*Value          // 关联的常量
    Var   []*Value          // 关联的变量
    Func  []*Func           // 关联的函数(工厂函数等)
    Method []*Func          // 类型的方法
}

说明

  • 表示一个类型的完整文档
  • 包含与该类型关联的常量、变量、函数和方法

示例

package main

import (
    "fmt"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
package example

// Result 表示操作结果。
type Result int

// Success 表示成功。
const Success Result = 0

// NewResult 创建结果。
func NewResult() Result {
    return Success
}

// Error 返回错误信息。
func (r Result) Error() string {
    return "error"
}
`

    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "example.go", src, parser.ParseComments)

    pkgDoc := doc.New(file, "example", doc.AllDecls)

    for _, t := range pkgDoc.Types {
        fmt.Printf("类型:%s\n", t.Name)
        fmt.Printf("  文档:%s\n", t.Doc.Text())
        fmt.Printf("  关联常量:%d\n", len(t.Const))
        fmt.Printf("  关联函数:%d\n", len(t.Func))
        fmt.Printf("  方法:%d\n", len(t.Method))
    }
}

运行

$ go run main.go
类型:Result
  文档:Result 表示操作结果。
  关联常量:1
  关联函数:1
  方法:1

函数文档结构体

Func

定义

type Func struct {
    Doc  *ast.CommentGroup // 函数的文档注释
    Name string            // 函数名
    Decl *ast.FuncDecl     // 函数声明节点
    Recv *Field           // 接收者(如果是方法)
}

说明

  • 表示函数或方法的文档
  • 对于方法,Recv 字段包含接收者信息

示例

package main

import (
    "fmt"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
package example

// Add 计算两个数的和。
func Add(a, b int) int {
    return a + b
}

// Calculator 是计算器。
type Calculator struct{}

// Subtract 计算两个数的差。
func (c Calculator) Subtract(a, b int) int {
    return a - b
}
`

    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "example.go", src, parser.ParseComments)

    pkgDoc := doc.New(file, "example", doc.AllDecls)

    fmt.Println("=== 函数 ===")
    for _, f := range pkgDoc.Funcs {
        fmt.Printf("%s: %s\n", f.Name, f.Doc.Text())
    }

    fmt.Println("\n=== 类型方法 ===")
    for _, t := range pkgDoc.Types {
        for _, m := range t.Methods {
            fmt.Printf("%s.%s: %s\n", t.Name, m.Name, m.Doc.Text())
        }
    }
}

运行

$ go run main.go
=== 函数 ===
Add: Add 计算两个数的和。

=== 类型方法 ===
Calculator.Subtract: Subtract 计算两个数的差。

字段结构体

Field

定义

type Field struct {
    Names []*ast.Ident // 字段名列表
    Type  ast.Expr     // 字段类型
}

说明

  • 表示函数参数、返回值或方法接收者
  • Names 为空表示匿名参数

三、包级别函数(按字母顺序)

从 AST 创建包文档

New

定义

func New(pkg *ast.File, importPath string, mode Mode) *Package

说明

  • 从 AST 创建包文档
  • 最常用的函数

参数

  • pkg:解析后的 AST 文件
  • importPath:包的导入路径
  • mode:文档提取模式

示例

package main

import (
    "fmt"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
// Package math 提供数学函数。
package math

// Pi 是圆周率。
const Pi = 3.14159

// Add 计算和。
func Add(a, b float64) float64 {
    return a + b
}
`

    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "math.go", src, parser.ParseComments)

    pkgDoc := doc.New(file, "math", doc.AllDecls)

    fmt.Printf("包:%s\n", pkgDoc.Name)
    fmt.Printf("文档:%s\n", pkgDoc.Doc.Text())
    fmt.Printf("常量:%d\n", len(pkgDoc.Consts))
    fmt.Printf("函数:%d\n", len(pkgDoc.Funcs))
}

运行

$ go run main.go
包:math
文档:Package math 提供数学函数。
常量:1
函数:1

从多个文件创建包文档

NewFromFiles

定义

func NewFromFiles(fset *token.FileSet, files []*ast.File, importPath string, mode Mode) *Package

说明

  • 从多个 AST 文件创建包文档
  • 适合多文件包

参数

  • fset:token 文件集
  • files:AST 文件列表
  • importPath:包的导入路径
  • mode:文档提取模式

示例

package main

import (
    "fmt"
    "go/ast"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src1 := `
package example

// Func1 是函数 1。
func Func1() {}
`

    src2 := `
package example

// Func2 是函数 2。
func Func2() {}
`

    fset := token.NewFileSet()
    
    // 解析多个文件
    file1, _ := parser.ParseFile(fset, "file1.go", src1, parser.ParseComments)
    file2, _ := parser.ParseFile(fset, "file2.go", src2, parser.ParseComments)
    
    files := []*ast.File{file1, file2}

    // 从多个文件创建包文档
    pkgDoc := doc.NewFromFiles(fset, files, "example", doc.AllDecls)

    fmt.Printf("包:%s\n", pkgDoc.Name)
    fmt.Printf("函数数:%d\n", len(pkgDoc.Funcs))
    
    for _, f := range pkgDoc.Funcs {
        fmt.Printf("  - %s: %s\n", f.Name, f.Doc.Text())
    }
}

运行

$ go run main.go
包:example
函数数:2
  - Func1: Func1 是函数 1。
  - Func2: Func2 是函数 2。

读取包文档

Readme

定义

func Readme(fsys fs.FS) string

说明

  • 从文件系统中读取 README 文件
  • 支持 README、README.md、README.txt 等

参数

  • fsys:文件系统接口

示例

package main

import (
    "embed"
    "fmt"
    "go/doc"
)

//go:embed *.md
var fsys embed.FS

func main() {
    // 假设有 README.md 文件
    readme := doc.Readme(fsys)
    fmt.Printf("README 长度:%d\n", len(readme))
}

提取函数文档

Synopsis

定义

func Synopsis(s string) string

说明

  • 提取文档的第一句话作为摘要
  • 用于列表或概览显示

示例

package main

import (
    "fmt"
    "go/doc"
)

func main() {
    longDoc := `Add 计算两个整数的和。
这是一个详细的说明,
包含多行内容。`

    short := doc.Synopsis(longDoc)
    fmt.Printf("摘要:%s\n", short)
}

运行

$ go run main.go
摘要:Add 计算两个整数的和。

判断是否导出

IsExported

定义

func IsExported(name string) bool

说明

  • 检查标识符是否已导出(首字母大写)
  • 与 ast.IsExported 功能相同

示例

package main

import (
    "fmt"
    "go/doc"
)

func main() {
    names := []string{"Public", "private", "Export", "internal"}
    
    for _, name := range names {
        fmt.Printf("%s: %v\n", name, doc.IsExported(name))
    }
}

运行

$ go run main.go
Public: true
private: false
Export: true
internal: false

判断是否是空白标识符

IsBlank

定义

func IsBlank(name string) bool

说明

  • 检查是否为空白标识符(_)

示例

package main

import (
    "fmt"
    "go/doc"
)

func main() {
    names := []string{"_", "x", "unused"}
    
    for _, name := range names {
        fmt.Printf("%s: %v\n", name, doc.IsBlank(name))
    }
}

运行

$ go run main.go
_: true
x: false
unused: false

获取类型名称

TypeName

定义

func TypeName(x ast.Expr) string

说明

  • 从类型表达式获取类型名称
  • 用于显示类型信息

示例

package main

import (
    "fmt"
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
package example

type MyType struct{}
`

    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "example.go", src, 0)

    // 获取类型声明
    for _, decl := range file.Decls {
        if gen, ok := decl.(*ast.GenDecl); ok {
            for _, spec := range gen.Specs {
                if ts, ok := spec.(*ast.TypeSpec); ok {
                    name := doc.TypeName(ts.Type)
                    fmt.Printf("类型名:%s\n", name)
                }
            }
        }
    }
}

运行

$ go run main.go
类型名:struct{MyType struct{}}

打印包文档

Print

定义

func Print(pkg *Package)

说明

  • 打印包文档到标准输出
  • 用于调试

示例

package main

import (
    "go/doc"
    "go/parser"
    "go/token"
)

func main() {
    src := `
package example

func Test() {}
`

    fset := token.NewFileSet()
    file, _ := parser.ParseFile(fset, "example.go", src, parser.ParseComments)
    pkgDoc := doc.New(file, "example", doc.AllDecls)

    // 打印包文档
    doc.Print(pkgDoc)
}

四、快速参考

模式常量

常量说明
AllDecls包含所有声明(包括未导出的)
PreserveAST保留 AST 引用
FilterFuncs只包含函数
AllMethods包含所有方法

核心结构体

结构体说明主要字段
Package包文档Name, Doc, Consts, Vars, Types, Funcs
Value值文档(常量/变量)Doc, Names, Type, Decl
Type类型文档Doc, Name, Const, Var, Func, Methods
Func函数/方法文档Doc, Name, Decl, Recv
Field字段信息Names, Type

包级别函数

函数说明
New(pkg, importPath, mode)从 AST 创建包文档
NewFromFiles(fset, files, importPath, mode)从多个文件创建包文档
Readme(fsys)读取 README 文件
Synopsis(s)提取文档摘要
IsExported(name)检查是否已导出
IsBlank(name)检查是否空白标识符
TypeName(x)获取类型名称
Print(pkg)打印包文档

使用场景

场景推荐函数
单文件包文档New
多文件包文档NewFromFiles
只关心导出内容mode = 0
包含未导出内容mode = AllDecls
需要进一步分析 ASTmode = PreserveAST
提取文档摘要Synopsis
检查导出状态IsExported

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