go vet 也是官方提供的静态分析工具,其内置了锁拷贝检查、循环变量捕获问题、printf 参数不匹配等工具。
比如新手老手都很容易犯的 loop capture 错误:
package main
func main() {
var a = map[int]int {1 : 1, 2: 3}
var b = map[int]*int{}
for k, r := range a {
go func() {
b[k] = &r
}()
}
}
go vet 会直接把你骂醒:
~/test git:master ❯❯❯ go vet ./clo.go
# command-line-arguments
./clo.go:8:6: loop variable k captured by func literal
./clo.go:8:12: loop variable r captured by func literal
执行 go tool vet help 可以看到 go vet 已经内置的一些 linter。
~ ❯❯❯ go tool vet help
vet is a tool for static analysis of Go programs.
vet examines Go source code and reports suspicious constructs,
such as Printf calls whose arguments do not align with the format
string. It uses heuristics that do not guarantee all reports are
genuine problems, but it can find errors not caught by the compilers.
Registered analyzers:
asmdecl report mismatches between assembly files and Go declarations
assign check for useless assignments
atomic check for common mistakes using the sync/atomic package
bools check for common mistakes involving boolean operators
buildtag check that +build tags are well-formed and correctly located
cgocall detect some violations of the cgo pointer passing rules
composites check for unkeyed composite literals
copylocks check for locks erroneously passed by value
errorsas report passing non-pointer or non-error values to errors.As
httpresponse check for mistakes using HTTP responses
loopclosure check references to loop variables from within nested functions
lostcancel check cancel func returned by context.WithCancel is called
nilfunc check for useless comparisons between functions and nil
printf check consistency of Printf format strings and arguments
shift check for shifts that equal or exceed the width of the integer
stdmethods check signature of methods of well-known interfaces
structtag check that struct field tags conform to reflect.StructTag.Get
tests check for common mistaken usages of tests and examples
unmarshal report passing non-pointer or non-interface values to unmarshal
unreachable check for unreachable code
unsafeptr check for invalid conversions of uintptr to unsafe.Pointer
unusedresult check for unused results of calls to some functions
默认情况下这些 linter 都是会跑的,当前很多 IDE 在代码修改时会自动执行 go vet,所以我们在写代码的时候一般就能发现这些错了。
data, err := getDataFromRPC()
if err != nil {
return nil, err
}
// do business logic
age := data.age
而自信的程序员可能会写成这样:
data, _ := getDataFromRPC()
// do business logic
age := data.age
如果底层 RPC 逻辑出错,上层的 data 是个空指针也是很正常的,如果底层函数返回的 err 非空时,我们不应该对其它字段做任何的假设。这里 data 完全有可能是个空指针,造成用户程序 panic。
errcheck 会强制我们在代码中检查并处理 err。
gocyclo
gocyclo 主要用来检查函数的圈复杂度。圈复杂度可以参考下面的定义:
圈复杂度(Cyclomatic complexity)是一种代码复杂度的衡量标准,在 1976 年由 Thomas J. McCabe, Sr. 提出。在软件测试的概念里,圈复杂度用来衡量一个模块判定结构的复杂程度,数量上表现为线性无关的路径条数,即合理的预防错误所需测试的最少路径条数。圈复杂度大说明程序代码可能质量低且难于测试和维护,根据经验,程序的可能错误和高的圈复杂度有着很大关系。
~/g/s/g/c/elasticsql git:master ❯❯❯ golangci-lint run .
main.go:36:9: S1034: assigning the result of this type assertion to a variable (switch stmt := stmt.(type)) could eliminate type assertions in switch cases (gosimple)
switch stmt.(type) {
^
main.go:38:34: S1034(related information): could eliminate this type assertion (gosimple)
dsl, table, err = handleSelect(stmt.(*sqlparser.Select))
^
main.go:40:23: S1034(related information): could eliminate this type assertion (gosimple)
return handleUpdate(stmt.(*sqlparser.Update))
^
main.go:42:23: S1034(related information): could eliminate this type assertion (gosimple)
return handleInsert(stmt.(*sqlparser.Insert))
^
select_handler.go:192:9: S1034: assigning the result of this type assertion to a variable (switch expr := expr.(type)) could eliminate type assertions in switch cases (gosimple)
switch expr.(type) {
The correct answer to this was actually different. Before git runs the aliases it checks the $PATH. In case the directory does not exist, or lacks permissions, git produces the "fatal: cannot exec 'git-co': Permission denied". It does not ever comes to check the aliases so git foobar will produce the same error.
Good people from the git mailing list also reminded me of an strace tool, that can help finding the entry that is returning EACCES, as in: strace -f -e execve git foobar