golangci-lint 不支持正则或简单配置加规则,所有自定义规则必须用 go/analysis 编写语义感知 analyzer,打包为命令并通过插件机制加载;仅用 ast.Inspect 会因缺失类型、作用域和位置信息导致高误漏检。

直接说结论:golangci-lint 本身不支持“写个正则或配置就加规则”,所有自定义规则必须用 go/analysis 编写语义感知的 analyzer,打包成独立命令,并通过 golangci-lint 的插件机制加载;试图绕过 analysis.Analyzer 结构、只靠 ast.Inspect 遍历 AST 写检查逻辑,90% 会在嵌套作用域、类型推导或位置报告上出错。
为什么不能只用 ast.Inspect 写检查器
很多人从 go/parser 和 ast.Inspect 入手,结果发现:fallthrough 找不到、time.Time 和 *time.Time 分不清、func(x context.Context) 在嵌套函数里漏检——根本原因是原始 AST 没有类型信息、没有作用域链、也没有父节点引用。
-
ast.Inspect是扁平遍历,*ast.CaseClause.Body里的fallthrough不在*ast.IfStmt的直系子节点中 - 字段名叫
CreatedAt,但类型是time.Time还是*time.Time?仅靠*ast.Ident.Name无法判定,必须查pass.TypesInfo.TypeOf(node) - 手动递归时容易 panic:比如对
funcDecl.Type.Results为 nil 时调用Len(),或访问未初始化的field.Type
最小可用 analyzer 必须满足的三要素
golangci-lint 加载插件时会做硬性校验,缺一不可。不是“能编译就行”,而是“命名、结构、行为”全对才能进检查流水线。
-
Analyzer.Name必须全小写、无下划线(如ctxparam),写成ctx-param或ctx_param直接被跳过 -
Analyzer.Doc不能为空字符串,哪怕只写"check http handler context usage"也比留空强 -
Run函数里报错必须用pass.Reportf(node, "..."),且node得是当前包真实 AST 节点(如*ast.FuncDecl),传token.NoPos或伪造位置会导致 CI 报错定位失效
集成进 golangci-lint 的三步验证法
光 go build 出一个二进制不等于能用。真正上线前必须过这三关:
立即学习“go语言免费学习笔记(深入)”;
- 导出变量名必须是
Analyzers,类型为[]*analysis.Analyzer,且每个 analyzer 都已赋值(不能是nil) - 在项目根目录
.golangci.yml中显式启用:linters-settings: custom: - path: ./myanalyzer,路径必须指向可执行文件(非源码) - 运行
golangci-lint run --debug,看日志里是否出现enabled custom linter: myanalyzer;若只报failed to load custom linter,八成是Name或Doc不合规
哪些规则值得花时间自定义
语言学习或团队落地时最容易堆一堆“伪规范”:比如“函数名必须小写”“注释必须句号结尾”。它们要么已有 revive 覆盖,要么根本无法稳定判定。真正该自动化的,是那些影响运行行为、暴露设计缺陷、或违反团队契约的点。
- 检查
http.HandlerFunc是否漏掉context.Context参数——关系到超时控制和取消传播 - 禁止结构体字段直接用
time.Time(强制*time.Time)——避免零值时间引发逻辑误判 - 检测
os.RemoveAll调用后是否忽略返回的error——这是典型资源清理遗漏风险 - 所有自定义规则必须跑在
golangci-lint --fast模式下,单次检查耗时超过 200ms 就得砍掉或加缓存,否则拖垮 CI
最常被忽略的一点:pass.TypesInfo 在 go/analysis 中默认不启用,必须显式声明依赖 inspect.Analyzer 才能拿到作用域和类型绑定信息——没这步,你的“检查 context 是否存在”规则连 func f(ctx context.Context) 都识别不了。


















