Go lintpack: gestionnaire de linter composable


lintpack est un utilitaire de construction de linter (analyseurs statiques), qui sont écrits à l'aide de l'API fournie. Sur cette base, l'analyseur statique de Go- Critic, familier à certains, est en train d'être réécrit.


Aujourd'hui, nous lintpack plus en détail ce qu'est le lintpack du point de vue de l'utilisateur.


Au début était critique de go ...


go-critique a commencé comme un projet pilote qui était un bac à sable pour le prototypage de presque toutes les idées d'analyse statique pour Go.


Une bonne surprise a été que certaines personnes ont envoyé des implémentations de détecteurs de divers problèmes dans le code. Tout était sous contrôle jusqu'à ce qu'une dette technique commence à s'accumuler, qu'il n'y avait pratiquement personne à éliminer. Les gens sont entrés, ont ajouté la vérification, puis ont disparu. Qui doit alors corriger les erreurs et modifier l'implémentation?


Un événement important a été la proposition d'ajouter des vérifications qui nécessitent une configuration supplémentaire, c'est-à-dire celles qui dépendent des dispositions locales pour le projet. Un exemple est de révéler la présence d'un en-tête de copyright dans un fichier (en-tête de licence) à l'aide d'un modèle spécial ou d'interdire l'importation de certains packages avec la proposition d'une alternative donnée.


Une autre difficulté était l'extensibilité. Il n'est pas pratique pour tout le monde d'envoyer son code au référentiel de quelqu'un d'autre. Certains ont voulu connecter dynamiquement leurs chèques afin de ne pas avoir à modifier les codes sources de go-critic critical.


Pour résumer, voici les problèmes qui ont fait obstacle au développement des go-critic :


  • Une charge de complexité. Trop de support, la présence d'un code sans propriétaire.
  • Faible qualité moyenne. experimental signifie «presque prêt à l'emploi» et «mieux ne pas fonctionner du tout».
  • Parfois, il est difficile de décider d'inclure ou non la vérification dans go-critic , et leur rejet contredit la philosophie de conception d'origine.
  • Différentes personnes voyaient les go-critic différemment. La plupart voulaient l'avoir comme un linter CI, qui vient avec gometalinter .

Afin de limiter en quelque sorte le nombre de divergences et d'interprétations incohérentes du projet, un manifeste a été rédigé.


Si vous voulez un contexte historique supplémentaire et encore plus de réflexion sur la catégorisation des analyseurs statiques, vous pouvez écouter l'enregistrement GoCritic, un nouvel analyseur statique pour Go . À ce moment, le lintpack n'existait pas encore, mais certaines idées sont nées ce jour-là, après le rapport.

Mais que faire si nous n'avions pas besoin de stocker tous les chèques dans un seul référentiel?


Rencontrez - lintpack




go-critic compose de deux éléments principaux:


  1. Mise en place des contrôles eux-mêmes.
  2. Un programme qui télécharge les packages validés par Go et exécute des validations sur ceux-ci.

Notre objectif: pouvoir stocker les chèques du linter dans différents référentiels et les collecter ensemble si nécessaire.


lintpack fait exactement cela. Il définit des fonctions qui vous permettent de décrire vos vérifications de telle manière que vous pouvez ensuite les exécuter via le linter généré.


Les packages qui sont implémentés en utilisant lintpack comme framework seront appelés lintpack compatibles avec lintpack ou compatibles avec lintpack .

Si go-critic était implémenté sur la base de lintpack , tous les contrôles pourraient être divisés en plusieurs référentiels. L'une des options de séparation peut être la suivante:


  1. L'ensemble principal où tous les contrôles stables et pris en charge sont récupérés.
  2. référentiel contrib où se trouve le code trop expérimental ou dépourvu de mainteneur.
  3. Vérifications personnalisables pour un projet spécifique.

Le premier point est particulièrement important en ce qui concerne l' intégration du go-critique dans golangci-lint .


Si vous restez au niveau go-critic , alors pour les utilisateurs, presque rien n'a changé. lintpack crée un lintpack presque identique, tandis que golangci-lint encapsule tous les différents détails d'implémentation.


Mais quelque chose a changé. Si de nouveaux lintpack sont créés sur la base de lintpack , vous disposerez d'une sélection plus riche de diagnostics prêts à l'emploi pour générer un linter. Imaginez un instant qu'il en soit ainsi, et qu'il existe plus de 10 ensembles de contrôles différents dans le monde.


Démarrage rapide



Pour commencer, vous devez installer lintpack lui-même:


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

Créez un linter à l'aide du package de test de lintpack :


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

L'ensemble comprend panicNil , qui trouve la panic(nil) dans le code et demande de le remplacer par quelque chose de distinct, car sinon recover() ne sera pas en mesure de dire si la panic été appelée avec un argument nil , ou s'il n'y a pas eu de panique du tout.


Exemple avec panique (néant)


Le code ci-dessous tente de décrire la valeur obtenue à partir de recover() :


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

Le résultat sera identique pour la panic(nil) et pour un programme qui ne panic(nil) pas.


Un exemple d'exécution du comportement décrit .




