16-代码检查:如何进行静态代码检查?

news2024/11/27 16:40:21

 

在做Go项目开发的过程中,我们肯定需要对Go代码做静态代码检查。虽然Go命令提供了go vet和go tool vet, 

 今天我想给你介绍的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还有一个非常大的优点:当前更新迭代速度很快,不断有新的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/golangci-lint@v1.41.1
$ 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 -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<TAB>

上面的命令行会自动补全为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支持两种配置方式,分别是命令行选项和配置文件。如果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> = <golangci-lint支持的所有linters> - <不期望执行的linters>,我们可以通过执行以下命令来获取:

$ ./scripts/print_enable_linters.sh
    - asciicheck
    - bodyclose
    - cyclop
    - deadcode
    - ...

将以上输出结果替换掉.golangci.yaml配置文件中的 linters.enable 部分即可。

 

如何使用golangci-lint进行静态代码检查?

要对代码进行静态检查,只需要执行 golangci-lint run 命令即可。接下来,我会先给你介绍5种常见的golangci-lint使用方法。

  1. 对当前目录及子目录下的所有Go文件进行静态代码检查:
$ golangci-lint run

命令等效于golangci-lint run ./...

  1. 对指定的Go文件或者指定目录下的Go文件进行静态代码检查:
$ golangci-lint run dir1 dir2/... dir3/file1.go

这里需要你注意:上述命令不会检查dir1下子目录的Go文件,如果想递归地检查一个目录,需要在目录后面追加/...,例如:dir2/...

  1. 根据指定配置文件,进行静态代码检查:
$ golangci-lint run -c .golangci.yaml ./...
  1. 运行指定的linter:

golangci-lint可以在不指定任何配置文件的情况下运行,这会运行默认启用的linter,你可以通过golangci-lint help linters查看它。

你可以传入参数-E/--enable来使某个linter可用,也可以使用-D/--disable参数来使某个linter不可用。下面的示例仅仅启用了errcheck linter:

$ golangci-lint run --no-config --disable-all -E errcheck ./...

这里你需要注意,默认情况下,golangci-lint会从当前目录一层层往上寻找配置文件名.golangci.yaml.golangci.toml.golangci.json直到根(/)目录。如果找到,就以找到的配置文件作为本次运行的配置文件,所以为了防止读取到未知的配置文件,可以用 --no-config 参数使golangci-lint不读取任何配置文件。

  1. 禁止运行指定的liner:

如果我们想禁用某些linter,可以使用-D选项。

$ golangci-lint run --no-config -D godot,errcheck

在使用golangci-lint进行代码检查时,可能会有很多误报。所谓的误报,其实是我们希望golangci-lint的一些linter能够容忍某些issue。那么如何尽可能减少误报呢?golangci-lint也提供了一些途径,我建议你使用下面这三种:

  • 在命令行中添加-e参数,或者在配置文件的issues.exclude部分设置要排除的检查错误。你也可以使用issues.exclude-rules来配置哪些文件忽略哪些linter。
  • 通过run.skip-dirsrun.skip-files或者issues.exclude-rules配置项,来忽略指定目录下的所有Go文件,或者指定的Go文件。
  • 通过在Go源码文件中添加//nolint注释,来忽略指定的代码行。

因为golangci-lint设置了很多linters,对于一个大型项目,启用所有的linter会检查出很多问题,并且每个项目对linter检查的粒度要求也不一样,所以glangci-lint使用nolint标记来开关某些检查项,不同位置的nolint标记效果也会不一样。接下来我想向你介绍nolint的几种用法。

  1. 忽略某一行所有linter的检查
var bad_name int //nolint
  1. 忽略某一行指定linter的检查,可以指定多个linter,用逗号 , 隔开。
var bad_name int //nolint:golint,unused
  1. 忽略某个代码块的检查。
//nolint
func allIssuesInThisFunctionAreExcluded() *string {
  // ...
}

//nolint:govet
var (
  a int
  b int
)
  1. 忽略某个文件的指定linter检查。

在package xx 上面一行添加//nolint注释。

