golang使用vet进行语法检查
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 vet和go 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进行语法检查的更多相关文章
- Golang 基础之基础语法梳理 (二)
大家好,今天将梳理出的 Go语言基础语法内容,分享给大家. 请多多指教,谢谢. 本次<Go语言基础语法内容>共分为三个章节,本文为第二章节 Golang 基础之基础语法梳理 (一) Gol ...
- 如何在Texstudio内加载语法检查词典?
如何在Texstudio编辑软件内加载"语法检查词典"? How to make dictionary work in TexStudio I am using TexStudio ...
- emacs配置eslint 语法检查.找不到node解决
使用emacs配置eslint 当调用语法检查时报错 Suspicious state from syntax checker javascript-eslint: Checker javascrip ...
- [No000013]在Office中关闭自动拼写检查和自动语法检查
大家知道有时候语法检查很麻烦,搞得文档里都是红线和绿线.解决办法就是关闭自动拼写检查.现在我们来介绍怎么关闭office包括Word .Outlook .PowerPoint .OneNote .Pu ...
- 一个简单的C语言语法检查器的实现
我自己的实现方法的核心过程:首先用一个非终结符代表所有要检查的程序代码,然后根据文法将这个整体的符号不断展开,以拼凑成按检查的程序的顺序排列的终结符序列,能成功说明语法正确,否则有错误. 关键词:分词 ...
- Grunt 之 使用 JavaScript 语法检查工具 jshint
前端开发环境准备好了,我们准备开始进行开发. 前端开发的主力语言是 JavaScript,这是一种脚本语言,没有编译器,也就没有了编译器带给我们的语法检查,怎样保证代码的质量呢?jshint 是一个强 ...
- VIM 语法检查
VIM Grammar Check 一.Language Tool Create by Dominique Pellé REFER:LanguageTool wikipedia REFER:Langu ...
- linux下常用语言的语法检查插件整理
linux下常用语言的语法检查插件 可以结合vim语法检查插件syntastic使用,具体请参考syntastic使用说明 如php,sql,json,css,js,html,shell,c等语法插件 ...
- MySQL递归查询_函数语法检查_GROUP_CONCAT组合结果集的使用
1-前言: 在Mysql使用递归查询是很不方便的,不像Sqlserver可以直接使用声明变量,使用虚拟表等等.如:DECLARE,BEGIN ... END ,WHILE ,IF 等等. 在My ...
随机推荐
- 安装Alcatraz来管理Xcode插件的步骤和注意点
简介: Alcatraz 是一个帮你管理 Xcode 插件.模版以及颜色配置的工具.它可以直接集成到 Xcode 的图形界面中,让你感觉就像在使用 Xcode 自带的功能一样 下载和管理插件步骤 一 ...
- Puppet部署
一.域名,IP规划 域名:beyond.com puppet master:puppet.sa.beyond.com 192.168.254.254 puppet client: *.beyond ...
- .NET二级域名共享Session
ASP.NET二级域名站点共享Session状态 今天, 我要写的是如何在二级域名站点之间,主站点和二级域名站点之间共享Session. 首先, Session要共享,站点之间SessionID必须要 ...
- 纯css制作带三角(兼容所有浏览器)
如何用 border 来制作三角. html 代码如下: 代码如下: <div class="arrow-up"></div> <div class= ...
- 【基础】java类的各种成员初始化顺序
父子类继承时的静态代码块,普通代码块,静态方法,构造方法,等先后顺序 前言: 普通代码块:在方法或语句中出现的{}就称为普通代码块.普通代码块和一般的语句执行顺序由他们在代码中出现的次序决定--“先出 ...
- 【数据库系列】MySql中的select的锁表范围
由于InnoDB预设的是Row-Level Lock,只有明确指定主键的时候MySql才会执行Row lock,否则MySql将会执行Table Lock. 1.明确指定主键则是行锁 2.明确指定主键 ...
- 让A超链接无效的办法 阻止元素发生默认的行为
$("a").click(function(event){ event.preventDefault(); }); event.preventDefault(); 方法阻止元素发生 ...
- HashRouter与BrowserRouter的异同
项目中控制路由跳转使用的是BrowserRouter 在开发过程中使用是没有问题的,但是将页面上传至服务器之后,问题就来了:用户访问的资源不存在,页面是空白的. 原因: 在browserHistory ...
- 中国标准时间、‘yyyy-MM-dd’格式时间转为时间戳
中国标准时间转为时间戳 let _time="Tue Mar 20 2018 00:00:00 GMT+0800 (中国标准时间)"; console.log(Date.parse ...
- [转]redhat7(centos7) not registered to Red Hat Subscription Management
[root@controller0 ~]# yum install ntp Loaded plugins: fastestmirror, product-id, search-disabled-rep ...