bigywater.tech
Go · 110

go vet:它在检查哪些常见错误

go vet 能抓到编译器放过的一类错误:格式串和参数不匹配、拷贝了锁、忘了调用 cancel。这篇写一个包含十几种典型错误的文件,看 vet 逐条报了什么,漏了什么,并验证 go test 里自带的那份 vet 只是它的一小部分。

16 条
一个故意写错的文件,go vet 的报告数
35 个
go tool vet help 列出的检查器个数
11 个
go test 自带的 vet 所跑的检查器个数,copylocks 不在其中

典型错误一次看完

实测 · 本机执行输出
  退出码 1;共 16 条报告:
    v.go:27:2: the cancel function is not used on all paths (possible context leak)
    v.go:32:1: this return statement may be reached without using the cancel var defined on line 27
    v.go:74:2: result of fmt.Sprintf call not used
    v.go:81:10: WaitGroup.Add called from inside new goroutine
    v.go:56:33: redundant or: a == 1 || a == 1
    v.go:70:9: second argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface type
    v.go:62:8: using resp before checking for errors
    v.go:19:14: fmt.Printf format %d has arg "字符串" of wrong type string
    v.go:20:2: fmt.Println arg list ends with redundant newline
    v.go:21:17: fmt.Printf format %s reads arg #2, but call has 1 arg
    v.go:54:36: x (32 bits) too small for shift of 40
    v.go:58:36: conversion from int to string yields a string of one rune, not a string of digits
    v.go:49:3: struct field tag `json: "b"` not compatible with reflect.StructTag.Get: bad syntax for struct tag value
    v.go:42:2: unreachable code
    v.go:36:2: self-assignment of x
    v.go:24:17: CopyLock passes lock by value: lab.T contains sync.Mutex

格式串与参数不符、Println 末尾多一个换行、拷贝含 Mutex 的结构体、cancel 没在所有路径上调用、WaitGroup.Add 写在 goroutine 里面、string(int)、位移超过类型宽度、errors.As 第二个参数不是指针、在检查 err 之前就用 resp……都被 vet 指出了位置和原因。重复的 json tag key 没有被报告,所以 vet 不是完备的。

先决条件

实测 · 本机执行输出
  go tool vet help 列出的检查器共 35 个:appends asmdecl assign atomic bools buildtag cgocall composites copylocks defers directive errorsas framepointer hostport httpresponse ifaceassert loopclosure lostcancel nilfunc printf shift sigchanyzer slog stdmethods stdversion stringintconv structtag testinggoroutine tests timeformat unmarshal unreachable unsafeptr unusedresult waitgroup

vet 要代码能通过类型检查。我第一次写的文件里有“声明了 cancel 没使用”和“缺少 return”两个编译错误,vet 只报了这一条编译错误就停了,后面的检查一条都没跑。

go test 里的 vet

实测 · 本机执行输出
  只有 printf 问题和 copylocks 问题时 go test:退出码 1;FAIL	lab [build failed] | FAIL | # lab | # [lab] | ./v.go:15:29: fmt.Printf format %d has arg "字符串" of wrong type string
  只有 copylocks 问题时 go test:退出码 0;ok  	lab	0.102s
  同样的代码 go vet:退出码 1;v.go:10:17: CopyLock passes lock by value: lab.T contains sync.Mutex

go test 会先跑一个高置信度子集(11 个检查器),发现问题就不运行测试。实验里同时有 printf 和 copylocks 问题时,go test 以 printf 问题失败;只有 copylocks 问题时,go test 通过,go vet 却失败。所以持续集成里要单独跑 go vet ./...。

文档

实测 · 本机执行输出
  As part of building a test binary, go test runs go vet on the package and its test source files to identify significant problems. If go vet finds any problems, go test reports those and does not run the test binary. Only a high-confidence subset of the default go vet checks are used. That subset is: atomic, bools, buildtag, directive, errorsas, ifaceassert, nilfunc, printf, stdversion, stringintconv, and tests. You can see the documentation for these and other vet tests via "go doc cmd/vet". To disable the running of go vet, use the -vet=off flag.

go help test 列出的子集是 atomic、bools、buildtag、directive、errorsas、ifaceassert、nilfunc、printf、stdversion、stringintconv、tests,-vet=off 可关闭。

分步演示

go vet
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Go 1.27.1(brew);用 go run / go build / go test 直接编译运行,需要第三方库时经代理从 proxy.golang.org 下载;计时用 time.Now / testing.B,数字每次运行会有小幅抖动。被检查的文件是自己写的,每个检查器至少写了一个典型错误;报告条数按 go vet ./... 的输出行数;检查器清单来自 go tool vet help;读过的文档:go help test 中关于 vet 的一段
    • 没有验证:每个检查器的准确率与误报、自定义 vet 工具(-vettool)、go vet -json 输出、结构体 tag 重复 key 为什么没有被报、loopclosure 在新语言版本下基本不再触发的情况、与 staticcheck 等第三方检查器的差别
    京ICP备2026067502号-1京公网安备11010502063487号