Go语言

大约一年前,我决定磨练我的Go技能。虽然与我每天使用的大多数其他语言相比,该语言相当小,但它仍然有一些我没有用到的有用的语法结构。除了用它构建工具之外,还有什么更好的方法来提高编程语言的技能......用于分析用它编写的程序?

你可以找到重振 GitHub上的github.com/mgechev/revive
Go语言的快速,可扩展,可配置和美观的linter-脚本宝典

介绍Revive

几年前,当我开始使用Go时,我注意到生态系统是多么自以为是。首先,语言的语法非常简约。与Perl和Ruby等语言相比,这种属性降低了表现力,但也使代码更容易阅读。

Go语言的快速,可扩展,可配置和美观的linter-脚本宝典

其次,“标签与空格”的争论与围棋世界无关。我们都使用标签。而已。我更喜欢空格上的标签吗?没关系 - gofmt已经为我挑选了标签。我也注意到了golint。它是一种强制语义和语法实践的工具。golint我用过的东西与大多数东西不同:

  • 它不可扩展。您不能包含/排除规则,并且项目不适用于新规则。
  • 它不允许我们禁用特定文件或文件中的一系列行的规则。
  • golint失败中有一定程度的信心。有时失败是假阴性。

Go团队正在保持工具的主张和简约。它遵循Go哲学,我尊重这一点。该项目的封闭范围几乎没有什么有趣的含义:

Go语言的快速,可扩展,可配置和美观的linter-脚本宝典
Georgi Serev复兴的标志

这就是为什么我决定将我的玩具用品项目公开并与社区分享。Revive实现了所有这些规则golint已经和失败都认为“信心”同样的概念golint引入。事实上,revive没有标志的调用具有相同的行为golint,不同之处在于它运行得更快。Revive建立在golint

  • 允许我们使用配置文件启用或禁用规则。
  • 允许我们使用TOML文件配置linting规则。
  • 提供禁用特定规则的功能或为文件或一系列行禁用整个linter。
    • golint 仅允许生成的文件。
  • 提供多个格式化程序,让我们自定义输出。
  • 允许我们自定义整个linter的返回代码或仅基于某些规则的失败。
  • 每个人都可以使用自定义规则或格式化程序轻松扩展它。
  • Revive提供了更多的规则golint
  • Revive跑得快。它在单独的goroutine中运行每个文件的规则。

我喜欢Go社区的自以为是的文化。我相信这是一个正确的方向,它让我们专注于重要的事情,而不是在讨论琐碎的事情上浪费时间。这就是为什么revive,通过定义更多自以为是的规则并提供更严格的预设,我们可以使更多与语法相关的参数无关紧要。

稍后,我将解释如何快速制定更多规则,revive但在此之前,让我分享一些关于如何使用短绒的说明!

用法

安装时间revive

go get github.com/mgechev/revive

上面的命令添加了revive二进制文件$GOPATH/bin

使用没有标志的工具具有与之相同的行为golint。当我们添加-formatter标志时会发生魔力:

Go语言的快速,可扩展,可配置和美观的linter-脚本宝典

从上图中我们可以看到,我们得到了31条警告"exported"。此规则是内置规则的端口,golint从中强制执行导出符号的实践(在此处查找完整的规则集)。

如果我们更喜欢忽略整个项目的这些警告,我们可以使用TOML格式的配置文件:

Go语言的快速,可扩展,可配置和美观的linter-脚本宝典

如果我们要仅为部分文件禁用特定规则,该怎么办?在这种情况下,我们可以使用以下技术:

package models

//revive:disable

type Expression struct {
    Value      string
    IsStar     bool
    IsVariadic bool
    IsWriter   bool
    Underlying string
}
//revive:enable

上面的注释禁用revive整个结构的所有规则。如果我们更喜欢只禁用exported规则,我们应该使用:

package models

//revive:disable:exported

type Expression struct {
    Value      string
    IsStar     bool
    IsVariadic bool
    IsWriter   bool
    Underlying string
}
//revive:enable:exported

请记住,针对linter的注释不应该以空格开头。有关更多详细信息,请参阅revive:unidiomatic // revive:语法

最后,如果我们想忽略目录中的所有文件,我们可以使用-exclude标志:

revive -exclude tests/... ./...

