16 代码检查:如何进行静态代码检查? 你好,我是孔令飞。上一讲中,我在讲代码开发的具体步骤时,提到了静态代码检查,今天我就来详细讲讲如何执行静态代码检查。
在做Go项目开发的过程中,我们肯定需要对Go代码做静态代码检查。虽然Go命令提供了go vet和go tool vet,但是它们检查的内容还不够全面,我们需要一种更加强大的静态代码检查工具。
其实,Go生态中有很多这样的工具,也不乏一些比较优秀的。今天我想给你介绍的golangci-lint,是目前使用最多,也最受欢迎的静态代码检查工具,我们的IAM实战项目也用到了它。
接下来,我就从golangci-lint的优点、golangci-lint提供的命令和选项、golangci-lint的配置这三个方面来向你介绍下它。在你了解这些基础知识后,我会带着你使用golangci-lint进行静态代码检查,让你熟悉操作,在这个基础上,再把我使用golangci-lint时总结的一些经验技巧分享给你。
为什么选择golangci-lint做静态代码检查?
选择golangci-lint,是因为它具有其他静态代码检查工具不具备的一些优点。在我看来,它的核心优点至少有这些:
- 速度非常快:golangci-lint是基于gometalinter开发的,但是平均速度要比gometalinter快5倍。golangci-lint速度快的原因有三个:可以并行检查代码;可以复用go build缓存;会缓存分析结果。
- 可配置:支持YAML格式的配置文件,让检查更灵活,更可控。
- IDE集成:可以集成进多个主流的IDE,例如 VS Code、GNU Emacs、Sublime Text、Goland等。
- linter聚合器:1.41.1版本的golangci-lint集成了76个linter,不需要再单独安装这76个linter。并且golangci-lint还支持自定义linter。
- 最小的误报数:golangci-lint调整了所集成linter的默认设置,大幅度减少了误报。
- 良好的输出:输出的结果带有颜色、代码行号和linter标识,易于查看和定位。
下图是一个golangci-lint的检查结果:
你可以看到,输出的检查结果中包括如下信息:
- 检查出问题的源码文件、行号和错误行内容。
- 出问题的原因,也就是打印出不符合检查规则的原因。
- 报错的linter。
通过查看golangci-lint的输出结果,可以准确地定位到报错的位置,快速弄明白报错的原因,方便开发者修复。
除了上述优点之外,在我看来golangci-lint还有一个非常大的优点:当前更新迭代速度很快,不断有新的linter被集成到golangci-lint中。有这么全的linter为你的代码保驾护航,你在交付代码时肯定会更有自信。
目前,有很多公司/项目使用了golangci-lint工具作为静态代码检查工具,例如 Google、Facebook、Istio、Red Hat OpenShift等。
golangci-lint提供了哪些命令和选项?
在使用之前,首先需要安装golangci-lint。golangci-lint的安装方法也很简单,你只需要执行以下命令,就可以安装了。 $ go get github.com/golangci/golangci-lint/cmd/[email protected] $ golangci-lint version /# 输出 golangci-lint 版本号,说明安装成功 golangci-lint has version v1.39.0 built from (unknown, mod sum: “h1:aAUjdBxARwkGLd5PU0vKuym281f2rFOyqh3GB4nXcq8=”) on (unknown)
这里注意,为了避免安装失败,强烈建议你安装golangci-lint releases page中的指定版本,例如 v1.41.1。
另外,还建议你定期更新 golangci-lint 的版本,因为该项目正在被积极开发并不断改进。
安装之后,就可以使用了。我们可以通过执行
golangci-lint -h 查看其用法,golangci-lint支持的子命令见下表:
此外,golangci-lint还支持一些全局选项。全局选项是指适用于所有子命令的选项,golangci-lint支持的全局选项如下:
接下来,我就详细介绍下golangci-lint支持的核心子命令:run、cache、completion、config、linters。
run命令
run命令执行golangci-lint,对代码进行检查,是golangci-lint最为核心的一个命令。run没有子命令,但有很多选项。run命令的具体使用方法,我会在讲解如何执行静态代码检查的时候详细介绍。
cache命令
cache命令用来进行缓存控制,并打印缓存的信息。它包含两个子命令:
- clean用来清除cache,当我们觉得cache的内容异常,或者cache占用空间过大时,可以通过
golangci-lint cache clean 清除cache。
- status用来打印cache的状态,比如cache的存放目录和cache的大小,例如: $ golangci-lint cache status Dir: /home/colin/.cache/golangci-lint Size: 773.4KiB
completion命令
completion命令包含4个子命令bash、fish、powershell和zsh,分别用来输出bash、fish、powershell和zsh的自动补全脚本。
下面是一个配置bash自动补全的示例: $ golangci-lint completion bash > ~/.golangci-lint.bash $ echo “source ‘$HOME/.golangci-lint.bash’” » ~/.bashrc $ source ~/.bashrc
执行完上面的命令,键入如下命令,即可自动补全子命令:
$ golangci-lint comp
上面的命令行会自动补全为
golangci-lint completion 。
config命令
config命令可以打印golangci-lint当前使用的配置文件路径,例如: $ golangci-lint config path .golangci.yaml
linters命令
linters命令可以打印出golangci-lint所支持的linter,并将这些linter分成两类,分别是配置为启用的linter和配置为禁用的linter,例如: $ golangci-lint linters Enabled by your configuration linters: … deadcode: Finds unused code [fast: true, auto-fix: false] … Disabled by your configuration linters: exportloopref: checks for pointers to enclosing loop variables [fast: true, auto-fix: false] …
上面我介绍了golangci-lint提供的命令,接下来,我们再来看下golangci-lint的配置。
golangci-lint配置
和其他linter相比,golangci-lint一个非常大的优点是使用起来非常灵活,这要得益于它对自定义配置的支持。
golangci-lint支持两种配置方式,分别是命令行选项和配置文件。如果bool/string/int的选项同时在命令行选项和配置文件中被指定,命令行的选项就会覆盖配置文件中的选项。如果是slice类型的选项,则命令行和配置中的配置会进行合并。
golangci-lint run 支持很多命令行选项,可通过
golangci-lint run -h 查看,这里选择一些比较重要的选项进行介绍,见下表:
此外,我们还可以通过golangci-lint配置文件进行配置,默认的配置文件名为.golangci.yaml、.golangci.toml、.golangci.json,可以通过
-c 选项指定配置文件名。通过配置文件,可以实现下面几类功能:
- golangci-lint本身的一些选项,比如超时、并发,是否检查
/*_test.go 文件等。
- 配置需要忽略的文件和文件夹。
- 配置启用哪些linter,禁用哪些linter。
- 配置输出格式。
- golangci-lint支持很多linter,其中有些linter支持一些配置项,这些配置项可以在配置文件中配置。
- 配置符合指定正则规则的文件可以忽略的linter。
- 设置错误严重级别,像日志一样,检查错误也是有严重级别的。
更详细的配置内容,你可以参考Configuration。另外,你也可以参考IAM项目的golangci-lint配置.golangci.yaml。.golangci.yaml里面的一些配置,我建议你一定要设置,具体如下:
run: skip-dirs: /# 设置要忽略的目录 - util - ./~ - api/swagger/docs skip-files: /# 设置不需要检查的go源码文件,支持正则匹配,这里建议包括:_test.go - “./\.my\.go$” - _test.go linters-settings: errcheck: check-type-assertions: true /# 这里建议设置为true,如果确实不需要检查,可以写成num, _ := strconv.Atoi(numStr)
check-blank: false gci: /# 将以github.com/marmotedu/iam
开头的包放在第三方包后面 local-prefixes: github.com/marmotedu/iam godox: keywords: /# 建议设置为BUG、FIXME、OPTIMIZE、HACK - BUG - FIXME - OPTIMIZE - HACK goimports: /# 设置哪些包放在第三方包后面,可以设置多个包,逗号隔开 local-prefixes: github.com/marmotedu/iam gomoddirectives: /# 设置允许在go.mod中replace的包 replace-local: true replace-allow-list: - github.com/coreos/etcd - google.golang.org/grpc - github.com/marmotedu/api - github.com/marmotedu/component-base - github.com/marmotedu/marmotedu-sdk-go gomodguard: /# 下面是根据需要选择可以使用的包和版本,建议设置 allowed: modules: - gorm.io/gorm - gorm.io/driver/mysql - k8s.io/klog domains: /# List of allowed module domains - google.golang.org - gopkg.in - golang.org - github.com - go.uber.org blocked: modules: - github.com/pkg/errors: recommendations: - github.com/marmotedu/errors reason: “github.com/marmotedu/errors
is the log package used by marmotedu projects.” versions: - github.com/MakeNowJust/heredoc: version: “> 2.0.9” reason: “use the latest version” local_replace_directives: false lll: line-length: 240 /# 这里可以设置为240,240一般是够用的 importas: /# 设置包的alias,根据需要设置 jwt: github.com/appleboy/gin-jwt/v2 metav1: github.com/marmotedu/component-base/pkg/meta/v1
需要注意的是,golangci-lint不建议使用
enable-all: true 选项,为了尽可能使用最全的linters,我们可以使用以下配置:
linters: disable-all: true enable: /# enable下列出 <期望的所有linters> - typecheck - ...期望的所有linters>
<期望的所有linters> =更多学习
更多实时资讯,前沿技术,生活趣事。尽在【老马啸西风】
交流社群:[交流群信息](https://mp.weixin.qq.com/s/rkSvXxiiLGjl3S-ZOZCr0Q)