Go Lintpack:可组合的lint管理器


lintpack是用于构建linter (静态分析器)的实用程序,使用提供的API编写这些实用程序。 基于此,一些人熟悉的go-critic静态分析器现在正在被重写。


今天,我们将从用户的角度更详细地lintpack什么是lintpack


一开始是批评家...


go-critic从一个试点项目开始,当时是一个沙盒,用于为Go几乎所有静态分析构想制作原型。


令人惊喜的是,有人在代码中发送了各种问题检测器的实现。 在技​​术债务开始积累之前,一切都处于控制之下,实际上没有人可以消除。 人们进来,添加了验证,然后消失了。 谁来纠正错误并修改实现?


一个重大事件是提议添加需要其他配置的检查,即那些依赖于项目本地安排的检查。 一个示例是使用特殊模板揭示文件中版权标头(许可证标头)的存在,或者通过给定替代方案的建议禁止导入某些软件包。


另一个困难是可扩展性。 不是每个人都愿意将其代码发送到其他人的存储库。 有些人想动态地连接他们的支票,以使他们不需要修改go-critic源代码。


总结一下,这是阻碍go-critic发展的问题:


  • 大量的复杂性。 支持过多,无所有者代码的存在。
  • 平均质量低。 experimental意思是“几乎随时可以使用”和“完全不运行”。
  • 有时候,很难决定是否在go-critic包含验证go-critic ,而拒绝验证则与原始设计理念背道而驰。
  • 不同的人go-critic看法不同。 大多数人希望将它作为gometalinter随附的CI gometalinter

为了以某种方式限制项目的差异和不一致的解释,撰写了一份宣言


如果您需要更多的历史背景信息,甚至需要更多有关静态分析器分类的思考,可以收听GoCritic录音,这是Go的新静态分析器 。 那时,lintpack还不存在,但是其中一些想法是在报告发布的那天诞生的。

但是,如果我们不需要将所有支票存储在一个存储库中怎么办?


见面-Lintpack




go-critic包括两个主要组成部分:


  1. 自己执行检查。
  2. 一个程序,该程序下载Go验证包并在其上运行验证。

我们的目标:能够将短绒的支票存储在不同的存储库中,并在必要时将它们收集在一起。


lintpack就是这样做的。 它定义了一些功能,这些功能使您可以描述支票,从而可以在生成的短绒棉纸上运行支票。


使用lintpack作为框架实现的软件包将称为lintpack兼容或lintpack兼容的软件包。

如果go-critic是基于lintpack实施的,则所有检查都可以分为几个存储库。 分离的选项之一可能是:


  1. 所有稳定和受支持的支票都将获得的主要集合。
  2. 代码所在的contrib存储库过于试验或缺少维护者。
  3. 可定制检查特定项目。

第一点对于将go-critic集成到golangci-lint中特别重要。


如果您一直处于go-critic级别,那么对于用户而言,几乎没有任何变化。 lintpack创建几乎相同的golangci-lint ,而golangci-lint封装了所有不同的实现细节。


但是情况发生了变化。 如果在lintpack的基础上创建了新的lintpack ,则将有更多可供选择的现成诊断程序来生成linter。 暂时想象一下,世界上有十​​多种不同的支票。


快速上手



首先,您需要安装lintpack本身:


 # lintpack    `$(go env GOPATH)/bin`. go get -v github.com/go-lintpack/lintpack/... 

使用lintpack的测试包创建一个lintpack


 lintpack build -o mylinter github.com/go-lintpack/lintpack/checkers 

该集合包括panicNil ,它在代码中找到panic(nil)并要求将其替换为可区分的内容,否则, recover()将无法判断是否使用nil参数调用了panic ,或者根本没有恐慌。


恐慌(无)示例


下面的代码尝试描述从recover()获得的值:


 r := recover() fmt.Printf("%T, %v\n", r, r) 

对于panic(nil)和不发生panic(nil)的程序,结果将相同。


所描述行为的运行示例




