go tool vet是你的好朋友,不要忽视它。

vet是一个优雅的工具,每个Go开发者都要知道并会使用它。它会做代码静态检查发现可能的bug或者可疑的构造。vet是Go tool套件的一部分,我们会在以后的文章中详细描述tool套件。它和go编译器一起发布,这意味着它不需要额外的依赖,可以很方便地通过以下的命令调用:

$ go tool vet <directory|files>

本文中所有的go代码段可以正常编译。这使得go vet有价值:它可以在编译阶段和运行阶段发现bug。

同时也注意,本文中的大多数代码都是故意写的很难看,不要使用。

在go vet和go tool vet之间选择

go vetgo tool vet实际上是两个分开的命令。

go vet,只在一个单独的包内可用,不能使用flag 选项(来激活某些指定的检测)。

go tool vet更加完整,它可用用于文件和目录。目录被递归遍历来找到包。go tool vet也可以按照检测的分组激活选项。

你可以打开一个终端,并比较go vet --help 和go tool vet --help两个命令的不同。

Print-format 错误

尽管go是强类型的,但是printf-format错误不会在编译阶段检测。C开发者可能使用默认激活的gcc的-Wformat选项。如果参数不能匹配格式它可以给出一个很好的警告:

warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]

不幸的是,在Go里面编译器没有任何输出。这是vet发挥作用的时候了。考虑下面的例子:

package mainimport "fmt"

func main() {
str := "hello world!"
fmt.Printf("%d\n", str)
}

这是一个典型的错误,一个坏的printf 格式。因为str是一个字符串,所以format应该用%s,而不是%d

这个代码编译后运行,打印出%!d(string=hello world!),不够友好。你可以点击源码下面的“run”链接来自己检查。现在,我们开始运行vet。

$ go tool vet ex1.go

ex1.go:7: arg str for printf verb %d of wrong type: string

当一个指针被使用时,vet也可以检测:

package main
import "fmt" func main() {
str := "hello world!"
fmt.Printf("%s\n", &str)
}
$ go tool vet ex2.go

ex2.go:7: arg &str for printf verb %s of wrong type: *string

vet也可以找到所有的Printf()家族函数(Printf(), Sprintf(), Fprintf(), Errorf(), Fatalf(), Logf(), Panicf()等)格式错误。但是如果你要实现一个函数,接收和printf类似的参数,你可以使用-printfuncs选项使得vet来检测。

package main
import "fmt" func customLogf(str string, args ...interface{}) {
fmt.Printf(str, args...)
}
func main() {
i :=
customLogf("the answer is %s\n", i)
}
$ go tool vet custom-printf-func.go
$ go tool vet -printfuncs customLogf custom-printf-func.go custom-printf-func.go:11: arg i for printf verb %s of wrong type: int

你可以看到如果没有-printfuncs选项,vet没有任何输出。

Boolean 错误

vet可以检查一直为true、false或者冗余的表达式。

package main

import "fmt"

func main() {
var i int // always true
fmt.Println(i != || i != ) // always false
fmt.Println(i == && i == ) // redundant check
fmt.Println(i == && i == )
}
$ go vet bool-expr.go
bool-expr.go:9: suspect or: i != 0 || i != 1
bool-expr.go:12: suspect and: i == 0 && i == 1
bool-expr.go:15: redundant and: i == 0 && i == 0

这种类型的警告常常是非常危险的,可以引起讨厌的bug。大多数情况下是由于排版错误引起的。

Range 循环

当读取变量的时候,在range块内的go协程可能是有问题的。在这些场景下,vet可以检测到它们:

package main

import "fmt"

func main() {
words := []string{"foo", "bar", "baz"}
for _, word := range words {
go func() {
fmt.Println(word)
}()
}
}

注意,这个代码包含竞态,可能不输出任何东西。事实上,main函数可能在所有的协程执行前已经结束,这导致进程退出。

$ go tool vet range.go
range.go:10: range variable word captured by func literal

Unreachable的代码

下面的例子包含3个函数,带有不能到达的代码,每个函数使用了不同的方式。

package main

import "fmt"

