GO工具链简易入门-go vet

快速上手

一个简单的命令就是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

发布了31 篇原创文章 · 获赞 32 · 访问量 724

猜你喜欢

转载自blog.csdn.net/a348752377/article/details/105580966