Go 怎么解析源码并列出函数名和参数
如果要给 Go 项目生成 API 索引、检查函数命名,或做一个轻量的代码导航工具,不必先把源码当成普通文本用正则切分。更稳妥的做法是用 go/parser 生成 AST,再从 *ast.FuncDecl 读取名称和参数字段。下面的程序接收一个 Go 文件路径,输出普通函数、方法、参数签名和声明行号。
核心关系是:parser.ParseFile负责把源码变成*ast.File,ast.Inspect找到*ast.FuncDecl,FuncDecl.Type.Params.List保存参数字段;类型本身再交给format.Node还原。
- 解析阶段使用
token.NewFileSet(),否则无法把 AST 位置换算成行号。 - 函数名在
FuncDecl.Name.Name,方法是否带接收者看FuncDecl.Recv。 - 参数不能只读取一个名字:
a, b int在 AST 中是一个字段、两个名称,需要展开。
先把源码变成可遍历的 AST
parser.ParseFile 可以从文件名读取源码,也可以接收字符串、字节切片或 io.Reader。这里直接传文件路径,第四个参数使用 parser.SkipObjectResolution,因为我们只关心语法树,不需要已经废弃的标识符对象解析。
FileSet 不只是形式参数,它记录文件和位置之间的映射。后面通过 fset.Position(decl.Pos()).Line 取得函数声明所在行。

从 FuncDecl 读取函数名与方法接收者
顶层函数和方法都会表示为 *ast.FuncDecl。名称从 Name.Name 读取;如果 Recv != nil,说明当前声明是方法,可以再把接收者类型格式化出来。使用 ast.Inspect 的好处是遍历逻辑清楚,并且不会把普通标识符误当函数名。
下面的完整示例只输出声明节点,不解析函数体里的函数调用,因此结果表示“这个文件声明了哪些函数”,不是“这些函数运行时调用了谁”。
package main
import (
"bytes"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"os"
"strings"
)
// formatExpr 把 AST 类型节点还原成读者能直接阅读的 Go 类型文本。
func formatExpr(fset *token.FileSet, expr ast.Expr) string {
var buf bytes.Buffer
if err := format.Node(&buf, fset, expr); err != nil {
return ""
}
return buf.String()
}
// formatParams 展开参数字段,兼容 a, b int 这种共享类型的写法。
func formatParams(fset *token.FileSet, fields *ast.FieldList) []string {
if fields == nil {
return nil
}
params := make([]string, 0, fields.NumFields())
for _, field := range fields.List {
typ := formatExpr(fset, field.Type)
if len(field.Names) == 0 {
// 只有类型时,保留无名参数的真实类型。
params = append(params, typ)
continue
}
for _, name := range field.Names {
// 一个 Field 可能对应多个同类型名称,因此要逐名输出。
params = append(params, name.Name+" "+typ)
}
}
return params
}
func main() {
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "用法:go run main.go path/to/file.go")
os.Exit(2)
}
filename := os.Args[1]
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, filename, nil, parser.SkipObjectResolution)
if err != nil {
// 读取失败和语法错误都应直接返回,避免输出不完整索引。
fmt.Fprintf(os.Stderr, "解析失败:%v\n", err)
os.Exit(1)
}
ast.Inspect(file, func(node ast.Node) bool {
decl, ok := node.(*ast.FuncDecl)
if !ok {
return true
}
kind := "函数"
receiver := ""
if decl.Recv != nil && decl.Recv.NumFields() > 0 {
kind = "方法"
receiver = formatExpr(fset, decl.Recv.List[0].Type)
}
params := strings.Join(formatParams(fset, decl.Type.Params), ", ")
line := fset.Position(decl.Pos()).Line
if receiver != "" {
fmt.Printf("%s %s.%s(%s) - 第%d行\n", kind, receiver, decl.Name.Name, params, line)
} else {
fmt.Printf("%s %s(%s) - 第%d行\n", kind, decl.Name.Name, params, line)
}
return true
})
}
把参数字段还原成可读签名
参数列表的关键是 ast.FieldList。每个 Field 有一个类型和零个或多个名称,因此不能写成“取 field.Names[0]”的固定逻辑。对于 func Open(path string, mode ...int),可得到 path string 与 mode ...int;对于 func Pair(a, b int),应展开成两个参数。
使用 format.Node 而不是自己拼接类型,能够保留指针、切片、映射、函数类型和可变参数的语法形状。它只负责格式化 AST,不代表做了类型检查;例如别名最终是什么底层类型,必须交给 go/types 或加载包的工具链判断。