上面的命令将tests递归地从当前目录中删除所有文件,不包括所有文件。如果我们想要排除多个目录使用:

revive -exclude tests/... -exclude utils/... ./...

可配置

在上一节的图像中,我们看到通过使用TOML格式的配置文件,我们可以配置执行revive。这是一个示例配置文件:

# Ignores files with "GENERATED" header, similar to golint
ignoreGeneratedHeader = true

# Sets the default severity to "warning"
severity = "warning"

# Sets the default failure confidence. The semantics behind this property
# is that revive ignores all failures with a confidence level below 0.8.
confidence = 0.8

# Sets the error code for failures with severity "error"
errorCode = 0

# Sets the error code for failures with severity "warning"
warningCode = 0

# Configuration of the `cyclomatic` rule. Here we specify that
# the rule should fail if it detects code with higher complexity than 10.
[rule.cyclomatic]
  arguments = [10]

# Sets the severity of the `package-comments` rule to "error".
[rule.package-comments]
  severity = "error"

让我们快速浏览一下各个属性。

  • ignoreGeneratedHeadergolint忽略具有标题的文件GENERATED。要禁用此行为,请将标志设置为false
  • severityrevive有两种类型的严重性 - warningerror。通过这种方式,我们可以区分具有高置信度的关键故障和具有较低置信度的关
  • confidence- 类似地golintrevive让我们为可能返回漏报的规则分配置信度。该confidence属性允许我们在给定的置信水平下过滤失败。
  • errorCodewarningCode-这两个属性让我们有不同的严重程度不同的返回码。我们可能希望您的错误无法通过CI,但我们的警告不会。

其余行与规则配置相关。可以通过设置其参数及其严重性来配置每个规则。例如,如果在我们的代码库中有一个超过10的圈复杂度的结构,上面的圈复杂度规则就会失败。

Go语言的快速,可扩展,可配置和美观的linter-脚本宝典

可扩展性和贡献

最初,我想让开发人员使用能够创建外部插件,以后可以通过配置文件引用并动态加载。不幸的是,-buildmode=plugin已知问题的支持非常有限

忽略此限制,还有另外两种简单的方法可以添加新规则并针对您的代码运行它们:

  • 为项目revive做出贡献- 对外部贡献开放。如果规则有意义并且通过了单元测试,那么欢迎成为其中的一部分revive!最重要的是,创建一个新规则只是实现这个简单的接口:
type Rule interface {
    Name() string
    Apply(*File, Arguments) []Failure
}
  • 分叉项目并将规则推到那里。如果您认为您的规则不适用于其他人(尽管他们可能会这样做),您可以分叉revive而不是将它们推向上游。只需确保您偶尔将代码与上游同步,以获取所有新功能和错误修复程序!

请记住,要创建规则,您不必熟悉整个代码库。所有规则都通过简单的界面很好地封装到访问者中。arguments-limit可以在此处找到示例实现。

格式化程序怎么样?

好吧,创建一个新的格式化器就像创建一个新规则一样简单。只需实现以下界面:

type Formatter interface {
    Format(<-chan Failure, RulesConfig) (string, error)
    Name() string
}

在这里,您可以找到JSON格式化程序的示例实现。

性能

我运行一些基本的基准测试,比较性能golintrevive。这是我在运行两个短线后发现的kubernetes

time golint be/…
real    0m25.389s
user    0m29.221s
sys     0m3.065s
time revive be/…
real    0m6.524s
user    0m22.882s
sys     0m1.114s

由于revive在单独的goroutine中提取单个文件,因此它优于golint大约4次

结论

Revive是一款简单,快速,可配置,可扩展,灵活,美观的Go。它在单独的goroutine中的每个文件的顶部运行linting规则,显着提高了性能。Revive让我们配置各个规则并为整个项目,单个文件或文件中的行范围禁用它们。最后但并非最不重要的是,revive让我们使用一组内置格式化程序,以易于理解,易于访问和易于使用的格式输出故障。

该项目不断发展,并以新规则,格式化程序或错误修复的形式开放新的贡献!如果您想创建一个更严格的linting预设,将团队中的编码风格讨论减少到最少,并专注于基本的事情,revive可能会有所帮助。

Go语言的快速,可扩展,可配置和美观的linter-脚本宝典