前言
团队协作开发中,必然存在着不同的代码风格,并且诸如 http body close
,unhandled error
等低级错误不能完全避免。通过使用 ci lint 能够及早的发现并修复问题,提高代码质量和幸福感。
目前主流的工具是 golangci-lint,里面集成了许多的 linters,安装及使用看官网文档即可,非常详细易读,本文主要是配合一些样例代码来分析一下相关 linters 的功能。
linters
deadcode
检查未使用的代码
var a = 100
func foo() {
println("hello,world")
}
main.go:3:5: `a` is unused (deadcode)
var a = 100
^
main.go:5:6: `foo` is unused (deadcode)
func foo() {
^
类似的工具还有 structcheck,varcheck
errcheck
检查未处理的错误
由于 Go 语言的错误处理机制,导致代码中遍地的 if err != nil
,因此有不耐烦的同学,在某些自认为不会出错的地方直接不处理,比如 json.Marshal()
,虽然多数情况下没啥问题,不过一但出了问题加班啥的估计跑不了。
可以通过修改配置文件来定制不同情况是否报告
linters-settings:
errcheck:
# 检查类型断言
check-type-assertions: true
# 检查使用 _ 来处理错误
check-blank: true
func main() {
foo()
var i interface{} = 1
ii := i.(int)
fmt.Println(ii)
num, _ := strconv.Atoi("110")
fmt.Println(num)
}
func foo() error {
return errors.New("i am error")
}
$ golangci-lint run
main.go:10:5: Error return value is not checked (errcheck)
foo()
^
main.go:13:8: Error return value is not checked (errcheck)
ii := i.(int)
^
main.go:16:7: Error return value of `strconv.Atoi` is not checked (errcheck)
num, _ := strconv.Atoi("110")
^
gosimple
简化代码
func main() {
t := time.Now()
fmt.Println(time.Now().Sub(t))
}
$ golangci-lint run
main.go:10:14: S1012: should use `time.Since` instead of `time.Now().Sub` (gosimple)
fmt.Println(time.Now().Sub(t))
^
在原始仓库中还有许多别的测试用例,感兴趣的同学可以看看,可以修改配置文件来指定生效的规则,默认是 all。
govet
go vet 是官方提供的工具,可以检查出许多问题,如 printf 参数不匹配、unmarshall 时未传递指针或者接口、循环变量捕获问题等。
type AAA struct {
A int `json:"a"`
}
func main() {
fmt.Printf("%s", true)
var a AAA
if err := json.Unmarshal([]byte(`{"a":1}`), a); err != nil {
panic(err)
}
var s []int
for i, v := range s {
go func() {
fmt.Println(i)
fmt.Println(v)
}()
}
}
$ golangci-lint run
main.go:23:16: loopclosure: loop variable i captured by func literal (govet)
fmt.Println(i)
^
main.go:24:16: loopclosure: loop variable v captured by func literal (govet)
fmt.Println(v)
^
main.go:13:2: printf: fmt.Printf format %s has arg true of wrong type bool (govet)
fmt.Printf("%s", true)
^
main.go:16:26: unmarshal: call of Unmarshal passes non-pointer as second argument (govet)
if err := json.Unmarshal([]byte(`{"a":1}`), a); err != nil {
^
ineffassign
检查无效的赋值,即变量赋值后并未使用
func main() {
a := os.Getenv("a")
if a == "" {
a = "unknown"
}
}
$ golangci-lint run
main.go:8:3: ineffectual assignment to a (ineffassign)
a = "unknown"
^
本以为这种情况应该不多,但是测试了一下我们组内的某个项目,发现还不少
请发表评论