直接用analysis.Run会panic,因它依赖gopls/go vet的完整构建上下文,而手写调用无法正确初始化;应改用analysistest.Run进行测试。

为什么直接用 analysis.Run 会 panic?
因为 analysis.Run 是给 gopls 或 go vet 内部用的,它依赖完整的构建上下文(loader.Config),而你手写调用时几乎不可能正确初始化。真实场景里,你应该用 analysistest.Run —— 它封装了 fake FS、fake build config 和结果断言逻辑,专为测试 linter 设计。
常见错误现象:panic: no packages matched 或 nil pointer dereference in pass.ResultOf,基本都是没走 analysistest 导致的。
- 测试入口必须是
analysistest.Run(t, analysistest.TestData, yourAnalyzer) -
TestData目录下放src/a/a.go这类结构,不能平铺文件 - 你的 analyzer 的
Run函数里别碰pass.Pkg,优先用pass.TypesInfo和pass.ResultOf[otherAnalyzer]
如何让 analyzer 只检查某个函数体,而不是整个包?
靠 pass.Report 前加 AST 范围判断。Go 的 analysis 不提供“作用域过滤”钩子,所有节点都会进 Run,你要自己切。
典型场景:只报 fmt.Printf 在 main 函数里的调用,忽略 test 文件或工具函数。
- 先用
pass.ResultOf[inspect.Analyzer]拿到*inspect.Inspector - 用
inspector.Preorder遍历,条件是ast.IsFuncLit(n) || (n == *funcDecl && n.(*ast.FuncDecl).Name.Name == "main") - 对匹配节点再做
ast.Inspect向下找*ast.CallExpr,检查fun是否为"fmt.Printf" - 别在
Run顶层遍历所有*ast.File,那样性能差且无法限定作用域
Analyzer 的 Requires 字段填什么才不报错?
填另一个 *analysis.Analyzer 的指针,不是名字字符串。填错最常见后果:运行时 panic “invalid Requires entry”,或者 pass.ResultOf 返回 nil。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
比如你要依赖类型信息,就得写 Requires: []*analysis.Analyzer{typesutil.Analyzer};如果还要看 AST 结构,就再加上 inspect.Analyzer。
- 标准库提供的 analyzer 都在
golang.org/x/tools/go/analysis/passes/下,如buildssa.Analyzer、ctrlflow.Analyzer - 自己写的 analyzer 如果被别人依赖,必须导出变量(如
var MyAnalyzer = &analysis.Analyzer{...}),否则对方Requires时取不到地址 -
Requires是 DAG,不能循环,比如 A require B,B require A 就会死锁
为什么本地跑通了,但集成到 gopls 就不触发?
因为 gopls 默认只启用白名单内的 analyzer(比如 shadow、unparam),你自己的 analyzer 不在配置里,它根本不会加载。
解决方法只有两个:改 gopls 启动参数,或改 gopls 源码重新编译。前者更可行:
- VS Code 中,在
settings.json加:"gopls": { "analyses": { "youranalyzername": true } } - 确保你的 analyzer 的
Name字段是合法标识符(全小写、无下划线、不以数字开头),比如"errorlint"可以,"my-linter"不行 -
gopls加载 analyzer 时会调Analyzer.Run两次:一次空包(预热),一次真实包。如果你的Run里有未判空的pass.Files[0]访问,第二次就会 panic
真正难的不是写 analyzer,而是搞清它在哪一层被谁调用、传了什么、哪些字段必须非空——这些细节文档里几乎没有,只能翻 analysistest 和 gopls/internal/lsp/cache 的源码。

