您可以在参数为。/ ./...或程序包(按其导入路径)的单独文件上启动linter。


 ./mylinter check bytes $GOROOT/src/bytes/buffer_test.go:276:3: panicNil: panic(nil) calls are discouraged 

 #   ,  go-lintpack    $GOPATH. mylinter=$(pwd)/mylinter cd $(go env GOPATH)/src/github.com/go-lintpack/lintpack/checkers/testdata $mylinter check ./panicNil/ ./panicNil/positive_tests.go:5:3: panicNil: panic(nil) calls are discouraged ./panicNil/positive_tests.go:9:3: panicNil: panic(interface{}(nil)) calls are discouraged 

默认情况下,此检查还会响应panic(interface{}(nil)) 。 要覆盖此行为,请将skipNilEfaceLit设置为true 。 您可以通过命令行执行此操作:


 $mylinter check -@panicNil.skipNilEfaceLit=true ./panicNil/ ./panicNil/positive_tests.go:5:3: panicNil: panic(nil) calls are discouraged 

cmd / lintpack和生成的linter的用法


lintpack和生成的lintpack都使用第一个参数来选择子命令。 可以通过调用不带参数的实用程序来获得可用子命令的列表及其启动示例。


 lintpack not enough arguments, expected sub-command name Supported sub-commands: build - build linter from made of lintpack-compatible packages $ lintpack build -help $ lintpack build -o gocritic github.com/go-critic/checkers $ lintpack build -linter.version=v1.0.0 . version - print lintpack version $ lintpack version 

假设我们用gocritic的名字命名了创建的gocritic


 ./gocritic not enough arguments, expected sub-command name Supported sub-commands: check - run linter over specified targets $ linter check -help $ linter check -disableTags=none strings bytes $ linter check -enableTags=diagnostic ./... version - print linter version $ linter version doc - get installed checkers documentation $ linter doc -help $ linter doc $ linter doc checkerName 

-help标志可用于某些子命令,该命令提供了附加信息(我将某些行剪得太宽了):


 ./gocritic check -help #     . 



已安装支票的文件


问题“如何找到关于skipNilEfaceLit参数的答案”? -阅读精美的手册(RTFM)!


有关已安装支票的所有文档都在mylinter 。 该文档可通过doc子命令获得:


 #     : $mylinter doc panicNil [diagnostic] #      : $mylinter doc panicNil panicNil checker documentation URL: github.com/go-lintpack/lintpack Tags: [diagnostic] Detects panic(nil) calls. Such panic calls are hard to handle during recover. Non-compliant code: panic(nil) Compliant code: panic("something meaningful") Checker parameters: -@panicNil.skipNilEfaceLit bool whether to ignore interface{}(nil) arguments (default false) 

类似于go list -f对模板的支持,您可以传入一个负责文档输出格式的模板行,这在编写markdown文档时很有用。


在哪里寻找安装检查?


为了简化对有用lintpack套件的搜索,这里提供了与lintpack兼容的软件包的集中列表: https : lintpack


以下是一些列表:



该列表会定期更新,并且可以接受添加请求。 这些软件包中的任何一个都可用于创建短绒。


下面的命令创建一个linter,其中包含上面列表中的所有检查:


 #   ,      #   Go . go get -v github.com/go-critic/go-critic/checkers go get -v github.com/go-critic/checkers-contrib # build   . lintpack build \ github.com/go-critic/go-critic/checkers \ github.com/go-critic/checkers-contrib 

lintpack build包括编译阶段的所有检查,可以将生成的linter放置在没有可用于实现已安装诊断的源代码的环境中,所有情况与静态链接一样。


动态包装附件


除了静态程序集,还可以加载提供其他检查的插件。


特殊之处在于,检查器实现不知道它将用于静态编译还是作为插件加载。 无需更改代码。


假设我们想将panicNil添加到panicNil中,但是我们不能从第一次编译期间使用的所有源中重建它。


  1. 创建linterPlugin.go

 package main //         , //    import'. import ( _ "github.com/go-lintpack/lintpack/checkers" ) 

  1. 建立一个动态库:

 go build -buildmode=plugin -o linterPlugin.so linterPlugin.go 

  1. 使用-pluginPath参数运行-pluginPath

 ./linter check -pluginPath=linterPlugin.so bytes 

