必须启用parser.parsecomments模式才能获取//注释,否则*ast.file.comments为空;需用fileset.position(c.pos()).line获取行号,并用strings.trimprefix清理"//"前缀。

Go 的 go/ast 不会把 // 注释挂到语法树节点上,默认直接丢弃;想拿到,必须主动启用解析开关并手动对齐位置——否则你看到的 FuncDecl.Doc 或 TypeSpec.Doc 里根本不会有单行注释。
parser.ParseComments 必须显式传入
默认调用 parser.ParseFile 时注释被跳过,*ast.File.Comments 是空切片。不加这个 flag,后续所有位置计算、遍历都无从谈起。
- 正确写法:
parser.ParseFile(fset, filename, src, parser.ParseComments) - 错误写法:
parser.ParseFile(fset, filename, src, 0)—— 即使源码里全是//,file.Comments也为空 - 若处理内存字符串(非文件),
filename不能为"",否则fset.File(c.Pos())可能返回nil,导致崩溃
Doc 字段只包含“绑定”的注释,不是所有上方注释
FuncDecl.Doc 或 Field.Doc 只收录紧贴声明**正上方且中间无空行**的 // 或 /* */;而 TypeSpec 根本没有 Doc 字段——它的注释实际挂在外层 *ast.GenDecl 上。
- 结构体类型注释要查
GenDecl.Doc,而不是TypeSpec.Doc - 函数参数列表里的内联注释(如
func f(x int // ignore)不属于Doc,也不会出现在file.Comments中——parser在词法阶段就报错或截断 - 构建标签(
// +build)会被当成普通注释提取,但语义完全不同,需额外用正则或go/build包识别
行号和位置必须用 fileset.Position() 计算
Comment.Pos() 是字节偏移,不是行号;直接用 c.Pos() 做条件判断或渲染定位,结果几乎总是错的。
- 获取真实行号:
fset.Position(c.Pos()).Line - 判断注释是否在函数上方:
c.Pos() ,而非比较 <code>.Line - 清理文本前缀用
strings.TrimPrefix(c.Text, "//"),别用strings.TrimLeft(c.Text, "/")——后者会误删/* */里的/
动态插入注释时,token.File.AddLine() 不可省略
用 go/printer 输出新 AST 时,如果注释位置超出原始源码范围(比如往空结构体里加字段+注释),printer 会因查不到行号而把所有注释堆在开头。
- 每新增一个
*ast.Comment,必须调用:fset.File(c.Pos()).AddLine(int(c.Pos())) - 每新增一个
*ast.Field,也要为它的NamePos和Type.Pos()对应位置注册行号 - 漏掉
AddLine是注释“漂移”最常见原因,且错误表现隐蔽——输出看似正常,但缩进/换行错乱
注释位置不是靠猜或空行数判断的,是靠字节偏移和 token.File 的行号映射严格对齐的;一旦手动构造节点,这两者就必须同步维护,否则 go/printer 就失去锚点。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