//nolint:unparam
package pkg
...

在使用nolint的过程中,有3个地方需要你注意。

首先,如果启用了nolintlint,你就需要在//nolint后面添加nolint的原因// xxxx

其次,你使用的应该是//nolint而不是// nolint。因为根据Go的规范,需要程序读取的注释//后面不应该有空格。

最后,如果要忽略所有linter,可以用//nolint;如果要忽略某个指定的linter,可以用//nolint:<linter1>,<linter2>

golangci-lint使用技巧

我在使用golangci-lint时,总结了一些经验技巧,放在这里供你参考,希望能够帮助你更好地使用golangci-lint。

技巧1:第一次修改,可以按目录修改。

 

当然,如果错误太多,一时半会儿改不完,想以后慢慢修改或者干脆不修复存量的issues,那么你可以使用golangci-lint的 --new-from-rev 选项,只检查新增的code,例如:

$ golangci-lint run --new-from-rev=HEAD~1

技巧2:按文件修改,减少文件切换次数,提高修改效率。

如果有很多检查错误,涉及很多文件,建议先修改一个文件,这样就不用来回切换文件。可以通过grep过滤出某个文件的检查失败项,例如:

$ golangci-lint run ./...|grep pkg/storage/redis_cluster.go
pkg/storage/redis_cluster.go:16:2: "github.com/go-redis/redis/v7" imported but not used (typecheck)
pkg/storage/redis_cluster.go:82:28: undeclared name: `redis` (typecheck)
pkg/storage/redis_cluster.go:86:14: undeclared name: `redis` (typecheck)
...

技巧3:把linters-setting.lll.line-length设置得大一些。

这里建议将linters-setting.lll.line-length设置为120/240。

技巧4:尽可能多地使用golangci-lint提供的linter。

golangci-lint集成了很多linters,可以通过如下命令查看:

$ golangci-lint linters
Enabled by your configuration linters:
deadcode: Finds unused code [fast: true, auto-fix: false]
...
varcheck: Finds unused global variables and constants [fast: true, auto-fix: false]

Disabled by your configuration linters:
asciicheck: Simple linter to check that your code does not contain non-ASCII identifiers [fast: true, auto-fix: false]
...
wsl: Whitespace Linter - Forces you to use empty lines! [fast: true, auto-fix: false]

这些linter分为两类,一类是默认启用的,另一类是默认禁用的。每个linter都有两个属性:

  • fast:true/false,如果为true,说明该linter可以缓存类型信息,支持快速检查。因为第一次缓存了这些信息,所以后续的运行会非常快。
  • auto-fix:true/false,如果为true说明该linter支持自动修复发现的错误;如果为false说明不支持自动修复。

如果配置了golangci-lint配置文件,则可以通过命令golangci-lint help linters查看在当前配置下启用和禁用了哪些linter。golangci-lint也支持自定义linter插件,具体你可以参考:New linters。

在使用golangci-lint的时候,我们要尽可能多的使用linter。使用的linter越多,说明检查越严格,意味着代码越规范,质量越高。如果时间和精力允许,建议打开golangci-lint提供的所有linter。

技巧5:每次修改代码后,都要执行golangci-lint。

每次修改完代码后都要执行golangci-lint,一方面可以及时修改不规范的地方,另一方面可以减少错误堆积,减轻后面的修改压力。

技巧6:建议在根目录下放一个通用的golangci-lint配置文件。

/目录下存放通用的golangci-lint配置文件,可以让你不用为每一个项目都配置golangci-lint。当你需要为某个项目单独配置golangci-lint时,只需在该项目根目录下增加一个项目级别的golangci-lint配置文件即可。

总结

Go项目开发中,对代码进行静态代码检查是必要的操作。当前有很多优秀的静态代码检查工具,但golangci-lint因为具有检查速度快、可配置、少误报、内置了大量linter等优点,成为了目前最受欢迎的静态代码检查工具。

golangci-lint功能非常强大,支持诸如run、cache、completion、linters等命令。其中最常用的是run命令,run命令可以通过以下方式来进行静态代码检查:

