Go Lintpack: Composable Linter Manager


lintpack ist ein Dienstprogramm zum Erstellen von Linter (statischen Analysatoren), die mit der bereitgestellten API geschrieben werden. Darauf aufbauend wird der einigen bekannte statische Analysator für Kritiker jetzt neu geschrieben.


Heute werden wir genauer lintpack was lintpack aus Sicht des Benutzers ist.


Am Anfang war Go-Kritiker ...


go-kritiker begann als Pilotprojekt, das eine Sandbox für das Prototyping fast aller statischen Analyseideen für Go war.


Eine angenehme Überraschung war, dass einige Leute Implementierungen von Detektoren für verschiedene Probleme im Code schickten. Alles war unter Kontrolle, bis sich eine technische Verschuldung ansammelte, die praktisch niemand beseitigen konnte. Die Leute kamen herein, fügten eine Bestätigung hinzu und verschwanden dann. Wer muss dann Fehler korrigieren und die Implementierung ändern?


Ein wichtiges Ereignis war der Vorschlag, Überprüfungen hinzuzufügen, die eine zusätzliche Konfiguration erfordern, dh solche, die von lokalen Vorkehrungen für das Projekt abhängen. Ein Beispiel ist das Aufdecken eines Copyright-Headers in einer Datei (Lizenz-Header) mithilfe einer speziellen Vorlage oder das Verbot des Imports einiger Pakete mit dem Vorschlag einer bestimmten Alternative.


Eine weitere Schwierigkeit war die Erweiterbarkeit. Es ist nicht für jeden bequem, seinen Code an das Repository eines anderen zu senden. Einige wollten ihre Prüfungen dynamisch verbinden, damit sie die go-critic Quellcodes nicht ändern mussten.


Zusammenfassend sind hier die Probleme, die der Entwicklung von go-critic im Wege standen:


  • Eine Menge Komplexität. Zu viel Unterstützung, das Vorhandensein eines inhaberlosen Codes.
  • Niedrige durchschnittliche Qualität. experimental bedeutete "fast einsatzbereit" und "besser gar nicht laufen".
  • Manchmal ist es schwierig zu entscheiden, ob die Überprüfung in den go-critic , und ihre Ablehnung widerspricht der ursprünglichen Designphilosophie.
  • Verschiedene Leute sahen go-critic unterschiedlich. Die meisten wollten es als CI-Linter haben, der mit gometalinter .

Um die Anzahl der Diskrepanzen und inkonsistenten Interpretationen des Projekts irgendwie zu begrenzen, wurde ein Manifest geschrieben.


Wenn Sie zusätzlichen historischen Kontext und noch mehr Gedanken über die Kategorisierung statischer Analysatoren wünschen , können Sie sich die GoCritic- Aufzeichnung anhören , einen neuen statischen Analysator für Go . Zu diesem Zeitpunkt gab es noch kein Lintpack, aber einige der Ideen wurden an diesem Tag nach dem Bericht geboren.

Aber was wäre, wenn wir nicht alle Schecks in einem Repository speichern müssten?


Meet - Lintpack




go-critic besteht aus zwei Hauptkomponenten:


  1. Durchführung der Prüfungen selbst.
  2. Ein Programm, das Go-validierte Pakete herunterlädt und Validierungen auf ihnen ausführt.

Unser Ziel: Schecks für den Linter in verschiedenen Repositories speichern und bei Bedarf zusammen sammeln zu können.


lintpack macht genau das. Es definiert Funktionen, mit denen Sie Ihre Prüfungen so beschreiben können, dass Sie sie dann durch den generierten Linter ausführen können.


Pakete, die mit lintpack als Framework implementiert werden, werden als lintpack kompatible oder lintpack kompatible Pakete bezeichnet.

Wenn go-critic lintpack go-critic auf Basis von lintpack , konnten alle Checks in mehrere Repositories unterteilt werden. Eine der Optionen für die Trennung kann die folgende sein:


  1. Der Hauptsatz, in den alle stabilen und unterstützten Prüfungen gelangen.
  2. Contrib-Repository, in dem der Code liegt, der entweder zu experimentell ist oder keinen Betreuer hat.
  3. Anpassbare Prüfungen für ein bestimmtes Projekt.

