golangci-lint 的 govet 默认仅启用约1/3子检查项,需在 .golangci.yml 中显式配置 enable-all: true 或 enable: ["shadow", "atomic"];接口误用由 staticcheck 的 SA 规则捕获;errcheck 对接口方法调用检查有限,需启用 check-exported: true;自定义接口规范需用 revive。

golangci-lint 的 govet 检查项不是开箱即用的完整集
govet 是 golangci-lint 默认启用的核心检查器,但它默认只开启约 1/3 的子检查项(如 printf、copylocks),像 shadow、atomic、nilness 这些关键项默认关闭。不显式配置,就等于漏掉大量语义隐患。
必须在 .golangci.yml 中明确启用全量或按需开启:
-
govet: enable-all: true是最省事的做法;若只想开特定项,用enable: ["shadow", "atomic"] - 某些子检查(如
shadow)在测试文件中误报率高,可单独禁用:disable: ["shadow"] -
nilness依赖控制流分析,对大型函数可能超时,建议搭配run.timeout: 2m防止静默失败
接口相关误用靠 staticcheck 而非 govet 捕获
Go 接口使用错误(比如将指针传给期望值接收者的接口方法、空接口赋值时类型擦除导致 panic、interface{} 与 any 混用)基本不在 govet 覆盖范围内。真正负责这类问题的是 staticcheck 的 SA 系列规则。
常见接口误用及对应规则:
立即学习“go语言免费学习笔记(深入)”;
-
SA1019:调用已弃用的接口方法或实现(如http.RoundTripper的旧字段) -
SA1029:向io.Writer写入未处理 error 的字节切片(典型于w.Write([]byte("x"))忽略返回值) -
SA4023:对interface{}做无意义的类型断言(如v.(interface{})) -
SA5009:在switch v := x.(type)中遗漏default分支且未覆盖所有可能类型
这些规则默认开启,但前提是 staticcheck 本身被启用——很多人只开了 govet 和 errcheck,结果接口层面的深层问题完全漏检。
errcheck 对接口方法调用的检查有边界条件
errcheck 不是简单地“查所有返回 error 的函数”,它依赖符号解析和调用上下文。对接口方法的检查尤其容易失效:
- 仅检查直接调用:如
io.WriteString(w, s)→ 会报错;但w.Write([]byte(s))(其中w是io.Writer接口)→ 默认不检查,除非启用errcheck: check-exported: true - 忽略测试上下文:默认跳过
t.Fatal、log.Fatal等终止流程的调用,但不会跳过mockClient.Do(req)这类接口调用——如果 mock 实现没返回 error,errcheck就无法感知 - 要覆盖接口方法,必须显式配置:
errcheck: check-exported: true,并确保 linter 能正确解析接口定义(即不能在vendor/或生成代码里跑)
自定义接口检查规则得用 revive,不是 govet 或 staticcheck
如果你需要强制要求“所有实现了 io.Closer 的结构体必须带 Close() error 方法”,或者“禁止将 context.Context 作为非第一个参数传入接口方法”——这类业务强相关的约束,govet 和 staticcheck 都做不到。
revive 是唯一支持编写自定义规则的活跃 linter(golint 已归档):
- 规则写在
linters-settings.revive.rules下,用 Go 表达式匹配 AST 节点 - 例如限制
context.Context位置:rule: "context-first-param" pattern: "func(.*context\.Context.*)" - 注意:启用
revive后,govet和staticcheck的同类检查(如参数类型)可能重复,建议关掉重叠项
真正难的不是写规则,而是让规则在接口嵌套、泛型方法、别名类型等场景下稳定匹配——多数团队最终只用 revive 做极少数几条高价值规范,而非全量覆盖。


