| 源码写法 | AST 读取重点 | 输出策略 |
|---|---|---|
func Sum(a, b int) | 一个 Field,两个 Names | 展开为 a int、b int |
func Read([]byte) | Names 为空 | 只输出 []byte |
func Log(v ...string) | Type 是 Ellipsis | 由 format.Node 保留 ...string |
func (s Store) Save() | Recv 非空 | 标记为方法并显示接收者 |
用位置和边界检查结果
运行时把待分析文件路径传给程序,例如 go run main.go ./sample.go。正常输出中的行号来自 FileSet,不是通过统计换行符猜出来的。若源文件有语法错误,ParseFile 可能返回部分 AST 和错误;索引工具通常应该像示例一样遇到错误就停止,避免把半截结果当成完整清单。
这个方案适合单文件或只看语法结构的轻量工具。如果需求扩大到整个模块、构建标签、类型推导、跨包引用或方法集分析,应改用 golang.org/x/tools/go/packages 配合 go/types。解析 AST 能回答“声明长什么样”,不能单独回答“这个标识符最终指向哪个定义”。
相关问题
Go AST 里怎么区分函数和方法?
检查 *ast.FuncDecl 的 Recv:普通函数为 nil,方法通常包含接收者字段。名称都从 Name.Name 读取。
为什么参数名称会少于参数数量?
Go 允许多个参数共享一个类型,例如 a, b int 只对应一个 Field。遍历 field.Names 才能展开每个名称;无名参数则应保留类型。
解析 Go 源码一定要做类型检查吗?
不一定。列出声明名称和语法形式只需要 go/parser 与 go/ast;需要跨包引用、别名和实际类型时,才增加加载与类型检查。
CSS sticky 不生效怎么检查父容器 overflow
- 上一篇
- CSS sticky 不生效怎么检查父容器 overflow
- 下一篇
- 兽音译者能翻译网址吗?文本转换、复制与本地处理说明
-
- Golang · Go教程 | 1小时前 | go · 工程实践 · go generate · Go 代码生成 go generate
- Go 怎么用 go generate 自动生成重复代码
- 136浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · Go Modules · 构建部署 · go mod vendor Go 离线构建 vendor/modules.txt
- Go 项目怎么打包依赖以支持离线构建
- 258浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go 怎么生成测试覆盖率文件并定位未覆盖函数
- 113浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Go 单元测试怎么替换 HTTP 客户端而不访问网络
- 165浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Go 多个测试怎么共享一次初始化并统一清理
- 398浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Go 文件系统代码怎么用内存文件做单元测试
- 278浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 162次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 88次使用
-
- LangGPT
- LangGPT是一种受编程语言启发的结构化提示词设计工具,提供双层框架、模块化模板及变量功能,帮助用户高效编写高质量Prompt。该项目已在GitHub免费开源,适用于内容创作、编程辅助等多场景。
- 13次使用
-
- ClickPrompt
- ClickPrompt是一款专为AI提示词编写者设计的开源在线工具,支持Stable Diffusion绘图、ChatGPT对话及GitHub Copilot代码辅助。提供Prompt自动生成、一键运行、社区分享及可视化优化功能,帮助用户高效获取精准AI输出。
- 49次使用
-
- PromptHero
- PromptHero是专业的AI提示词搜索引擎与优化平台,支持Stable Diffusion、Midjourney等主流模型。提供海量提示词库、分类搜索、在线课程及社区互动,助力用户高效生成高质量AI图像与文本。
- 32次使用
-
- Java 性能优化上线清单:从定位、改造到灰度发布
- 2026-06-11 860浏览
-
- Spring Boot 压测验证:Gatling、JMeter 与性能回归门禁
- 2026-06-11 843浏览
-
- Java NMT 非堆内存排查:Direct Buffer、线程栈与 Metaspace 分析
- 2026-06-11 826浏览
-
- Spring Boot 容器内存优化:JVM 堆、非堆与 MaxRAMPercentage
- 2026-06-11 809浏览
-
- Tomcat 连接与线程参数调优:maxThreads、acceptCount 与 KeepAlive
- 2026-06-11 792浏览