Der erste Punkt ist von besonderer Bedeutung im Zusammenhang mit der Integration von Go-Kritiker in Golangci-Lint .


Wenn Sie auf der Ebene der go-critic bleiben, hat sich für die Benutzer fast nichts geändert. lintpack erstellt einen nahezu identischen Linter, während golangci-lint alle verschiedenen Implementierungsdetails zusammenfasst.


Aber etwas hat sich geändert. Wenn neue lintpack auf der Basis von lintpack , haben Sie eine lintpack Auswahl an vorgefertigten Diagnosen zur Erzeugung eines Linter. Stellen Sie sich für einen Moment vor, dass dies so ist und es weltweit mehr als 10 verschiedene Überprüfungssätze gibt.


Schnellstart



Um zu beginnen, müssen Sie lintpack selbst installieren:


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

Erstellen Sie einen Linter mit dem lintpack von lintpack :


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

Das Set enthält panicNil , das panic(nil) im Code findet und ihn durch etwas Unterscheidbares ersetzen panicNil , da andernfalls recover() nicht erkennen kann, ob panic mit dem Argument nil aufgerufen wurde oder überhaupt keine Panik aufgetreten ist.


Beispiel mit Panik (null)


Der folgende Code versucht, den Wert zu beschreiben, der aus recover() :


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

Das Ergebnis ist für panic(nil) und für ein Programm, das nicht in panic(nil) , identisch.


Ein Beispiel für das beschriebene Verhalten .




Sie können den Linter in separaten Dateien mit Argumenten vom Typ ./... oder Paketen (über deren Importpfad) starten.


 ./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 

Standardmäßig reagiert diese Prüfung auch auf panic(interface{}(nil)) . Um dieses Verhalten zu überschreiben, setzen Sie skipNilEfaceLit auf true . Sie können dies über die Befehlszeile tun:


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

Verwendung für cmd / lintpack und generierten Linter


Sowohl lintpack als auch der generierte Linter verwenden das erste Argument, um einen Unterbefehl auszuwählen. Die Liste der verfügbaren Unterbefehle und Beispiele für deren Start erhalten Sie, indem Sie das Dienstprogramm ohne Argumente aufrufen.


 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 

Angenommen, wir haben den erstellten Linter mit dem Namen 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 

Das Flag -help ist für einige Unterbefehle verfügbar, die zusätzliche Informationen enthalten (ich habe einige Zeilen zu breit ausgeschnitten):


 ./gocritic check -help #     . 



Dokumentation der installierten Prüfungen


Die Antwort auf die Frage "Wie erfahre ich etwas über den Parameter skipNilEfaceLit?" - Lesen Sie das schicke Handbuch (RTFM)!


Die gesamte Dokumentation zu installierten Prüfungen befindet sich in mylinter . Diese Dokumentation ist über den Unterbefehl doc verfügbar:


 #     : $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) 

Wie bei der Unterstützung von Vorlagen in go list -f können Sie eine Vorlagenzeile übergeben, die für das Ausgabeformat der Dokumentation verantwortlich ist. Dies kann beim Schreiben von Markdown-Dokumenten hilfreich sein.


Wo kann man nach Installationsprüfungen suchen?


Um die Suche nach nützlichen lintpack zu vereinfachen, gibt es eine zentralisierte Liste von lintpack kompatiblen Paketen: https://go-lintpack.imtqy.com/ .


Hier sind einige der Liste:



Diese Liste wird regelmäßig aktualisiert und kann angefordert werden. Jedes dieser Pakete kann verwendet werden, um einen Linter zu erstellen.


Der folgende Befehl erstellt einen Linter, der alle Überprüfungen aus der obigen Liste enthält:


 #   ,      #   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 umfasst alle Überprüfungen in der Kompilierungsphase. Der resultierende Linter kann in einer Umgebung platziert werden, in der es keine Quellcodes für die Implementierung der installierten Diagnose gibt. Bei statischer Verknüpfung ist alles wie gewohnt.