$ golangci-lint run #  对当前目录及子目录下的所有Go文件进行静态代码检查
$ golangci-lint run dir1 dir2/... dir3/file1.go # 对指定的Go文件或者指定目录下的Go文件进行静态代码检查
$ golangci-lint run -c .golangci.yaml ./... # 根据指定配置文件,进行静态代码检查
$ golangci-lint run --no-config --disable-all -E errcheck ./... # 运行指定的 errcheck linter
$ golangci-lint run --no-config -D godot,errcheck # 禁止运行指定的godot,errcheck liner

此外,golangci-lint还支持 //nolint 、//nolint:golint,unused 等方式来减少误报。

课后练习

  1. 执行golangci-lint linters命令,查看golangci-lint支持哪些linter,以及这些linter的作用。
  2. 思考下,如何在golangci-lint中集成自定义的linter。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1560781.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构】——树和二叉树相关概念(全网超级详解)

创作不易&#xff0c;家人们来一波三连吧&#xff1f;&#xff01; 前言 世界上最大的树--雪曼将军树&#xff0c;这棵参天大树不是最长也不是最宽&#xff0c;是不是很奇怪&#xff0c;大只是他的体积是最大的&#xff0c;看图片肯定是感触不深&#xff0c;大家可以自己去看…

go入门到精通

初识Go语言 Go语言介绍 Go语言是什么 2009年11月10日&#xff0c;Go语言正式成为开源编程语言家庭的一员。 Go语言&#xff08;或称Golang&#xff09;是云计算时代的C语言。Go语言的诞生是为了让程序员有更高的生产效率&#xff0c;Go语言专门针对多处理器系统应用程序的编…

vue2项目安装(使用vue-cli脚手架)

使用npm安装 安装镜像&#xff08;使npm创建项目更快&#xff09;&#xff1a;镜像可更换 npm config set registry https://registry.npmmirror.com1.全局安装vue-cli&#xff08;一次&#xff09; npm install -g vue/cli 2. 查看vue-cli 版本 vue --version 3. 创建项目…

【Pytorch学习笔记(二)】张量的创建(补充)

一、知识回顾 我们在博客《张量的创建与访问》中已经讨论了一些张量的创建方法如torch.CharTensor()、torch.FloatTensor()以及torch.zeros()等张量创建方法&#xff0c;但由于其仅仅介绍了cpu版本torch下张量的创建方法和只有具体数据类型张量&#xff0c;本节内容旨在补充gp…

数字示波器

数字示波器 综述&#xff1a;本文讲述了数字示波器的电路组成。 一&#xff0e;定义 显示电信号波形的仪器 二&#xff0e;组成 由模拟前端处理电路、电源电路、单片机电路、控制电路、触发电路、校准电路组成。 1&#xff09;模拟前端处理电路 将输入的模拟信号处理后传…

2024中国医药企业项目管理大会将于7月在京召开

“创新是企业之魂”&#xff0c;对于医药企业来说药品创新研发能力很大程度上决定了公司核心竞争力和可持续发展能力。新药研发具有高投入、高成本、高风险、高收益、长周期等特点&#xff0c;从药物的发现研发到临床试验到获批生产上市销售是一个充满风险挑战的较为漫长历程&a…

判断点在多边形内的算法

在计算几何中&#xff0c;判定点是否在多边形内&#xff0c;是个非常有趣的问题。通常有两种方法&#xff1a; 一、Crossing Number&#xff08;交叉数&#xff09; 它计算从点P开始的射线穿过多边形边界的次数。当“交叉数”是偶数时&#xff0c;点在外面;当它是奇数时&…

基于8086毫秒数码管计时器仿真设计

**单片机设计介绍&#xff0c;基于8086毫秒数码管计时器仿真设计 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于8086毫秒数码管计时器仿真设计概要主要关注于利用8086微处理器实现毫秒级别的计时功能&#xff0c;并通过数码管显示时间…

action method

