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 ...
随机推荐
- Struts2(一)基本配置
一.Struts2概述 1.什么是Struts2? Struts2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样使得业务逻辑控制器能够和ServletAPI脱离开来. 2.工作原理 当 ...
- 删除SQL注入的一些方法总结
sql替换法: ); set @myStr='oa_20121026new.bak</title><style>.alx2{position:absolute;clip:rec ...
- URL域名获取
http://"是协议名 "www.test.com"是域名 "是端口号 "aaa"是站点名 "bbb.aspx"是页面 ...
- 题目1029:魔咒词典(map使用以及字符串读取函数总结)
题目链接:http://ac.jobdu.com/problem.php?pid=1029 详解链接:https://github.com/zpfbuaa/JobduInCPlusPlus // // ...
- Ant的Manifest任务
建立一个清单文件,他将放入某个jar,作为jar文件的说明书.其中,在清单文件可以指定jar文件的main-class,jar文件将可以直接运行.例子: <manifest > < ...
- dhroid - ioc高级(接口,对象注入)
下面到了接口对象的注入了解冻吧,现在才是我们的重点,这才是ioc的核心思想,上面的都是android的辅助1.5 对象依赖问题 我们先来将一下对象对象依赖的重要性,很多同学可能只学了android没学 ...
- wpgcms---详情页面数据怎么渲染
wpgcms的详情页面的数据会被保存在 contentInfo 这么一个字段里面. 面包屑导航调用: <p>当前位置 {% for c in crumb|slice(1, crumb|le ...
- thinkCMF----公共模板的引入
这个主要用于前台模板的 头部和底部分离: 具体引入方法: <include file="public@source"/> <include file=" ...
- Spark2 Linear Regression线性回归
回归正则化方法(Lasso,Ridge和ElasticNet)在高维和数据集变量之间多重共线性情况下运行良好. 数学上,ElasticNet被定义为L1和L2正则化项的凸组合: 通过适当设置α,Ela ...
- [ZT] 医学图像分析相关的会议
原文地址:http://blog.sina.com.cn/s/blog_ad7c19000102v42d.html 一. 图形学.可视化领域的会议: (一)高级别会议 1. Siggraph (图形 ...