go vet 是必须跑的静态检查地基,自带于 Go SDK,500ms 内全量扫描,专查“合法但大概率写错”的代码,如 Printf 类型不匹配、未导出字段带 JSON 标签、sync.WaitGroup 传值不传址;它不报未处理 error 或弃用 API,但为所有静态检查提供基础保障。

go vet 是必须先跑的,它不装、不配、不依赖模块,Go SDK 自带,500ms 内扫完整个项目,专盯“合法但大概率写错了”的代码——比如 fmt.Printf("%s", 42)、结构体字段小写却带 json:"name"、sync.WaitGroup 传值不传址。它不报未处理 error,也不查弃用 API,但它是所有静态检查的地基。
golangci-lint 是实际项目的默认选择
别纠结选哪个 linter,直接用 golangci-lint。它不是单个工具,而是并行跑 staticcheck、errcheck、govet、unused 等十多个检查器的聚合平台。安装命令是:
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.54.2
关键点:
- 必须在项目根目录(有
go.mod)运行golangci-lint run,否则报 “no Go files in directory” - 默认只启用保守子集,漏掉大量问题;必须配
.golangci.yml显式开启errcheck(捕获忽略 error)、staticcheck(查死代码、弃用 API)、unused(找未用函数/变量) - 禁用
golint——它已被官方归档,规则主观、噪音大,2026 年起社区已普遍弃用
staticcheck 单独用只适合特定场景
staticcheck 是语义级深度检查的核心引擎,但**不能脱离模块上下文单独运行**。常见错误:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 在子目录下执行
staticcheck ./subdir→ 报 “no Go files in directory”,因为它需要go.mod加载类型信息,不是简单遍历文件 - CI 中
GO111MODULE=off或用 vendor 模式 → 直接失败,它不支持 GOPATH mode - 第一次运行慢是正常的,它在构建类型图谱;后续缓存后快很多
规则配置要克制:-checks=SA1019,SA9003,SA4006 覆盖弃用 API、无用 return、重复赋值,够用;SA1006(未用 fmt 参数)在模板渲染层极易误报,慎开。
别碰自己写的 AST + reflect 静态检查工具
看到 “用反射做静态分析” 的教程就跳过。反射(reflect)只作用于运行时对象,而静态检查必须在编译前读源码——真正干活的是 go/parser 和 go/ast。有人把 ast.Ident 直接喂给 reflect.TypeOf,结果 panic:nil pointer。AST 节点不是运行时值,它只是语法树上的一个节点。想查 struct tag?得先用 AST 提取原始字符串,再用 reflect.StructTag 解析;想推导变量真实类型?必须上 go/types,仅靠 AST 会把函数名、包别名、未定义标识符全当成变量。

