func add(a int, b int) int {
return a + b
fmt.Println("unreachable")
return
} func div(a int, b int) int {
if b == {
panic("division by 0")
} else {
return a / b
}
fmt.Println("unreachable")
return
} func fibonnaci(n int) int {
switch n {
case : return
case : return
default: return fibonnaci(n-) + fibonnaci(n-)
}
fmt.Println("unreachable")
return
} func main() {
fmt.Println(add(, ))
fmt.Println(div(, ))
fmt.Println(fibonnaci())
}
$ go vet unreachable.go

unreachable.go:8: unreachable code
unreachable.go:19: unreachable code
unreachable.go:33: unreachable code

混杂的错误

这里是一个代码段,包含了其他的几个vet可以检测的混杂的错误:

package main
import (
"fmt"
"log"
"net/http"
) func f() {}
func main() { // Self assignment
i :=
i = i // a declared function cannot be nil
fmt.Println(f == nil) // shift too long
fmt.Println(i >> ) // res used before checking err
res, err := http.Get("https://www.spreadsheetdb.io/")
defer res.Body.Close()
if err != nil {
log.Fatal(err)
}
}
$ go tool vet misc.go

misc.go:14: self-assignment of i to i
misc.go:17: comparison of function f == nil is always false
misc.go:20: i might be too small for shift of 32
misc.go:24: using res before checking for errors

误报和漏报

有时,vet可能忽略了错误,并警告可疑代码,这些代码实际上是正确的。下面的例子:

package main

import "fmt"

func main() {
rate := // this condition can never be true
if rate > && rate < {
fmt.Println("rate %:", rate)
}
}
$ go tool vet false.go

false.go:10: possible formatting directive in Println call

这种情况很明显永远都不是true,但是并不会检测出来。然而,vet警告了一种可能的错误(使用Println()而不是Printf()),这里的Println()非常好用。

总的来说,使用go tool vet提示很少会有误报与漏报的情况。

性能

vet的README描述了,只是可能的错误是值得检测的。这种方式保证了vet不会变慢。

此时,Docker包含了23M的Go代码(包含依赖)。在Core i5机器上,vet花费了21.6秒来分析它。这是1MB/s的数量级。

可能你期待有一天,可以看到这些“不可能的检查”包含在vet里面。为了满足所有人的需求,默认不激活它们可能是一个好办法。如果检查在技术上是可行的,并且在现实生活中可以找到实际的缺陷项目,那么把它作为一个选项是有价值的。

vet和build比较

虽然vet是不完美的,但是它仍然是一个非常有价值的工具,它应该在所有的Go项目中定期使用。它是那么有价值,以至于它甚至可以让我们怀疑是不是有些检测不应该被编译器检测到。为什么有人会编译一个检测到有prrintf格式错误的代码呢?

vet的使用

go vet 和 go tool vet基本上功能类似,go tool vet可以递归的对package进行语法检测,可以自行测试区别。

对一个.go源文件进行检查

下面的vet.go代码有一行语法错误,我们用go tool vet vet.go 检查

....

一个包下所有源文件进行检测

