文章目录
快速上手
一个简单的命令就是go vet [directory]
返回结果大约如下图
$ go vet ./alecthomas/gometalinter/
adjcmartix.go:85: suspect or: char != "" || char != " "
main.go:55: arg usage in Fprint call is a function value, not a function call
resolve.go:161: unreachable code
sparse.go:58: _m might be too small for shift of 32
go vet会检查什么?
在我们日常编写代码的时候, 我们会不时写出一些虽然符合语法逻辑但是不符合自己的想法, 而且会有未预期效果的代码.go vet总结了部分常见的类型并检查, 值得注意的是, 虽然绝大多数时候go vet
检查项都是准确的(错误), 但是仍然需要结合自身想法考虑.
默认情况下,go tool vet
会检索所有类型的错误(相当于-all).我们可以使用选项来指定特定类型
查找所有选项
go tool vet help
对于原子性函数的不寻常使用(atomic)
可以使用go vet -atomic
func main() {
var a int32 = 0
var wg sync.WaitGroup
for i := 0; i < 500; i++ {
wg.Add(1)
go func() {
a = atomic.AddInt32(&a, 1)
wg.Done()
}()
}
wg.Wait()
}
main.go:15:4: direct assignment to atomic value
这里go vet
就发现第8行代码直接赋值的错误
锁复制(copylocks)
锁永远不应该复制,否则将会无法实现加锁的功能.一般来说,出现锁复制往往是粗心大意的成果,我们可以使用go vet -copylocks
来发现该问题
func main() {
var lock sync.Mutex
l := lock
l.Lock()
l.Unlock()
}
from vet: main.go:9:7: assignment copies lock value to l: sync.Mutex
锁复制的另一种形式就是值方法,go vet
同样可以发现该问题
type Foo struct {
lock sync.Mutex
}
func (f Foo) Lock() {
f.lock.Lock()
}
func main() {
f := Foo{lock: sync.Mutex{}}
f.Lock()
}
from vet: main.go:9:9: Lock passes lock by value: command-line-arguments.Foo contains sync.Mutex
循环闭包(loopclosure)
在使用闭包的时候,一个常见的错误写法如下图所示
func main() {
var wg sync.WaitGroup
for _, v := range []int{0,1,2,3} {
wg.Add(1)
go func() {
print(v)
wg.Done()
}()
}
wg.Wait()
}
3333
from vet: main.go:10:12: loop variable v captured by func literal
go vet -loopclosure
可以检测出该问题
没有取消context(lostcancel)
在使用context包的时候,为了避免context泄露,一定要执行Cancel函数.go vet -lostcancel
可以检查出该问题
func Foo(ctx context.Context) {}
func main() {
ctx, _ := context.WithCancel(context.Background())
Foo(ctx)
}
from vet: main.go:8:7: the cancel function returned by context.WithCancel should be called, not discarded, to avoid a context leak
没有实现标准库要求接口(stdmethods)
go vet
同样会检查在使用标准库的时候你实现的接口函数有着相同的签名
type Foo struct {}
func (f Foo) MarshalJSON() (string, error) {
return `{a: 0}`, nil
}
func main() {
f := Foo{}
j, _ := json.Marshal(f)
println(string(j))
}{}
from vet: main.go:7:14: method MarshalJSON() (string, error) should have signature MarshalJSON() ([]byte, error)
可以使用go vet -stdmethods
专项检查
不合约定的字段标签(structtag)
字段标签一般来说需要符合在reflect包中的约定. 有些时候一个空格可能会造成难以发现的bug
type Foo struct {
A int `json: "foo"`
}
func main() {
f := Foo{}
j, _ := json.Marshal(f)
println(string(j))
}{"A":0}
from vet: main.go:6:2: struct field tag `json: "foo"` not compatible with reflect.StructTag.Get: bad syntax for struct tag value
可以使用go vet -structtag
发现
其他选项
可以具体查看这里
定制分析器
go vet
强大的地方在于,我们完全可以基于自己的需求定制分析器
可以看这里
定制指令
既然可以单独使用分析器,我们也可以自行组合一套自己需要的分析器并封装为指令.如下图所示
package main
import (
"golang.org/x/tools/go/analysis/multichecker"
"golang.org/x/tools/go/analysis/passes/atomic"
"golang.org/x/tools/go/analysis/passes/loopclosure"
"github.com/blanchonvincent/ctxarg/analysis/passes/ctxarg"
)
func main() {
multichecker.Main(
atomic.Analyzer,
loopclosure.Analyzer,
ctxarg.Analyzer,
)
}
参考
https://medium.com/a-journey-with-go/go-vet-command-is-more-powerful-than-you-think-563e9fdec2f5
https://golang.org/cmd/vet/
https://medium.com/a-journey-with-go/go-how-to-build-your-own-analyzer-f6d83315586f