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。 - 配置文件无效时以非零状态退出,而不是静默继续。
升级检查顺序
先处理会翻转结论的,再处理新增规则,最后才是纯误报修复。
- SA4023、SA5011。 这两项决定基线结论是否可信。SA5011 已停用,用 gopls 的 nilness 分析器补位;SA4023 的告警逐条复核,尤其涉及无约束泛型和接口值跨分支的场景,必须用 2026.2.2 或更新版本,2.0 与 2.1 都有已知误报。
- 新增 SA9010。 搜一遍
defer后面直接跟函数调用的位置。 - 崩溃面。 泛型结构体字面量 + 弃用字段、间接依赖函数调用、类型参数与零值比较,这三类在旧补丁版本上会直接崩。
- 纯误报修复与默认值变化。 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。