golangci-lint个人理解
Go 语言自带套件为我们提供了静态代码分析工具 vet,它能用于检查 go 项目中可以通过编译但仍可能存在错误的代码。
在维基百科是如下定义 lint 的:
在计算机科学中,lint 是一种工具程序的名称,它用来标记源代码中,某些可疑的、不具结构性(可能造成 bug)的段落。它是一种静态程序分析工具,最早适用于 C 语言,在 UNIX 平台上开发出来。后来它成为通用术语,可用于描述在任何一种计算机程序语言中,用来标记源代码中有疑义段落的工具。
但是如果想要更多,就可以用golangci-lint。golangci-lint 是一个 linter 的集成框架。它集成了非常多的 linter,包括了上文提到的vet,合理使用它可以帮助我们更全面地分析与检查 Go 代码。
VScode安装go插件
安装golangci-lint
macOS
使用 brew:
brew install golangci-lint
brew upgrade golangci-lint
Linux and Windows
# binary will be $(go env GOPATH)/bin/golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.46.2
golangci-lint --version
其他版本看官方文档: https://golangci-lint.run/usage/install/
VScode配置golangci-lint
https://golangci-lint.run/usage/integrations/#editor-integration
通过设置目录配置
通过json文件配置
打开VScode设置配置,添加如下内容
"go.lintTool":"golangci-lint",
"go.lintFlags": [
"--fast"
]
Using it in an editor without --fast
can freeze your editor. Golangci-lint automatically discovers .golangci.yml
config for edited file: you don’t need to configure it in VS Code settings.
- 如果不使用
--fast
,可能会导致你的VScode卡死 (当然如果你恰巧财力雄厚,电脑配置极高(大机),请忽略) - 会自动检测.golangci.yml(见下文)
添加配置前
添加配置后
配置后会有自动代码lint提示
添加.golangci.yml配置(可选,但是推荐,是精华)
深入有精髓,本文是简单使用
.golangci.yml 配置可以看golangci-lint官方网站的配置
一个可用的实践配置
linters:
disable-all: true # 关闭其他linter
enable: # 下面是开启的linter列表,之后的英文注释介绍了相应linter的功能
- deadcode # 找出未使用的代码
# - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
- gosimple # Linter for Go source code that specializes in simplifying a code
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
- ineffassign # Detects when assignments to existing variables are not used
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
- structcheck # Finds unused struct fields
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
- unused # Checks Go code for unused constants, variables, functions and types
- varcheck # Finds unused global variables and constants
- scopelint # Scopelint checks for unpinned variables in go programs
# - golint # Carry out the stylistic conventions put forth in Effective Go and CodeReviewComments
linters-settings:
govet: # 对于linter govet,这里手动开启了它的某些扫描规则
check-shadowing: true
check-unreachable: true
check-rangeloops: true
check-copylocks: true
主动使用golangci-lint
在前面VScode配置golangci-lint之后就已经有代码lint提示了(见上文),但是这种方式有些缺陷(可以见后文的缺陷)
- 可能不能立马展示出结果,可能需要等待其在后台运行一段时间
- 虽然能够自动检测项目中的
.golangci.yml
文件,但是可能对于整个项目文件的检测,会导致检测提前中断在某个错误上,然后不继续检测了。比如: 项目中多个文件,然后多个main函数,导致每个文件中main重定义错误先报错,后面main里面的错误就不检测了…
所以这里展示一下主动的使用,比如我们有一些其他的针对单个文件的场景
在项目根目录下运行golangci-lint run
,该命令会加载.golangci.yml
(如果有,没有则用默认的检测规则),执行lint
缺陷(可选)
项目中多main,导致main后内容不检测
虽然能够自动检测项目中的.golangci.yml
文件,但是可能对于整个项目文件的检测,会导致检测提前中断在某个错误上,然后不继续检测了。比如: 项目中多个文件,然后多个main函数,导致每个文件中main重定义错误先报错,后面main里面的错误就不检测了…
修改前
修改后:
配置探索
nil检测
又研究一小时,终于找到了nilness集成到golangci-lint方法,但是还是有很多不足,有些nil还是检测不出来,不过这是静态检测已经尽力做到最好了
可以看linters-settings配置
linters:
disable-all: false # 关闭其他linter
enable: # 下面是开启的linter列表,之后的英文注释介绍了相应linter的功能
- deadcode # 找出未使用的代码
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
- gosimple # Linter for Go source code that specializes in simplifying a code
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
- ineffassign # Detects when assignments to existing variables are not used
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
- structcheck # Finds unused struct fields
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
- unused # Checks Go code for unused constants, variables, functions and types
- varcheck # Finds unused global variables and constants
- scopelint # Scopelint checks for unpinned variables in go programs
- golint # Carry out the stylistic conventions put forth in Effective Go and CodeReviewComments
linters-settings:
govet: # 对于linter govet,这里手动开启了它的某些扫描规则
check-shadowing: true
check-unreachable: true
check-rangeloops: true
check-copylocks: true
# 启动nilness检测
enable:
- nilness
效果如下:
更深入的nil探测
吐血了,又研究了接近4小时,发现只有很明显的nil解引用的错误能够检查出来,其他的nil调用的错误都检查不出来(这是lint工具目前做不到的东西),听说goland能检查,尽管会有误报,但个人比不报好,当然,良好的单元测试来检测这些panic会更有效
package main
import (
"fmt"
"reflect"
)
type Worker interface {
String() string
}
type Person struct {
Age uint8
Name string
Sex string
}
type Employee struct {
Person
}
type Employer struct {
Title string
Person
}
func (p *Person) String() string {
return fmt.Sprintf("Name: %s, Sex: %s, Age: %d", p.Name, p.Sex, p.Age)
}
func (er *Employer) String() string {
return fmt.Sprintf("%s, Title: %s", er.Person.String(), er.Title)
}
func main() {
test()
}
func test() {
var (
w Worker
emptyPoint *Employee
)
w = &Employer{
Title: "Title",
Person: Person{
Name: "Tom",
Age: 50,
Sex: "male",
},
}
if IsNil(w) {
fmt.Printf("interface w is nil")
return
}
// **bad**
e := w.(*Employee)
fmt.Println(e.String())
// ok
if e, ok := w.(*Employee); ok {
fmt.Printf("It's Employee, %s\n", e.String())
} else if e, ok := w.(*Employer); ok {
fmt.Printf("It's Employer, %s\n", e.String())
}
// **bad**
fmt.Println(emptyPoint.String())
}
func IsNil(i interface{}) bool {
vi := reflect.ValueOf(i)
if vi.Kind() == reflect.Ptr {
return vi.IsNil()
}
return false
}
更多的功能(可选)
使用 pre-commit hook (可选)
因为每次都要进行golangci-lint run
命令检测很麻烦,
所以还是使用git hooks
集成比较方便,在提交代码前,确保自己的代码是符合规范的。
嵌入gitlab ci(可选)
不会,没有实践过,但是很多大佬实践过,有兴趣的可以自行学习
关于const常量命令golint争议(可选,代码规范个人看法)
使用C风格的ALL_CAPS的大写加下划线定义宏或者const常量在golint中报错,而测试发现不带下划线的又竟然可以,但是易读性也太差了,所以还是老老实实地遵守golint的标准,对于需要导出的常量,使用大写的驼峰,不需要导出的常量,使用小写的驼峰
我的猜想(主要是想说服自己):
之所以C和Python需要中大家都定义宏和常量为ALL_CAPS全大写加下划线,是因为其中没有及时的静态检查,所以不方便知道这是常量,但是go中比较容易知道是常量…所以通过命名来约定
而golang则为了其他方面保持简单统一,重点是大写就一定导出了,就没有对这个常量这里约定为ALL_CAPS格式
也是可以的,是一个约定而已,其实习惯了也感觉还是挺舒服的
后面浏览其他文档,发现以前VScode默认使用golint,不过后面已经确认把 golint冻结、废弃:issue 38968,前面也能看到默认的是staticcheck
所以不用遵守这个也可以,但是遵守一下比较好