编程 Staticcheck 2026.2.2 升上去:SA4023 重写会翻转旧结论,SA5011 被停用

2026-10-26 00:03:45

Staticcheck 2026.2.2:SA4023 重写后告警会翻转,wasm2go 用例内存从 10GiB 降到 1.5GiB

项目地址:dominikh/go-tools,检查清单见 staticcheck.dev/docs/checks,命令行用法见 staticcheck.dev/docs/running-staticcheck/cli/。版本信息来源是 GitHub Releases 页(https://github.com/dominikh/go-tools/releases)。

升级后实际会看到的三类现象

新告警:

  • SA9010 新增:defer foo() 而 foo 返回函数。
  • SA1019 现在会报结构体字面量里给弃用字段赋值。
  • SA1026 / SA9005 也检查 MarshalIdent 类调用。
  • SA4003 覆盖泛型比较。
  • SA4006 支持复合赋值(+=、^=)与后置 ++/--。

消失的告警:

  • S1005 不再对 map 索引的空白标识符赋值报警,v, _ := m[k] 可以用了。
  • U1000 对「泛型 + 接口」组合的误报大幅减少(issue 1616 / 1440 / 1294)。
  • SA4023 重新实现后,可能不再报旧版本能发现的问题。

崩溃与停用:

  • SA5011 因内部改动被停用,替代是 gopls 的 nilness 分析器。
  • SA4023 在检查间接依赖函数调用时崩溃(#1741)。
  • SA1019 在泛型结构体字面量里设置弃用字段时崩溃(#1821)。
  • 类型参数值与零值做 <= 等顺序比较时崩溃。

还有一类既不算新增也不算消失的变化:SA6005 对 strings.EqualFold 结果取反时给出更清晰的提示;ST1001 的 dot_import_whitelist 默认值加入了 simd/archsimd。

2026.2 的底座:Go 1.27、GOCACHEPROG、忽略大小写

2026.2(v0.8.0,2026-08-20)的主体改动:

  • 新增 Go 1.27 支持,弃用标准库 API 数据库同步覆盖 Go 1.27,并支持两个新语言特性:泛型方法(generic methods)、结构体字面量直接引用嵌入字段。
  • 支持 GOCACHEPROG:与 go 工具使用相同的环境变量与协议,可以自建 build / fact / diagnostic 缓存实现。
  • 所有接受检查名的地方(-checks、-explain 标志、lint 指令、配置文件里的 checks 设置)忽略大小写,SA1000、sa1000、sA1000 指向同一检查。
  • 复杂控制流的性能明显改善,例如 wasm2go 的产物。极端案例内存从 10 GiB 降到 1.5 GiB,wall time 从 73s 降到 13s。

新增 SA9010:defer 了一个返回函数的函数

func Start() func() { return func() {} }

func user() {
    defer Start()
}

defer Start() 既不会在函数返回前调用 Start,也不会调用 Start 返回的那个函数。要延迟执行的是返回值本身:

func user() {
    f := Start()
    defer f()
}

看规则说明:

staticcheck -explain SA9010

会翻转结论的两处:SA4023 与 SA5011

SA4023 是重写,不是微调。 新实现可能发现以前发现不了的问题,也可能漏掉以前能发现的问题;它是新版本里最需要人工复核的一类告警。重写之后紧接着出了两个补丁版本:

  • 2026.2.1(v0.8.1,2026-08-21)修 2026.2 引入的两个误报:不再把无约束泛型当作永不 nil(#1736);在并不涉及 typed nil 的场景不再触发(#1737)。
  • 2026.2.2(v0.8.2,2026-10-12)继续修 nilness 追踪:接口值跨分支的 nilness、加载全局变量时的 nilness(#1737、#1738);不再假设无约束类型参数的零值总是 nil;检查间接依赖函数调用时不再崩溃(#1741)。

SA5011 被停用。 原因是内部改动,是否恢复没有时间表,替代方案是 gopls 里的 nilness 分析器。原先靠 SA5011 拦住的问题,需要换工具覆盖。

按检查分组看语义变化

弃用 API:

  • SA1019 除了原有场景,现在也报结构体字面量里给弃用字段赋值,诊断格式更一致;泛型结构体字面量设置弃用字段的崩溃已在 2026.2.2 修掉(#1821)。

序列化:

  • SA1026 与 SA9005 现在也检查 MarshalIdent 类函数调用。

泛型相关:

  • SA4003 的比较检查覆盖泛型。
  • U1000 对泛型与接口的组合支持更好,误报应大幅减少(issue 1616、1440、1294)。要专门看「仅被测试使用」的代码,可以加 -tests=false。

赋值与运算符:

  • SA4006 支持复合运算符(+=、^= 等)以及后置 ++/--,所以以前漏掉的无效赋值现在会报。

简化类:

  • S1005 不再对 map 索引的空白标识符赋值报警,v, _ := m[k] 这种「键可能缺失、用零值可以」的写法被允许。
  • SA6005 在需要对 strings.EqualFold 结果取反时,提示更清楚。

默认配置:

  • ST1001 的 dot_import_whitelist 默认值包含新的 simd/archsimd 包,这类点导入不再被风格检查拦。

文档补充涉及 SA1002、SA1012、SA1014、SA1020、SA2000、SA2003、SA4010,只影响说明文本,不影响判定。

中间表示与兼容性修复

2026.2.2 里有一批不体现在检查规则上、但直接影响能不能跑完的修复:

  • 支持 Go 1.27.2 使用的新 export data 格式。
  • 修复中间表示中与方法泛型、泛型类型方法内闭包、nil unsafe.Pointer 相关的若干 bug。
  • range-over-func 循环跳转到标签时,正确区分 goto 与 continue。
  • 保留单 case select 中接收赋值(receive assignments)的正确求值顺序。
  • 构建中间表示时降低内存占用、减少无谓工作,尤其是处理方法与泛型类型时。

从更早版本升上来还会碰到的行为变化

2026.1(v0.7.0,2026-02-13)除了补齐 Go 1.25 / 1.26 的弃用库(含 Go 1.21 遗漏的 crypto/elliptic 弃用)与 Go 1.26 的 new(expr),还有三条会影响 CI 的改动:

  • 版本不匹配检查放宽,不再比较 patch 级别:用 Go 1.26.0 构建的 Staticcheck 可以检查使用 Go 1.26.1 的代码。
  • 不再打开不是普通文件(或不是指向普通文件的符号链接)的 staticcheck.conf。
  • 配置文件无效时以非零状态退出,而不是静默继续。

升级检查顺序

先处理会翻转结论的,再处理新增规则,最后才是纯误报修复。

  1. SA4023、SA5011。 这两项决定基线结论是否可信。SA5011 已停用,用 gopls 的 nilness 分析器补位;SA4023 的告警逐条复核,尤其涉及无约束泛型和接口值跨分支的场景,必须用 2026.2.2 或更新版本,2.0 与 2.1 都有已知误报。
  2. 新增 SA9010。 搜一遍 defer 后面直接跟函数调用的位置。
  3. 崩溃面。 泛型结构体字面量 + 弃用字段、间接依赖函数调用、类型参数与零值比较,这三类在旧补丁版本上会直接崩。
  4. 纯误报修复与默认值变化。 S1005、U1000、ST1001 这类属于「升级后告警变少」,确认减少的部分确实是误报即可。

可执行命令:

# 安装到 2026.2.2
go install honnef.co/go/tools/cmd/staticcheck@2026.2.2

# 看某条规则的说明、起始版本与在线文档链接
staticcheck -explain S1039
staticcheck -explain SA9010

# 只跑指定检查;检查名大小写不敏感
staticcheck -checks SA4023,SA5011,SA9010 ./...

# 机器可读输出,便于和旧版本结果做 diff
staticcheck -f json ./... > staticcheck-2026.2.json

# 覆盖目标 Go 版本(默认取 go.mod 的 go 指令)
staticcheck -go 1.0 ./...

# 跳过测试分析,对 U1000 有用,可找出仅被测试使用的代码
staticcheck -tests=false ./...

# 通过 go vet 驱动
go vet -vettool=$(which staticcheck) ./...

补充几点命令行约定:staticcheck 像 go vet、go build 一样接受包模式,支持 -tags;默认同时分析包与其测试;从 Go 1.21 起 go.mod 的 go 指令表示最低版本,旧模块需要用 -go 覆盖。检查前缀分类为 SA(staticcheck,正确性)、S(simple,简化)、ST(stylecheck,风格)、QF(quickfix);默认配置排除 SA9003、ST1000、ST1003、ST1016、ST1020~ST1023。

复制全文 生成海报 staticcheck Go lint SA4023 SA5011 SA9010 代码质量

推荐文章

程序员茄子在线接单