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