警告:对动态模块的支持是通过在Windows上不起作用的插件包实现的。

-verbose标志可以帮助您确定打开或关闭了哪个检查,最重要的是,它将显示哪个过滤器禁用了该检查。


-verbose的示例


请注意, panicNil显示在包括的检查列表中。 如果删除-pluginPath参数,它将不再是正确的。


 ./linter check -verbose -pluginPath=./linterPlugin.so bytes debug: appendCombine: disabled by tags (-disableTags) debug: boolExprSimplify: disabled by tags (-disableTags) debug: builtinShadow: disabled by tags (-disableTags) debug: commentedOutCode: disabled by tags (-disableTags) debug: deprecatedComment: disabled by tags (-disableTags) debug: docStub: disabled by tags (-disableTags) debug: emptyFallthrough: disabled by tags (-disableTags) debug: hugeParam: disabled by tags (-disableTags) debug: importShadow: disabled by tags (-disableTags) debug: indexAlloc: disabled by tags (-disableTags) debug: methodExprCall: disabled by tags (-disableTags) debug: nilValReturn: disabled by tags (-disableTags) debug: paramTypeCombine: disabled by tags (-disableTags) debug: rangeExprCopy: disabled by tags (-disableTags) debug: rangeValCopy: disabled by tags (-disableTags) debug: sloppyReassign: disabled by tags (-disableTags) debug: typeUnparen: disabled by tags (-disableTags) debug: unlabelStmt: disabled by tags (-disableTags) debug: wrapperFunc: disabled by tags (-disableTags) debug: appendAssign is enabled debug: assignOp is enabled debug: captLocal is enabled debug: caseOrder is enabled debug: defaultCaseOrder is enabled debug: dupArg is enabled debug: dupBranchBody is enabled debug: dupCase is enabled debug: dupSubExpr is enabled debug: elseif is enabled debug: flagDeref is enabled debug: ifElseChain is enabled debug: panicNil is enabled debug: regexpMust is enabled debug: singleCaseSwitch is enabled debug: sloppyLen is enabled debug: switchTrue is enabled debug: typeSwitchVar is enabled debug: underef is enabled debug: unlambda is enabled debug: unslice is enabled # ...   . 



gometalintergolangci-lint的比较


为了避免混淆,值得描述项目之间的主要区别。


gometalintergolangci-lint主要集成了其他的,通常实现方式非常不同的linter ,它们提供了便捷的访问途径。 他们针对使用静态分析仪的最终用户。


lintpack简化了新linters的创建,提供了一个框架,该框架使在其基础上实现的不同程序包在同一可执行文件中兼容。 然后,可以将这些检查(对于golangci-lint)或可执行文件(对于gometalinter)嵌入到上述meta-lint中。


假设与lintpack兼容的检查之一是golangci-lint一部分。 如果有任何与其可用性相关的问题,这可能是golangci-lint的责任,但是如果在实施验证本身时出错,那么这就是验证作者lintpack生态系统的问题。


换句话说,这些项目解决了各种问题。


批评家呢?


go-critic移植到lintpack过程已经完成;可以在go-critic / checkers存储库中找到检查程序


 #  go-critic : go get -v github.com/go-critic/go-critic/... #  go-critic : lintpack -o gocritic github.com/go-critic/go-critic/checkers 

golangci-lint之外使用go-critic毫无意义,但是lintpack可以让您安装go-critic未包含的那些检查。 例如,这些可能是您编写的诊断信息。


待续


在下一篇文章中,您将学习如何创建与lintpack兼容的检查。


与从头开始的实现相比,在这里我们将分析在实现基于lintpack的linter时获得的优势。


我希望您有兴趣购买Go的新支票。 让我知道静态分析变得太多了,我们将一起快速解决这个问题。

Source: https://habr.com/ru/post/zh-CN430196/


All Articles