Ir lintpack: gerenciador de linter composable


O lintpack é um utilitário para a construção de linter (analisadores estáticos), escritos usando a API fornecida. Com base nisso, o analisador estático go- critical, familiar para alguns, agora está sendo reescrito.


Hoje vamos lintpack com mais detalhes o que lintpack o lintpack do ponto de vista do usuário.


No começo era crítico ...


O go-critic começou como um projeto piloto que era uma caixa de areia para prototipar quase todas as idéias de análise estática para o Go.


Uma surpresa agradável foi que algumas pessoas enviaram implementações de detectores de vários problemas no código. Tudo estava sob controle até que uma dívida técnica começou a se acumular, que praticamente não havia ninguém a eliminar. As pessoas entraram, adicionaram a verificação e desapareceram. Quem então precisa corrigir os erros e modificar a implementação?


Um evento significativo foi a proposta de adicionar verificações que requerem configuração adicional, ou seja, aquelas que dependem de arranjos locais para o projeto. Um exemplo é revelar a presença de um cabeçalho de direitos autorais em um arquivo (cabeçalho da licença) usando um modelo especial ou proibir a importação de alguns pacotes com a proposta de uma determinada alternativa.


Outra dificuldade foi a extensibilidade. Não é conveniente que todos enviem seu código ao repositório de outra pessoa. Alguns queriam conectar dinamicamente suas verificações para que não precisassem modificar os códigos go-critic fonte go-critic .


Resumindo, eis os problemas que impediram o desenvolvimento go-critic :


  • Uma carga de complexidade. Muito suporte, a presença de um código sem dono.
  • Baixa qualidade média. experimental significava "quase pronto para usar" e "melhor não funcionar".
  • Às vezes, é difícil decidir se deve incluir a verificação no go-critic e rejeitá-la contradiz a filosofia de design original.
  • Pessoas diferentes viram o go-critic diferente. A maioria queria tê-lo como um linter de CI, que vem com o gometalinter .

Para limitar de alguma forma o número de discrepâncias e interpretações inconsistentes do projeto, um manifesto foi escrito.


Se você deseja um contexto histórico adicional e ainda mais reflexão sobre a categorização de analisadores estáticos, pode ouvir a gravação GoCritic, um novo analisador estático para o Go . Naquele momento, o lintpack ainda não existia, mas algumas das idéias nasceram naquele dia, após o relatório.

Mas e se não precisássemos armazenar todas as verificações em um repositório?


Conheça - lintpack




go-critic consiste em dois componentes principais:


  1. Implementação das próprias verificações.
  2. Um programa que baixa pacotes validados por Go e executa validações neles.

Nosso objetivo: poder armazenar verificações para o linter em diferentes repositórios e coletá-las quando necessário.


O lintpack faz exatamente isso. Ele define funções que permitem descrever suas verificações de forma que você possa executá-las no linter gerado.


Os pacotes implementados usando o lintpack como estrutura serão chamados de lintpack compatíveis com lintpack ou compatíveis com lintpack .

Se o próprio go-critic lintpack foi implementado com base no lintpack , todas as verificações poderiam ser divididas em vários repositórios. Uma das opções de separação pode ser a seguinte:


  1. O conjunto principal onde todas as verificações estáveis ​​e suportadas são obtidas.
  2. contrib repositório onde está o código que é muito experimental ou sem um mantenedor.
  3. Verificações personalizáveis ​​para um projeto específico.

O primeiro ponto é de particular importância em relação à integração do crítico em golangci-lint .


Se você permanecer no nível go-critic , então para os usuários quase nada mudou. lintpack cria um linter quase idêntico, enquanto o golangci-lint encapsula todos os diferentes detalhes de implementação.


Mas algo mudou. Se novos lintpack forem criados com base no lintpack , você terá uma seleção mais rica de diagnósticos prontos para gerar um linter. Imagine por um momento que seja assim, e existem mais de 10 conjuntos diferentes de verificações no mundo.


Início rápido



Para começar, você precisa instalar o próprio lintpack :


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

Crie um linter usando o pacote de teste do lintpack :


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

O conjunto inclui panicNil , que encontra panic(nil) no código e panicNil uma substituição por algo distinguível, porque caso contrário o recover() não será capaz de dizer se o panic foi chamado com argumento nil ou se não houve pânico.


Exemplo com pânico (nulo)


O código abaixo tenta descrever o valor obtido de recover() :


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

O resultado será idêntico para panic(nil) e para um programa que não entre em panic(nil) .


Um exemplo de execução do comportamento descrito .




Você pode iniciar o linter em arquivos separados, com argumentos do tipo ./... ou pacotes (pelo caminho de importação).


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

