ast.ParseFile需传入非nil的token.FileSet、非空src、含ParseComments和ParseFullFiles的mode,并检查err;ast.Inspect遍历只读节点,修改须更新父节点字段;字符串替换必用strconv.Quote;输出必须用go/format.Node且确保Pos/End有效。

ast.ParseFile 必须配齐四个参数
直接调用 parser.ParseFile 却没返回有效 *ast.File,八成是参数没对齐。它不自动建 token.FileSet、不帮你读文件、也不容忍空源码或错误模式。
必须显式传入:
-
fset := token.NewFileSet()—— 不能为nil,否则所有.Pos()返回 0,后续go/format.Node会 panic -
src要非空:磁盘读取传nil,内存解析传[]byte(如os.ReadFile结果) -
mode至少含parser.ParseComments(要注释)和parser.ParseFullFiles(要函数体),否则FuncDecl.Body或FuncDecl.Doc为nil - 必须检查
err != nil—— BOM、CRLF 混用、语法错都会让file为nil,接着调用ast.Inspect(file, ...)就 panic
用 ast.Inspect 遍历,但别在回调里改节点
ast.Inspect 是唯一能覆盖全部节点类型的遍历方式,但它返回的每个 node 都是只读副本。你在回调里对 node.(*ast.BasicLit).Value = "new" 这种操作完全无效 —— AST 不支持就地修改,go/format 也根本不看这个字段。
真正生效的替换逻辑是:
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 在回调中识别目标节点(如
node.Kind == token.STRING的*ast.BasicLit) - 拿到它的父节点(比如
*ast.AssignStmt、*ast.CompositeLit、*ast.ReturnStmt等) - 根据父节点类型,手动更新其对应字段:例如把
assign.Rhs[0]指向一个新构造的&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(newStr)} - 注意:不能直接赋值给
node变量,它只是形参;必须找到并修改父结构体里的指针字段
字符串替换必须用 strconv.Quote,别手拼引号
手动拼 """ + s + """ 或 "`" + s + "`" 会崩——它无法处理换行、Unicode、反斜杠转义,更不会自动选反引号(当内容含双引号或换行时)。
strconv.Quote 才是唯一正确方式:
- 输入
"a b"→ 输出""a\nb"" - 输入
"he"llo"→ 输出""he\"llo"" - 输入含换行或双引号太多时,自动切到反引号:
`line1 line2` - 别忘了:替换后的新节点
Value字段必须是带引号的完整字面量字符串,不是裸内容
输出修改后的代码必须走 go/format.Node
改完 AST 后不能直接 fmt.Printf 或 ast.Print,那只是调试用,输出不可编译。安全输出的唯一路径是 go/format.Node(要求输入是完整 *ast.File)或 go/printer.Fprint(更底层,需传 *token.FileSet)。
关键约束:
-
go/format.Node要求整个*ast.File的Pos()和End()有效 —— 如果你替换了某个字符串节点但没更新父节点的End,长度变化会导致格式化乱码或 panic - 不要对
format.Source输出做二次字符串替换(比如删空行、加注释),它已是最小合法 Go 代码 - 若只改了部分节点,建议用
go/format.Source全量重格式化字节流,比局部Node更稳
最易被忽略的是:AST 修改后,所有位置信息(Pos/End)仍指向原始源码偏移,而 go/format 依赖这些位置做缩进和换行。如果新字符串长度和原字符串差太多,又没重建 token.FileSet 或调整节点位置,输出大概率错位甚至 panic。实际项目中,宁可全量解析+重写,也别在原 AST 上做局部“打补丁”式替换。

















