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

转自:http://studygolang.com/articles/9619

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 mainimport "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 mainimport "fmt"

func customLogf(str string, args ...interface{}) {fmt.Printf(str, args...)
}func main() {i := 42customLogf("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 mainimport "fmt"

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

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

Range 循环

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

package mainimport "fmt"

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

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

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

Unreachable的代码

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

package mainimport "fmt"

func add(a int, b int) int {        return a + bfmt.Println("unreachable")    return 0}

func div(a int, b int) int {        if b == 0 {panic("division by 0")} else {                return a / b}fmt.Println("unreachable")        return 0}

func fibonnaci(n int) int {       switch n {            case 0:        return 1case 1:        return 1default:       return fibonnaci(n-1) + fibonnaci(n-2)}fmt.Println("unreachable")        return 0}

func main() {fmt.Println(add(1, 2))fmt.Println(div(10, 2))fmt.Println(fibonnaci(5))
}
$ go vet unreachable.go

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

混杂的错误

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

package mainimport (    "fmt""log""net/http")

func f() {}func main() {    // Self assignmenti := 42i = i    // a declared function cannot be nilfmt.Println(f == nil)    // shift too longfmt.Println(i >> 32)    // res used before checking errres, 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 imisc.go:17: comparison of function f == nil is always false
misc.go:20: i might be too small for shift of 32misc.go:24: using res before checking for errors

误报和漏报

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

package main

import "fmt"

func main() {rate := 42// this condition can never be trueif rate > 60 && rate < 40 {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项目中定期使用。它是那么有价值,以至于它甚至可以让我们怀疑是不是有些检测不应该被编译器检测到。

go tool vet是你的好朋友相关推荐

  1. Golang 要注意的陷阱和常见错误

    原文: 50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs 翻译: Go的50度灰:新Golang开发者要 ...

  2. Go语言学习笔记(一)Let's 干吧

    加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 简介     Go是Google开发的一种 静态强类型.编译型,并发型,并具有垃圾回收功能的编程语言.为了方便搜索 ...

  3. golang 开发常见坑

    目录 初级 开大括号不能放在单独的一行 未使用的变量 未使用的Imports 简式的变量声明仅可以在函数内部使用 使用简式声明重复声明变量 不能使用短变量声明来设置字段值 Can't Use Shor ...

  4. gometalinter代码检查

    简介 gometalinter工具可以检查Go代码中一些隐蔽的错误.go的编译器有的时候仅仅是能检查语法层面的错误,而不能检测出逻辑的错误.所以就需要一些额外的错误检查工具来进行代码检测. 该工具基本 ...

  5. Golang新开发者要注意的陷阱和常见错误

    原文: 50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs   翻译: Go的50度灰:新Golang开发 ...

  6. 启动服务错误5拒绝访问_【Go API 开发实战 5】基础1:启动一个最简单的 RESTful API 服务器...

    启动一个最简单的 RESTful API 服务器 本节核心内容 启动一个最简单的 RESTful API 服务器 设置 HTTP Header API 服务器健康检查和状态查询 编译并测试 API 本 ...

  7. go语言实现第一个程序-hello,world!

    0.前言 工作中一直使用c++编写高并发服务器程序,但c++编写高并非服务器程序时多线程逻辑,锁机制导致的死锁,内存泄漏,崩溃等问题会耗费大量时间和精力. 听同事说go语言是专门做高并发编程的,不用考 ...

  8. Golang精编100题

    能力模型 级别 模型 初级 primary 熟悉基本语法,能够看懂代码的意图: 在他人指导下能够完成用户故事的开发,编写的代码符合CleanCode规范: 中级 intermediate 能够独立完成 ...

  9. windows下《Go Web编程》之Go命令

    Go命令: go build 用于编译代码,默认会编译当前目录下的所以go文件.若只需编译某个文件,go build后加上文件名,如go build a.go. go build会忽略目录下以&quo ...

  10. 送给水深火热的 Gopher 们的解药

    看了看日历,现在已经是 2021 年了,偶尔还是能看到有人在发诸如 <http body 未关闭导致线上事故>,或者 <sql.Rows 未关闭半夜惊魂>类的文章,令人有一种梦 ...

最新文章

  1. cad里面f命令用不了,CAD出现命令无效、失灵等问题?不用慌,两招帮你快速解决...
  2. 在Windows上部署NTP Server
  3. IDEA Java Web 推送Tomcat
  4. 远程客户端连接MysqL数据库太慢解决方案
  5. 2020 年程序员高考试卷来了!
  6. 甲骨文提供免费HR工具,助力客户保障员工安全
  7. asp.net网站的配置文件
  8. Swift 2.2 最基本的多线程
  9. 【swupdate文档 五】从可信的来源更新镜像
  10. swift 引用其他类_浅谈swift中闭包修饰符 weak?unowned? 或什么都不用
  11. 安装Ubuntu系统
  12. html5 随机抽奖,jQuery+H5按空格键随机抽奖代码
  13. C语言网络编程:recv函数详解
  14. c 抓取百度页面html,搜索引擎百度蜘蛛详解,百度蜘蛛IP分析
  15. 上山的路上,总得给自己一点阳光。
  16. 全国卖菜大爷将大面积失业,社区团购夺走卖菜商贩生计,真的会出问题吗
  17. Apache Kafka的流式SQL引擎——KSQL
  18. java 中 print、println 与 printf 的区别
  19. Python打印空心等腰三角形
  20. 分享13道Redis面试题,助你面试不再慌

热门文章

  1. 用单片机c语言输入8位输出,单片机C语言教程(二)
  2. FPGA与数字信号处理
  3. 爱企人事工资管理系统 v8.1 免费下载
  4. 【apue】UNIX环境高级编程 超详细介绍
  5. u大师装iso系统linux,【iso怎么用u盘装系统】iso镜像怎么用u盘装_iso用u盘装系统-系统城...
  6. 服务器tcpip修复工具,tcpip协议修复工具winsockfix
  7. linux chmod详解
  8. 数据结构--严蔚敏(C语言版)笔记
  9. 单链表的实现 (C语言版 数据结构 严蔚敏)
  10. 优酷土豆并购是如虎添翼