Por padrão, essa verificação também responde ao panic(interface{}(nil)) . Para substituir esse comportamento, defina skipNilEfaceLit como true . Você pode fazer isso através da linha de comando:


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

uso para cmd / lintpack e linter gerado


O lintpack e o linter gerado usam o primeiro argumento para selecionar um subcomando. A lista de subcomandos disponíveis e exemplos de seu lançamento podem ser obtidos chamando o utilitário sem argumentos.


 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 

Suponha que gocritic o linter criado com o nome 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 

O sinalizador -help está disponível para alguns subcomandos, o que fornece informações adicionais (cortei algumas linhas muito largas):


 ./gocritic check -help #     . 



Documentação de verificações instaladas


A resposta para a pergunta "como descobrir o próprio parâmetro skipNilEfaceLit?" - leia o manual chique (RTFM)!


Toda a documentação sobre as verificações instaladas está dentro do mylinter . Esta documentação está disponível através do subcomando 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) 

Assim como o suporte a modelos na go list -f , você pode passar uma linha de modelo responsável pelo formato de saída da documentação, que pode ser útil ao gravar documentos de remarcação.


Onde procurar verificações para instalação?


Para simplificar a busca por suítes de lintpack úteis, existe uma lista centralizada de pacotes compatíveis com o lintpack : https://go-lintpack.imtqy.com/ .


Aqui estão algumas da lista:



Esta lista é atualizada periodicamente e está aberta para solicitações de adição. Qualquer um desses pacotes pode ser usado para criar um linter.


O comando abaixo cria um linter que contém todas as verificações da lista acima:


 #   ,      #   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 inclui todas as verificações no estágio de compilação, o linter resultante pode ser colocado em um ambiente em que não há códigos-fonte para a implementação dos diagnósticos instalados; tudo é como de costume com o link estático.


Anexo de pacote dinâmico


Além da montagem estática, é possível carregar plug-ins que fornecem verificações adicionais.


A peculiaridade é que a implementação do verificador não sabe se será usada para compilação estática ou se será carregada como um plug-in. Nenhuma alteração no código é necessária.


Suponha que desejemos adicionar panicNil ao linter, mas não podemos reconstruí-lo a partir de todas as fontes usadas durante a primeira compilação.


  1. Crie linterPlugin.go :

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

  1. Crie uma biblioteca dinâmica:

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

  1. Execute o linter com o parâmetro -pluginPath :

 ./linter check -pluginPath=linterPlugin.so bytes 

Aviso: O suporte para módulos dinâmicos é implementado por meio de um pacote de plugins que não funciona no Windows.

O sinalizador -verbose pode ajudá-lo a descobrir qual verificação está ativada ou desativada e, o mais importante, mostrará qual filtro desativou a verificação.


Exemplo com -verbose


Observe que panicNil exibido na lista de verificações incluídas. Se removermos o argumento -pluginPath, ele deixará de ser verdadeiro.


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



Comparação com gometalinter e golangci-lint


Para evitar confusão, vale a pena descrever as principais diferenças entre os projetos.


O gometalinter e o golangci-lint integram principalmente outros linters , geralmente implementados de maneira muito diferente, proporcionando acesso conveniente a eles. Eles têm como alvo usuários finais que usarão analisadores estáticos.


O lintpack simplifica a criação de novos linters, fornece uma estrutura que torna diferentes pacotes, implementados em sua base, compatíveis dentro do mesmo arquivo executável. Essas verificações (para golangci-lint) ou o arquivo executável (para gometalinter) podem ser incorporadas nos meta-linters mencionados acima.


Suponha que uma das verificações compatíveis com o lintpack parte do golangci-lint . Se houver algum problema relacionado à sua usabilidade, isso pode ser de responsabilidade do golangci-lint , mas se houver um erro na implementação da verificação em si, esse é o problema dos autores do ecossistema de verificação, lintpack.


Em outras palavras, esses projetos resolvem vários problemas.


E o crítico?


O processo de portar o go-critic para o lintpack já foi concluído; os verificadores podem ser encontrados no repositório do 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 

Não faz sentido usar o go-critic fora do golangci-lint , mas o lintpack pode permitir que você instale as verificações que não estão incluídas no go-critic . Por exemplo, esses podem ser diagnósticos escritos por você.


Para ser continuado


Você aprenderá como criar suas próprias verificações compatíveis com o lintpack no próximo artigo.


Lá, analisaremos quais vantagens você obtém ao implementar seu lintpack baseado em lintpack em comparação com a implementação do zero.


Espero que você tenha um apetite por novos cheques para o Go. Deixe-me saber o quanto a análise estática se torna demais, resolveremos rapidamente esse problema juntos.

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


All Articles