go vet 和 staticcheck 不够用,因为它们只覆盖未使用变量、空指针解引用等通用规则,无法强制内部规范(如HTTP handler必须以Handler结尾、日志必须用log.With);需用go/analysis编写Analyzer,通过analysis.Analyzer类型定义Name/Doc/Run,利用pass.TypesInfo获取语义信息,导出为Analyzers变量并配置到golangci-lint中。

为什么 go vet 和 staticcheck 不够用
因为它们只覆盖通用规则,比如未使用的变量、空指针解引用、死代码等。当你需要强制团队遵守内部规范时——比如所有 HTTP handler 必须以 Handler 结尾、日志必须用 log.With 而非裸调 log.Printf、禁止在 test 文件里写 os.Exit——这些工具完全不管。
这时候就得自己写 linter,本质是 AST 遍历 + 规则匹配,不是改配置,而是写 Go 代码。
用 golang.org/x/tools/go/analysis 编写可集成的 Analyzer
这是官方推荐方式,生成的 linter 可被 golangci-lint、go vet 直接加载,不是独立命令行工具。
- 必须实现
analysis.Analyzer类型,核心字段是Name(唯一标识)、Doc(描述)、Run(主逻辑) -
Run函数接收*analysis.Pass,它包含已解析的 AST、类型信息、文件路径等,不用自己 parse - 不要手动调用
parser.ParseFile或types.NewChecker——Pass已帮你准备好,重复初始化会崩溃或漏报 - 示例:检查函数名是否含
Handler后缀
func run(pass *analysis.Pass) (interface{}, error) {
for _, file := range pass.Files {
ast.Inspect(file, func(n ast.Node) bool {
if f, ok := n.(*ast.FuncDecl); ok {
if !strings.HasSuffix(f.Name.Name, "Handler") {
pass.Reportf(f.Pos(), "handler function %s should end with 'Handler'", f.Name.Name)
}
}
return true
})
}
return nil, nil
}
如何让自定义 linter 被 golangci-lint 识别
不能只放个 main.go 就完事。golangci-lint 要求 analyzer 必须导出为变量,且包路径能被 import。
立即学习“go语言免费学习笔记(深入)”;
- 你的模块需有
go.mod,且module名不与标准库冲突(比如别叫go.lint) - 在某个
.go文件中导出全局变量:var Analyzer = &analysis.Analyzer{...} -
golangci-lint的配置文件.golangci.yml中加:
linters-settings:
custom:
my-handler-check:
path: github.com/yourname/mylint/analyzer
description: enforce Handler suffix on HTTP handlers
original-url: https://github.com/yourname/mylint
然后执行 golangci-lint run 就会自动加载并运行它。
常见坑:AST 遍历时机和作用域判断不准
直接遍历 pass.Files 得到的是语法树,但没做类型检查。比如想判断某个调用是不是 log.Printf,仅靠函数名匹配会误报——用户可能定义了同名函数,或用了别名导入。
- 正确做法:用
pass.TypesInfo查类型信息,再通过typesutil.Uses或typesutil.Packages定位真实对象 - 别在
Run里做耗时操作(如读文件、HTTP 请求),linter 必须快,否则拖慢 CI - 测试 analyzer 一定要用
golang.org/x/tools/go/analysis/analysistest,它模拟真实构建环境;手写ast.File测试容易绕过类型系统,导致线上漏报 - 注意
pass.Pkg.Path是包导入路径,不是文件路径;过滤 test 文件要用strings.HasSuffix(pass.Pkg.Name, "_test"),而不是看文件名
AST 是静态结构,类型信息才是语义关键。漏掉 TypesInfo 就等于只做字符串匹配,规则越复杂,误报率越高。


















