📚
handbook
  • Introduction
  • 1.前言
    • 前言
    • 鸣谢
  • 2.环境篇
    • 工具部署和使用
      • 团队协作工具
        • Confluence
      • 开发工具
        • Docker
          • 镜像导入导出
          • 安装
        • Docker Compose
      • 持续集成工具
        • Gerrit
        • Sonarqube
          • 分析参数设定
          • Prerequisite
          • 服务端设置
        • Ubuntu Ci Deploy
          • ubuntu使用docker部署jenkins+sonarqube
        • 持续集成部署
      • 文本编辑工具
        • Gitbook相关注意事项
        • Markdown快速入门
      • 版本控制
        • Git
          • 1.基础
            • Git基础(一)
            • Git基础(二)
            • Git基础(三)
            • Git基础(四)
            • Git基础(五)
          • 2.命令详解
            • 命令速查
          • 3.进阶技巧
            • git技巧
      • 自动化测试工具
        • Appium
          • capability参数配置
          • 安装
          • 简介
      • 项目管理工具
        • Jira
    • 开发环境配置
      • 通用
        • Homebrew安装与使用
        • Git服务器添加SSH Key
        • koroFileHeader使用
        • nodejs与npm的安装
        • npm更换国内源
        • pip使用相关
        • PostgreSQL安装与使用
        • proxychain安装与使用
        • shell配置环境变量
        • snapd安装与使用
        • terminal走代理
    • 快捷键速查
      • shell常用快捷键
  • 3.语言篇
    • C
      • 代码规范
      • 语言技巧
    • Cpp
      • 代码规范
      • 基础知识
        • 理解C++中的左值和右值
      • 语言技巧
        • 并发编程
          • 简单的线程池实现
    • Golang
      • 代码规范
        • 避免使用转义字符串
        • 避免参数语义不明确
        • 嵌套式结构体
        • 函数的分组与顺序
        • 函数命名
        • 声明一致性
        • 导入别名
        • 使用字段名初始化结构体
        • 本地变量声明
        • map初始化
        • nil用法
        • 包命名
        • 命名Printf样式的函数
        • 减少嵌套
        • 缩小变量作用域
        • struct引用初始化
        • 测试表声明
        • 顶层变量声明
        • 不必要的else
      • 环境配置
        • 代码检查格式化工具
          • Go Fmt
          • Goimports
          • Golint
          • Go Vet
        • go mod详解
        • golang安装
        • Golang开发环境
        • Troubleshooting
      • 语言技巧
        • 如何分包
    • Java
      • 代码规范
      • 语言技巧
        • 注解编程
        • 动态代理
    • Js
      • 语言技巧
        • Rollup
    • Kotlin
      • 基础知识
        • 写给开发者Kotlin指引(一)
        • 写给开发者Kotlin指引(二)
    • Python
      • 语言技巧
        • Best Practice Of Python S Project Structure
  • 4.规范篇
    • Git message规范
  • 5.技术篇
    • Android技术
      • Hook
        • EdXposed例子
        • Android 10 上安装Magisk和EdXposed
      • Tinker
        • 1.Tinker及其使用
      • 准备
        • ADB连接设备步骤及注意事项
        • adb连接设备
        • aosp编译
      • 基础
        • Binder接口调用的鉴权方法
        • Make 及 Android 编译系统介绍
        • 使用Content Provider为其他应用提供数据
      • 源码阅读
        • Framework源码分析 Looper Handler
        • Framework源码分析 启动流程 ServiceManager的初始化
        • Framework源码分析 启动流程 Zygote启动SystemServer
    • JS Bridge
      • JSBridge初探
    • Kernel技术
      • kallsyms子系统
    • Test技术
      • 软件测试
        • jnekin+sonar 部署 问题总结
        • 性能测试基础
        • 软件测试的背景
        • 测试基础
        • 测试人员的核心竞争力
    • 操作系统原理
      • 处理器如何实现原子操作
Powered by GitBook
On this page
  • 一、用法
  • 二、Goland集成
  • 三、简介
  • 3.1 Print-format错误
  • 3.2 Boolean错误
  • 3.3 Range循环
  • 3.4 Unreachable的代码
  • 3.5 混杂的错误
  • 3.6 误报和漏报
  • 3.7 性能

Was this helpful?

  1. 3.语言篇
  2. Golang
  3. 环境配置
  4. 代码检查格式化工具

Go Vet

go vet用于检查代码中的错误,例如printf的参数错误等。

一、用法

go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]

一般使用

go vet /path/to/file

二、Goland集成

可以使用Tools->Go Tools->Go Vet File。

三、简介

3.1 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!),不够友好。现在,我们开始运行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 := 42
    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没有任何输出。

3.2 Boolean错误

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

package main

import "fmt"

func main() {
    var i int

    // always true
    fmt.Println(i != 0 || i != 1)    // always false
    fmt.Println(i == 0 && i == 1)    // redundant check
    fmt.Println(i == 0 && i == 0)
}
$ 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。大多数情况下是由于排版错误引起的。

3.3 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

3.4 Unreachable的代码

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

package main

import "fmt"

func add(a int, b int) int {
    return a + b
    fmt.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 1
        case 1:        return 1
        default:       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 code
unreachable.go:19: unreachable code
unreachable.go:33: unreachable code

3.5 混杂的错误

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

package main
import (
    "fmt"
    "log"
    "net/http"
)

func f() {}
func main() {    // Self assignment
    i := 42
    i = i    // a declared function cannot be nil
    fmt.Println(f == nil)    // shift too long
    fmt.Println(i >> 32)    // 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

3.6 误报和漏报

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

package main

import "fmt"

func main() {
    rate := 42

    // this condition can never be true
    if 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提示很少会有误报与漏报的情况。

3.7 性能

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

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

PreviousGolintNextgo mod详解

Last updated 4 years ago

Was this helpful?