Go 语言代码检查
代码格式化
go fmt
在 Go 语言中用于格式化代码。格式化后,代码风格保持一致,更容易被维护。
格式化处理包括但不限于以下方面:
- 缩进使用 tab 键而非空格。
- 左大括号
{
总是放在与控制语句同一行末尾。 - 在运算符和逗号后自动添加空格。
- 确保导入包声明按字典顺序排列,并且格式化为组。
- 自动删除多余空格和空行。
- …
基本用法
命令基本格式为:go fmt [packages]
。这里 [packages]
表示想要格式化的包路径。如果不指定包路径,go fmt
将作用于当前目录。
例如格式化当前目录及其子目录下所有 Go 文件:
go fmt ./...
通过 -x
参数可以看到,go fmt
实际上是对 gofmt -l -w
命令的简单封装:
PS D:\Software\Programming\Go\new> go fmt -x main.go
D:\Software\Program\go\go1.22.0\bin\gofmt.exe -l -w main.go
直接使用 gofmt
命令可实现更多功能,例如下面这些选项:
-w
:修改后直接保存到文件。默认只打印格式化后结果。-s
:尝试简化代码。-r
:应用自定义替换规则来格式化代码。
简化代码
简化代码是指安全地减少代码冗余度和复杂度。例如下面代码中存在冗余的变量声明和没必要的索引方式:
package main
import "fmt"
func main() {
var s string = "123"
fmt.Println(s[0:len(s)])
}
通过 gofmt -s
命令简化代码效果如下:
D:\Software\Programming\Go>gofmt -s main.go
package main
import "fmt"
func main() {
var s string = "123"
fmt.Println(s[0:])
}
可以看到 s[0:len(s)]
被简化为 s[0:]
,但是 var s string = "123"
并没有被简化为 s := "123"
或 var s = "123"
。因为有些情况下必须显式声明变量类型,不能使用自动推断的类型,要判断具体情况估计逻辑太复杂,还得靠人工优化。
gofmt -s
尽管优化力度有限,但还是建议加入到日常开发中,例如配置到 GoLand 自动触发:
- 打开「设置」-「工具」-「File Watcher」,点击新建「custom」自定义模板;
- 「名称」填入:
简化代码
,「文件类型」选择「Go 文件」,「作用域」选择「当前文件」; - 「程序」字段填入:
gofmt
,「实参」字段填入:-s -w $FilePathRelativeToProjectRoot$
,「输出路径」填入:$FilePathRelativeToProjectRoot$
。内置变量指代当前文件的绝对路径; - 展开「高级选项」,取消勾选「自动保存编辑的文件以触发观察程序」,只用保持「进行外部更改时触发观察程序」。这样配置确保简化命令不会在自动保存时触发,免得与内置「代码样式」功能冲突;
- 点击「确定」按钮完成配置,测试效果。
配置后,GoLand 在每次手动执行代码格式化时,会自动调用简化代码命令。
替换规则
gofmt
最强大的功能要数自定义替换规则,gofmt -r
使用 Go 语言抽象语法树(AST)来进行模式匹配和替换,可以用于自动重构代码。
语法为:gofmt -r 'pattern -> replacement' -w [path ...]
。说明如下:
pattern
是希望匹配的代码片段。replacement
是用来替换的新代码片段。- 规则必须被单引号「'」包围。如果在 Windows 平台,则使用双引号「"」代替。
- 可以同时应用多个替换规则,只需在引号内用逗号分隔每个规则。
最简单的用处是替换包名、常量名、类型名、结构体字段名、函数调用和方法调用,例如换掉已废弃的 ioutil.WriteFile
函数:gofmt -r "ioutil.WriteFile -> os.WriteFile" -w .
也可以将所有形如 a[b:len(a)]
的代码替换为 a[b:]
:gofmt -r "a[b:len(a)] -> a[b:]" -w *.go
。
导入维护
goimports
是 gofmt
增强版,除了有 gofmt
所有功能外,还能自动处理导入依赖:自动添加缺失与自动删除多余的 import
语句。
goimports
需要先下载安装:
go install golang.org/x/tools/cmd/goimports@latest
然后就像使用 gofmt
一样使用它:
goimports -w -l -s .
升级修复
每当 Go 语言版本更新时,都可能引入一些破坏性变更,go fix
命令用来帮助修复升级导致的问题代码。go fix
会检查代码中已弃用的语法或库函数,并尝试替换为新方法或函数。
go fix
用法非常简单,直接指定路径或文件即可:
go fix .
随着 Go 语言日趋稳定,go fix
已很少使用。前面提到 ioutil
包已标记弃用很久,包中函数被分散到其他包中,例如 ioutil.TempDir
被替换为 os.MkdirTemp
,这种情况并不能用 go fix
来修复。
规范检查
一般 IDE 中都带有代码分析与审查功能,否则可以尝试使用 golint
来检查代码规范,并获得改进建议。
先通过 go install
来安装工具:
go install golang.org/x/lint/golint@latest
安装好后运行 golint
命令对指定文件或目录进行检查:
D:\Software\Programming\Go\new>golint
main.go:11:2: don't use underscores in Go names; var replace_str should be replaceStr
上面检查结果提出变量命名不规范。大多数时候,报告结果都需要仔细阅读,并一一对照改正。
错误检查
go vet
是代码静态分析工具,用于分析代码中各种逻辑和构造错误。这些错误不影响编译,但可能导致程序运行时异常。例如:
- 格式化字符串问题:格式化时参数类型不匹配,例如用
%d
来打印字符串。 - 死代码:永远无法运行的代码,例如在函数
return
语句后的代码。 - 变量问题:未使用的变量或包导入。
- 布尔表达式类型错误:使用一个非布尔值作为条件表达式。
- 误用比较:对函数类型值进行比较,或者比较结构体类型时,结构体包含不能比较字段。
- 误用通道:对一个
nil
通道读写或关闭。
还有更多 go vet
能检测到的错误类型,这里不逐一列举。
和 golint
一样,go vet
只负责检查并报告错误,需要开发手动修复代码:
D:\Software\Programming\Go\new>go vet
# new
# [new]
vet.exe: .\main.go:12:2: newstr declared and not used
尽管 go vet
可以捕获许多常见错误,但也有一些错误检测不到或误报。应该与其他工具配合使用,提高 Go 语言代码质量。