go vet不是可选项,是上线前必须跑的一环;默认仅检查当前目录基础项,漏子包、测试文件及未导出字段JSON标签等问题,须用go vet ./ -tests ./并显式启用-shadow、-printf等关键检查器。

go vet 不是可选项,是上线前必须跑的一环;但默认只检查几个基础项,多数高危问题根本不会报——得手动开关键检查器。
go vet ./ 为什么总漏掉子包和测试文件
直接执行 go vet(不带参数)只检查当前目录下可编译的 .go 文件,完全忽略 ./internal、./cmd、foo_test.go 这类路径。更糟的是,如果当前目录不是 module 根,它可能连 import path 都推断错,静默跳过分析。
- 必须用
go vet ./:斜杠表示“当前模块下所有包”,递归扫描全部子目录 - 想包含测试文件?加
-tests:go vet -tests ./ - 非 module 环境?先确认
GO111MODULE=on,否则 fallback 到 GOPATH 模式,行为不可控 - CI 中别信 “本地能过”,统一用
go list ./ | grep -v '/vendor\|/third_party'排除第三方路径再传给go vet
printf 格式错误报 “call has arguments but no format verb” 怎么修
这是最常被复制粘贴坑到的点:fmt.Println("user:", name) 改成 fmt.Printf("user:", name) 后,字符串里没动词(%s、%d),go vet 就报这个错——它不是语法错误,但运行时输出会是 user:%!s(MISSING) 这种鬼东西。
- 检查
fmt.Printf、fmt.Sprintf、fmt.Fprintf的第一个参数,确保每个%后紧跟合法动词,或用%%转义字面百分号 - 自定义日志函数(如
logx.Infof)也要管?加-printfuncs=Infof,Warnf,Errorf - 硬编码 SQL 或路径里含
%?比如"SELECT * FROM users WHERE name LIKE '%user%'"→ 必须写成"... LIKE '%%user%%'"
为什么 struct 字段加了 json:"name" 却序列化为空
字段名小写(未导出),但打了 json:"name" tag,go vet 会直接报 struct field tag not compatible with reflect.StructTag。这不是警告,是明确告诉你:这段 JSON 序列化永远不生效,因为反射看不到未导出字段。
立即学习“go语言免费学习笔记(深入)”;
- 修复只有两个选择:把字段首字母大写(
Name string),或删掉json:tag - 误报?比如用了
unsafe或自定义序列化库 → 在该行加//go:novet跳过(仅限你确认安全) - 注意构建标签影响:如果字段在
//go:build unit下才存在,而你跑go vet -tags=ci ./,它就根本看不到这个字段,也不会报错
range 循环中取地址存进切片,为什么所有元素都指向同一个值
典型闭包陷阱:for _, v := range items { ptrs = append(ptrs, &v) }。go vet 默认会报 loop variable v captured by func literal(如果后面有 goroutine 或闭包),但对纯 append(&v) 默认不报——得靠 -shadow 或更准的 -copylocks 类检查器辅助发现。
- 正确写法是显式副本:
for _, v := range items { v := v; ptrs = append(ptrs, &v) } - 想让
go vet主动揪这类问题?加-shadow=true(Go 1.22+ 必须显式开) - 但注意:
-shadow不报跨函数遮蔽,也不报 struct field 名和局部变量同名的情况,别指望它覆盖全部 - 真正保险的做法:搭配
staticcheck(它有SA5008规则专治这个)
最常被忽略的其实是边界:它不检查 vendor 下的代码,不分析未被 import 的文件,也看不出 interface{} 底层是不是指针。所谓“静态检查”,只是在类型系统和 AST 层做有限推演——工具再强,也得靠人读懂它报的每一行,而不是扫一眼零报错就合上终端。


