Vous pouvez démarrer le linter sur des fichiers séparés, avec des arguments de type ./... ou des packages (par leur chemin d'importation).


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

Par défaut, cette vérification répond également à la panic(interface{}(nil)) . Pour remplacer ce comportement, définissez skipNilEfaceLit sur true . Vous pouvez le faire via la ligne de commande:


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

utilisation pour cmd / lintpack et linter généré


Le lintpack et le linter généré utilisent le premier argument pour sélectionner une sous-commande. La liste des sous-commandes disponibles et des exemples de leur lancement peuvent être obtenues en appelant l'utilitaire sans arguments.


 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 

Supposons que nous avons nommé le linter créé par le nom 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 

L'indicateur -help est disponible pour certaines sous-commandes, ce qui fournit des informations supplémentaires (j'ai coupé certaines lignes trop larges):


 ./gocritic check -help #     . 



Documentation des chèques installés


La réponse à la question "comment découvrir le paramètre très skipNilEfaceLit?" - lisez le manuel de fantaisie (RTFM)!


Toute la documentation sur les chèques installés se trouve dans mylinter . Cette documentation est disponible via la sous-commande 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) 

Tout comme la prise en charge des modèles dans go list -f , vous pouvez passer une ligne de modèle responsable du format de sortie de la documentation, ce qui peut être utile lors de l'écriture de documents de démarque.


Où chercher des chèques pour l'installation?


Pour simplifier la recherche de suites de lintpack utiles, il existe une liste centralisée des lintpack compatibles avec lintpack : https://go-lintpack.imtqy.com/ .


Voici une partie de la liste:



Cette liste est mise à jour périodiquement et est ouverte aux demandes d'ajout. N'importe lequel de ces packages peut être utilisé pour créer un linter.


La commande ci-dessous crée un linter qui contient toutes les vérifications de la liste ci-dessus:


 #   ,      #   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 comprend toutes les vérifications au stade de la compilation, le linter résultant peut être placé dans un environnement où il n'y a pas de code source pour l'implémentation des diagnostics installés, tout est comme d'habitude avec une liaison statique.


Attachement de package dynamique


En plus de l'assemblage statique, il est possible de charger des plugins qui fournissent des vérifications supplémentaires.


La particularité est que l'implémentation du vérificateur ne sait pas si elle sera utilisée pour la compilation statique ou sera chargée en tant que plug-in. Aucune modification du code n'est requise.


Supposons que nous voulions ajouter panicNil au linter, mais nous ne sommes pas en mesure de le reconstruire à partir de toutes les sources utilisées lors de la première compilation.


  1. Créez linterPlugin.go :

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

  1. Construisez une bibliothèque dynamique:

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

  1. Exécutez le linter avec le paramètre -pluginPath :

 ./linter check -pluginPath=linterPlugin.so bytes 

Avertissement: la prise en charge des modules dynamiques est implémentée via un module d' extension qui ne fonctionne pas sous Windows.

L'indicateur -verbose peut vous aider à déterminer quelle vérification est -verbose ou désactivée et, plus important encore, il indiquera quel filtre a désactivé la vérification.


Exemple avec -verbose


Notez que panicNil affiché dans la liste des contrôles inclus. Si nous supprimons l'argument -pluginPath, il cessera d'être vrai.


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



Comparaison avec gometalinter et golangci-lint


Afin d'éviter toute confusion, il convient de décrire les principales différences entre les projets.


gometalinter et golangci-lint intègrent principalement d'autres linters , souvent mis en œuvre de manière très différente, leur offrent un accès pratique. Ils ciblent les utilisateurs finaux qui utiliseront des analyseurs statiques.


lintpack simplifie la création de nouveaux linters, fournit un cadre qui rend différents packages, implémentés sur sa base, compatibles dans le même fichier exécutable. Ces vérifications (pour golangci-lint) ou le fichier exécutable (pour gometalinter) peuvent ensuite être incorporés dans les méta-linters susmentionnés.


Supposons que l'une des lintpack compatibles lintpack le lintpack partie de golangci-lint . S'il y a un problème lié à son utilisation, cela peut être la responsabilité de golangci-lint , mais s'il s'agit d'une erreur dans la mise en œuvre de la vérification elle-même, alors c'est le problème des auteurs de la vérification, l'écosystème lintpack.


En d'autres termes, ces projets résolvent divers problèmes.


Et le go-critique?


Le processus de portage de go-critic lintpack vers lintpack est déjà terminé; les vérificateurs peuvent être trouvés dans le référentiel 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 

Cela n'a pas de sens d'utiliser go-critic golangci-lint dehors de golangci-lint , mais lintpack peut vous permettre d'installer les contrôles qui ne sont pas inclus dans l'ensemble go-critic . Par exemple, il peut s'agir de diagnostics écrits par vous.


À suivre


Vous apprendrez comment créer vos propres lintpack compatibles avec le lintpack dans le prochain article.


Nous y analyserons les avantages que vous obtenez lors de la mise en œuvre de votre linterpack lintpack par rapport à la mise en œuvre à partir de zéro.


J'espère que vous avez un appétit pour de nouveaux chèques pour Go. Faites-moi savoir combien l'analyse statique devient trop, nous allons résoudre rapidement ce problème ensemble.

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


All Articles