go tool vet source/directory/*.go

对一个package进行语法检查

我们同样可以利用vet 对一个package进行检查,当然传入的包名必须是 相对路径 或者完整package。

例如我当前项目目录在$GOPATH/src/Test ,那么传入可以输入 go tool vet Test/vet 对vet包进行语法检查

如果我当前工作目录就是Test 那么我也可以直接输入 go tool vet ./vet 利用相对路径进行语法检查

不可以同时对package和源文件进行检查,但可以同时对多个包或者多个源文件进行检查

检测多个package

go tool vet  package1  package2

检测多个源文件

go tool vet file1.go file2.go

错误的用法

go tool vet file.go package1

附加tags

我们还可以给vet 传递一些 tag 来指定检测行为,默认是all,全部检查,当传入以下tag的时候all将被设置为false

使用方法

go tool vet -atomic=false test.go

更多tag含义如下表,我从网上寻找,大家也可以去golang 官方去找 vet command的文档,里面更精确地描述。

golang使用vet进行语法检查的更多相关文章

  1. Golang 基础之基础语法梳理 (二)

    大家好,今天将梳理出的 Go语言基础语法内容,分享给大家. 请多多指教,谢谢. 本次<Go语言基础语法内容>共分为三个章节,本文为第二章节 Golang 基础之基础语法梳理 (一) Gol ...

  2. 如何在Texstudio内加载语法检查词典?

    如何在Texstudio编辑软件内加载"语法检查词典"? How to make dictionary work in TexStudio I am using TexStudio ...

  3. emacs配置eslint 语法检查.找不到node解决

    使用emacs配置eslint 当调用语法检查时报错 Suspicious state from syntax checker javascript-eslint: Checker javascrip ...

  4. [No000013]在Office中关闭自动拼写检查和自动语法检查

    大家知道有时候语法检查很麻烦,搞得文档里都是红线和绿线.解决办法就是关闭自动拼写检查.现在我们来介绍怎么关闭office包括Word .Outlook .PowerPoint .OneNote .Pu ...

  5. 一个简单的C语言语法检查器的实现

    我自己的实现方法的核心过程:首先用一个非终结符代表所有要检查的程序代码,然后根据文法将这个整体的符号不断展开,以拼凑成按检查的程序的顺序排列的终结符序列,能成功说明语法正确,否则有错误. 关键词:分词 ...

  6. Grunt 之 使用 JavaScript 语法检查工具 jshint

    前端开发环境准备好了,我们准备开始进行开发. 前端开发的主力语言是 JavaScript,这是一种脚本语言,没有编译器,也就没有了编译器带给我们的语法检查,怎样保证代码的质量呢?jshint 是一个强 ...

  7. VIM 语法检查

    VIM Grammar Check 一.Language Tool Create by Dominique Pellé REFER:LanguageTool wikipedia REFER:Langu ...

  8. linux下常用语言的语法检查插件整理

    linux下常用语言的语法检查插件 可以结合vim语法检查插件syntastic使用,具体请参考syntastic使用说明 如php,sql,json,css,js,html,shell,c等语法插件 ...

  9. MySQL递归查询_函数语法检查_GROUP_CONCAT组合结果集的使用

    1-前言: 在Mysql使用递归查询是很不方便的,不像Sqlserver可以直接使用声明变量,使用虚拟表等等.如:DECLARE,BEGIN ...  END   ,WHILE ,IF 等等. 在My ...

随机推荐

  1. (原创)Windows下使用android ADT工具dmtracedump.exe绘图

    在windows下使用dmtracedump绘图时,出现如下错误: 'dot' 不是内部或外部命令,也不是可运行的程序 或批处理文件. 应该是没有dot这个执行程序,安装:Graphviz程序,然后将 ...

  2. php 建立 搜索 分词树

    <?php /** * @author: xiaojiang 20140107 * php 建立分词树 * */ class Tree{ public $w = ''; public $subT ...

  3. php安装xdebug后var_dump输出没有格式化的问题

    开发环境,装了一个xdebug扩展方便调试代码. 但是环境配置好了之后却发现xdebug加载成功了但是var_dump输出的内容却没有使用html格式化 这时想到估计是php.ini里面的某个输出的配 ...

  4. CSS-禁用a标签

    <style> a.disabled { pointer-events: none; filter: alpha(opacity=50); /*IE滤镜,透明度50%*/ -moz-opa ...

  5. Effective Java (6) - 消除过期的对象引用

    一.引言 很多人可能在想这么一个问题:Java有垃圾回收机制,那么还存在内存泄露吗?答案是肯定的,所谓的垃圾回收GC会自动管理内存的回收,而不需要程序员每次都手动释放内存,但是如果存在大量的临时对象在 ...

  6. SQL Server2008安装后1433端口没监听问题

    win2008系统安装完SQL Server2008后发现1433端口并没有监听,netstat -an并没有发现监听的1433端口,本机telnet localhost 1433也连不通,百度之后说 ...

  7. 转载:C/C++关于string.h头文件和string类

    学习C语言时,用字符串的函数例如stpcpy().strcat().strcmp()等,要包含头文件string.h 学习C++后,C++有字符串的标准类string,string类也有很多方法,用s ...

  8. SharpGL学习笔记(七) OpenGL的变换总结

    笔者接触OpenGL最大的困难是: 经常调试一份代码时, 屏幕漆黑一片, 也不知道结果对不对,不知道如何是好! 这其实就是关于OpenGL"变换"的基础概念没有掌握好, 以至于对& ...

  9. call()、apply()和bind()的异同

    相同点: 改变this的指向: var a = { name:"丸子", fn:function(){ console.log(this.name); } } var b = a. ...

  10. Nginx写IO占用高故障处理

    文章来源:<https://www.centos.bz/2015/04/handle-nginx-write-io-problem/> 故障现象 突然收到一台服务器负载过高告警,紧接着网站 ...