Dynamischer Paketanhang


Zusätzlich zur statischen Assembly können Plugins geladen werden, die zusätzliche Überprüfungen ermöglichen.


Die Besonderheit ist, dass die Checker-Implementierung nicht weiß, ob sie für die statische Kompilierung verwendet oder als Plug-In geladen wird. Es sind keine Änderungen am Code erforderlich.


Angenommen, wir möchten dem Linter panicNil hinzufügen, können ihn jedoch nicht aus allen Quellen neu panicNil , die bei der ersten Kompilierung verwendet wurden.


  1. Erstellen Sie linterPlugin.go :

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

  1. Erstellen Sie eine dynamische Bibliothek:

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

  1. Führen Sie den Linter mit dem Parameter -pluginPath :

 ./linter check -pluginPath=linterPlugin.so bytes 

Warnung: Die Unterstützung für dynamische Module wird über ein Plugin- Paket implementiert, das unter Windows nicht funktioniert.

Das Flag -verbose kann Ihnen dabei helfen, herauszufinden, welche Prüfung -verbose oder deaktiviert ist, und zeigt vor allem an, welcher Filter die Prüfung deaktiviert hat.


Beispiel mit -verbose


Beachten Sie, dass panicNil in der Liste der enthaltenen Prüfungen angezeigt wird. Wenn wir das Argument -pluginPath entfernen, ist es nicht mehr wahr.


 ./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 # ...   . 



Vergleich mit Gometallinter und Golangci-Flusen


Um Verwirrung zu vermeiden, sollten die Hauptunterschiede zwischen den Projekten beschrieben werden.


gometalinter und golangci-lint integrieren in erster Linie andere, oft sehr unterschiedlich implementierte Linters und bieten einen bequemen Zugang zu ihnen. Sie richten sich an Endbenutzer, die statische Analysegeräte verwenden.


lintpack vereinfacht die Erstellung neuer Linters und bietet ein Framework, mit dem verschiedene auf seiner Basis implementierte Pakete in derselben ausführbaren Datei kompatibel sind. Diese Überprüfungen (für Golangci-Lint) oder die ausführbare Datei (für Gometalinter) können dann in die oben genannten Meta-Linters eingebettet werden.


Angenommen, eine der mit lintpack kompatiblen Prüfungen ist Teil von golangci-lint . Wenn es ein Problem im Zusammenhang mit der Benutzerfreundlichkeit gibt, liegt dies möglicherweise in der Verantwortung von golangci-lint . Wenn es sich jedoch um einen Fehler bei der Implementierung der Verifizierung selbst handelt, ist dies das Problem der Autoren des Verifizierungs-Lintpack-Ökosystems.


Mit anderen Worten, diese Projekte lösen verschiedene Probleme.


Was ist mit Go-Kritiker?


Der Portierungsvorgang von go-critic lintpack nach lintpack ist bereits abgeschlossen. Checkers finden Sie im Repository von go-critic lintpack / Checkers .


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

Es macht wenig Sinn, go-critic golangci-lint außerhalb von golangci-lint , aber mit lintpack können Sie die Prüfungen installieren, die nicht im go-critic lintpack go-critic . Dies können beispielsweise von Ihnen geschriebene Diagnosen sein.


Fortsetzung folgt


Im nächsten Artikel erfahren Sie, wie Sie Ihre eigenen lintpack kompatiblen Prüfungen erstellen.


Dort analysieren wir, welche Vorteile Sie bei der Implementierung Ihres Lintpack-basierten lintpack gegenüber der Implementierung von Grund auf erhalten.


Ich hoffe, Sie haben Appetit auf neue Schecks für Go. Lassen Sie mich wissen, wie viel statische Analyse zu viel wird. Wir werden dieses Problem schnell gemeinsam lösen.

Source: https://habr.com/ru/post/de430196/


All Articles