package cn.hello01;import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ActionSupport;public class UserAction extends ActionSupport{//增加public String save(){System.out.println("保存");return Action.SUCCESS;}//删除public String …

RESTful的优点

优点 1.通过url对资源定位&#xff0c;语义清晰&#xff1b; 2.通过HTTP谓词表示不同的操作&#xff0c;接口自描述&#xff1b; 3.可以对GET、PUT、DELETE请求重试&#xff08;幂等的&#xff09;&#xff1b; 4.可以对GET请求做缓存&#xff1b; 5.通过HTTP状态码反映服务器端…

SQLite3进行数据库各项常用操作

目录 前言1、SQLite介绍2、通过SQLite创建一个数据库文件3、往数据库文件中插入数据4、数据库文件信息查询5、修改数据库中的内容6、删除数据库中的内容 前言 本文是通过轻量化数据库管理工具SQLite进行的基础操作和一些功能实现。 1、SQLite介绍 SQLite是一个广泛使用的嵌入…

Docker镜像构建

Docker镜像构建 1. docker commit 平常我们都是从公共仓库拉取镜像&#xff0c;我们也可以从容器中构建我们自己的镜像。 需求&#xff1a; 1. 基础镜像centos 2. 安装jdk 3. 安装nginx1.1 创建容器 # 拉取镜像 docker pull centos:7 # 创建容器 docker run -di --name ce…

鸿蒙实战开发-如何使用三方库

使用三方库 在使用三方库之前&#xff0c;需要安装 ohpm&#xff0c;并在环境变量中配置。 在项目目录的Terminal窗口执行ohpm命令下载依赖 ohpm install yunkss/eftool 命令运行成功后&#xff0c;在项目的oh-package.json5文件中会自动添加上依赖&#xff0c;如下所示&am…

Python读取PDF文字转txt,解决分栏识别问题,能读两栏

搜索了一下&#xff0c;大致有这些库能将PDF转txt 1. PyPDF/PyPDF2&#xff08;截止2024.03.28这两个已经合并成了一个&#xff09;pypdf PyPI 2. pdfplumber GitHub - jsvine/pdfplumber: Plumb a PDF for detailed information about each char, rectangle, line, et cete…

react 面试题(2024 最新版)

1. 对 React 的理解、特性 React 是靠数据驱动视图改变的一种框架&#xff0c;它的核心驱动方法就是用其提供的 setState 方法设置 state 中的数据从而驱动存放在内存中的虚拟 DOM 树的更新 更新方法就是通过 React 的 Diff 算法比较旧虚拟 DOM 树和新虚拟 DOM 树之间的 Chan…

Docker搭建LNMP环境实战(07):安装nginx

1、模拟应用场景描述 假设我要搭建一个站点&#xff0c;假设虚拟的域名为&#xff1a;api.test.site&#xff0c;利用docker实现nginxphp-fpmmariadb部署。 2、目录结构 2.1、dockers根目录 由于目前的安装是基于Win10VMWareCentOS虚拟机&#xff0c;同时已经安装了VMWareT…

【React】vite + react 项目,配置项目路径别名 @

vite react 项目&#xff0c;配置项目路径别名 1 安装 types/node2 在 vite.config.ts 中添加配置&#xff1a;3 配置路径别名的提示 使用 vite 开发 react 项目时&#xff0c;可以通过一下步骤配置路径别名&#xff1a; 1 安装 types/node npm i -D types/node2 在 vite.con…

vue3组合式函数

vue3的组合式函数的作用是封装和复用响应式状态的函数。只能在setup 标签的script标签汇总或者setup函数中使用。 普通的函数只能调用一次&#xff0c;但是组合式函数接受到响应式参数&#xff0c;当该值发生变化时&#xff0c;也会触发相关函数的重新加载。 如下 定义了一个…

聊一聊电子邮件?

电子邮件是什么&#xff1f; 电子邮件是一种基于客户/服务器架构的应用。功能是实现人与人之间的交流。直到现在&#xff0c;电子邮件依然是当前因特网 注意&#xff1a;基于客户/服务器方式和基于B/S架构不一样&#xff01;客户/服务器表示的范围更广&#xff0c;当基于客户…