自定义golang linter规则必须使用inspect.analyzer构建inspector遍历ast,而非手动递归;analyzer.name须全小写无下划线,doc不能为空,导出变量名必须为复数analyzers;revive.toml仅控制内置规则,新增规则需编译为二进制并显式引用。

为什么直接写 AST 遍历会漏掉 fallthrough 或嵌套参数
因为原始 ast.Node 没有父节点指针、没有作用域上下文,更不保证语义完整性。比如 fallthrough 语句不在 *ast.IfStmt 或 *ast.BlockStmt 的 Stmts 列表里,它藏在 *ast.CaseClause.Body 中;又比如函数参数类型可能是 *ast.StarExpr(*time.Time),而非直接是 *ast.Ident(time.Time)。手动递归遍历 pass.Files 极易跳过这些分支。
正确做法是依赖 inspect.Analyzer 提供的 *inspector.Inspector:
- 它预建了节点父子关系和作用域映射,
inspector.Preorder可按需筛选*ast.BranchStmt或*ast.FuncDecl - 所有节点位置(
token.Pos)都来自真实解析上下文,pass.Reportf报错时能准确定位到源码行 - 必须声明
Requires: []*analysis.Analyzer{inspect.Analyzer},否则pass.ResultOf[inspect.Analyzer]为空,panic: nil pointer dereference
golangci-lint 加载自定义 analyzer 失败的三个硬性条件
编译通过 ≠ 能被 golangci-lint 识别。它对插件有隐式契约,缺一不可:
-
Analyzer.Name必须全小写、无下划线(如ctxparam,不能是ctx_param或CtxParam) -
Analyzer.Doc不能为空字符串(哪怕只写"check context param") - 导出变量名必须是
Analyzers(复数),类型为[]*analysis.Analyzer,且包级初始化即赋值(不能延迟计算)
常见错误:把 analyzer 放在 main.go 里却没导出 var Analyzers = []*analysis.Analyzer{&Analyzer} → 运行 golangci-lint run 时静默忽略,无任何提示。
revive.toml 里加 rule.xxx 不生效的根本原因
revive 的 TOML 配置文件只控制已有规则的开关与参数,**不支持运行时注入新逻辑**。它底层仍是 go/analysis 框架,所有规则都得编译进二进制。你在 TOML 里写 [rule.context-first-param],revive 启动时根本找不到对应 Go 实现,直接跳过,连 warning 都不打。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
要让新规则生效,必须:
- 用 Go 实现一个
*analysis.Analyzer(如检查func (c context.Context, ...)) - 用
singlechecker打包成可执行文件(名字必须以-结尾,如contextparam-) - 在
.revive.toml中通过rules字段引用该二进制路径,而非规则名
go vet -vettool=./xxx- 调用时位置信息错乱
报错位置不准(比如总显示在文件第一行)通常是因为传给 pass.Reportf 的 pos 不是 AST 节点的真实 token.Pos。常见错误包括:
- 传了
nil或硬编码整数(如pass.Reportf(token.NoPos, "...")) - 用了
field.Type.Pos()却没确认field.Type非空(比如参数无类型标注时为nil) - 误用
funcDecl.Name.Pos()替代funcDecl.Type.Params.List[0].Pos(),导致报错锚点偏移到函数名而非参数声明处
安全做法:只对明确非空的 AST 节点调用 .Pos(),并优先使用 field.Pos()(参数字段本身的位置)而非其子节点。
真正卡住人的地方,往往不是写不出逻辑,而是 analyzer 名字大小写不对、Doc 字段留空、或忘了导出 Analyzers 变量——这些错误不会编译失败,也不会运行报错,只是让规则彻底消失。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










