Como desativar uma regra de estilo de verificação específica para uma linha de código específica?

183

Eu tenho um checkstyle regra de validação configurado no meu projeto, que proíbe a definir métodos de classe com mais de 3 parâmetros de entrada. A regra funciona bem para minhas classes, mas às vezes tenho que estender classes de terceiros, que não obedecem a essa regra específica.

Existe a possibilidade de instruir o "estilo de verificação" que um determinado método deve ser ignorado silenciosamente?

BTW, acabei com meu próprio wrapper de checkstyle: qulice.com (consulte Controle estrito da qualidade do código Java )

yegor256
fonte

Respostas:

291

Confira o uso do supressionCommentFilter em http://checkstyle.sourceforge.net/config_filters.html#SuppressionCommentFilter . Você precisará adicionar o módulo ao seu checkstyle.xml

<module name="SuppressionCommentFilter"/>

e é configurável. Assim, você pode adicionar comentários ao seu código para desativar o estilo de verificação (em vários níveis) e, em seguida, reativar novamente usando os comentários no seu código. Por exemplo

//CHECKSTYLE:OFF
public void someMethod(String arg1, String arg2, String arg3, String arg4) {
//CHECKSTYLE:ON

Ou melhor ainda, use esta versão mais aprimorada:

<module name="SuppressionCommentFilter">
    <property name="offCommentFormat" value="CHECKSTYLE.OFF\: ([\w\|]+)"/>
    <property name="onCommentFormat" value="CHECKSTYLE.ON\: ([\w\|]+)"/>
    <property name="checkFormat" value="$1"/>
</module>

que permite desativar verificações específicas para linhas de código específicas:

//CHECKSTYLE.OFF: IllegalCatch - Much more readable than catching 7 exceptions
catch (Exception e)
//CHECKSTYLE.ON: IllegalCatch

* Nota: você também precisará adicionar o FileContentsHolder:

<module name="FileContentsHolder"/>

Veja também

<module name="SuppressionFilter">
    <property name="file" value="docs/suppressions.xml"/>
</module>

debaixo de SuppressionFilter seção na mesma página, que permite desativar verificações individuais de recursos correspondentes ao padrão.

Portanto, se você possui seu checkstyle.xml:

<module name="ParameterNumber">
   <property name="id" value="maxParameterNumber"/>
   <property name="max" value="3"/>
   <property name="tokens" value="METHOD_DEF"/>
</module>

Você pode desativá-lo no seu arquivo xml de supressão com:

<suppress id="maxParameterNumber" files="YourCode.java"/>

Outro método, agora disponível no Checkstyle 5.7, é suprimir violações por meio da @SuppressWarningsanotação java. Para fazer isso, você precisará adicionar dois novos módulos ( SuppressWarningsFiltere SuppressWarningsHolder) no seu arquivo de configuração:

<module name="Checker">
   ...
   <module name="SuppressWarningsFilter" />
   <module name="TreeWalker">
       ...
       <module name="SuppressWarningsHolder" />
   </module>
</module> 

Em seguida, dentro do seu código, você pode fazer o seguinte:

@SuppressWarnings("checkstyle:methodlength")
public void someLongMethod() throws Exception {

ou, para várias supressões:

@SuppressWarnings({"checkstyle:executablestatementcount", "checkstyle:methodlength"})
public void someLongMethod() throws Exception {

NB: O checkstyle:prefixo " " é opcional (mas recomendado). De acordo com os documentos, o nome do parâmetro deve estar em letras minúsculas, mas a prática indica que qualquer caso funciona.

Chris Knight
fonte
7
Lembre-se de adicionar o FileContentsHolder no TreeWalter. Veja stackoverflow.com/a/5764666/480483
djjeck
2
se você usar //CHECKSTYLE.OFF: e depois esquecer de ativá-lo novamente, ele permanecerá desmarcado apenas no arquivo que contém //CHECKSTYLE.OFF: ou em todos os arquivos processados ​​posteriormente também?
Roland
1
@Roland, permanece desativado apenas pela duração dessa classe de teste.
22614 Chris Cavaleiro
1
"o nome do parâmetro deve estar em minúsculas." @SuppressWarnings("checkstyle:VariableDeclarationUsageDistance")funcionou tão bem para mim quanto o equivalente em minúsculas.
Anders Rabo Thorbeck
2
Desde o checkstyle 8.1, o SuppressionCommentFilter deve estar sob o TreeWalkere FileContentHoldernão é mais necessário (disponível).
precisa saber é o seguinte
70

Se você preferir usar anotações para silenciar seletivamente as regras, agora é possível usar a @SuppressWarningsanotação, começando com o Checkstyle 5.7 (e suportado pelo Checkstyle Maven Plugin 2.12+).

Primeiro, no seu checkstyle.xml, adicione o SuppressWarningsHoldermódulo ao TreeWalker:

<module name="TreeWalker">
    <!-- Make the @SuppressWarnings annotations available to Checkstyle -->
    <module name="SuppressWarningsHolder" />
</module>

Em seguida, ative SuppressWarningsFilterlá (como um irmão TreeWalker):

<!-- Filter out Checkstyle warnings that have been suppressed with the @SuppressWarnings annotation -->
<module name="SuppressWarningsFilter" />

<module name="TreeWalker">
...

Agora você pode anotar, por exemplo, o método que deseja excluir de uma determinada regra do Checkstyle:

@SuppressWarnings("checkstyle:methodlength")
@Override
public boolean equals(Object obj) {
    // very long auto-generated equals() method
}

O checkstyle:prefixo no argumento para @SuppressWarningsé opcional, mas eu gosto disso como um lembrete de onde esse aviso veio. O nome da regra deve estar em minúsculas.

Por fim, se você estiver usando o Eclipse, ele reclamará sobre o argumento desconhecido:

@SuppressWarnings não suportados ("checkstyle: methodlength")

Você pode desativar este aviso do Eclipse nas preferências, se desejar:

Preferences:
  Java
  --> Compiler
  --> Errors/Warnings
  --> Annotations
  --> Unhandled token in '@SuppressWarnings': set to 'Ignore'
Henrik Heimbuerger
fonte
2
Eu o nomeio como a resposta verificada, pois acho que essa é a solução que deve funcionar melhor na maioria dos casos.
Avandeursen
33

O que também funciona bem é o SuppressWithNearbyCommentFilter, que usa comentários individuais para suprimir eventos de auditoria.

Por exemplo

// CHECKSTYLE IGNORE check FOR NEXT 1 LINES
public void onClick(View view) { ... }

Para configurar um filtro para que CHECKSTYLE IGNORE verifique NEXT var LINES, evite acionar auditorias para a verificação fornecida da linha atual e das próximas linhas var (para um total de var + 1 linhas):

<module name="SuppressWithNearbyCommentFilter">
    <property name="commentFormat" value="CHECKSTYLE IGNORE (\w+) FOR NEXT (\d+) LINES"/>
    <property name="checkFormat" value="$1"/>
    <property name="influenceFormat" value="$2"/>
</module>

http://checkstyle.sourceforge.net/config.html

Akos Cz
fonte
Eu mudaria o regex para o CHECKSTYLE IGNORE (\w+) FOR NEXT (\d+) LINES?qual tornará o comando ignore mais legível. (Você poderá usar "CHECKSTYLE IGNORE, verifique a próxima linha 1" e "CHECKSTYLE IGNORE, verifique a próxima linha 3").
Matt3o12
@ matt3o12 CHECKSTYLE IGNORE (\w+) FOR NEXT (\d+) LINEtambém funciona para mim (combina ambos linee lines).
Slava Semushin
3

Todas as respostas referentes a SuppressWarningsFilter está faltando um detalhe importante. Você só pode usar o ID com todas as letras minúsculas se ele estiver definido como tal no seu checkstyle-config.xml. Caso contrário, você deve usar o nome do módulo original.

Por exemplo, se no meu checkstyle-config.xml eu tenho:

<module name="NoWhitespaceBefore"/>

Eu não posso usar:

@SuppressWarnings({"nowhitespacebefore"})

No entanto, devo usar:

@SuppressWarnings({"NoWhitespaceBefore"})

Para que a primeira sintaxe funcione, o checkstyle-config.xml deve ter:

<module name="NoWhitespaceBefore">
  <property name="id" value="nowhitespacebefore"/>
</module>

Foi isso que funcionou para mim, pelo menos na versão 6.17 do CheckStyle.

João Baltazar
fonte
1

Tive dificuldade com as respostas acima, potencialmente porque defini os avisos checkStyle como erros. O que funcionou foi SuppressionFilter: http://checkstyle.sourceforge.net/config_filters.html#SuppressionFilter

A desvantagem disso é que o intervalo de linhas é armazenado em um arquivo suppresssions.xml separado, portanto, um desenvolvedor desconhecido pode não fazer a conexão imediatamente.

Saltymule
fonte
Obrigado, era a única coisa que funcionou para mim também
jonathanrz
1
<module name="Checker">
    <module name="SuppressionCommentFilter"/>
    <module name="TreeWalker">
        <module name="FileContentsHolder"/>
    </module>
</module>

Para configurar um filtro para suprimir eventos de auditoria entre um comentário contendo a linha BEGIN GENERATED CODE e um comentário contendo a linha END GENERATED CODE:

<module name="SuppressionCommentFilter">
  <property name="offCommentFormat" value="BEGIN GENERATED CODE"/>
  <property name="onCommentFormat" value="END GENERATED CODE"/>
</module>

//BEGIN GENERATED CODE
@Override
public boolean equals(Object obj) { ... } // No violation events will be reported

@Override
public int hashCode() { ... } // No violation events will be reported
//END GENERATED CODE

Ver mais

Roberto
fonte
0

Você pode experimentar https://checkstyle.sourceforge.io/config_filters.html#SuppressionXpathFilter

Você pode configurá-lo como:


<module name="SuppressionXpathFilter">
  <property name="file" value="config/suppressions-xpath.xml"/>
  <property name="optional" value="false"/>
</module>
        

Gere supressões do Xpath usando a CLI com a opção -g e especifique a saída usando a opção -o.

https://checkstyle.sourceforge.io/cmdline.html#Command_line_usage

Aqui está um trecho de formiga que o ajudará a configurar sua geração automática de supressões Checkstyle:


<target name="checkstyleg">
    <move file="suppressions-xpath.xml"
      tofile="suppressions-xpath.xml.bak"
      preservelastmodified="true"
      force="true"
      failonerror="false"
      verbose="true"/>
    <fileset dir="${basedir}"
                    id="javasrcs">
    <include name="**/*.java" />
    </fileset>
    <pathconvert property="sources"
                            refid="javasrcs"
                            pathsep=" " />
    <loadfile property="cs.cp"
                        srcFile="../${cs.classpath.file}" />
    <java classname="${cs.main.class}"
                logError="true">
    <arg line="-c ../${cs.config} -p ${cs.properties} -o ${ant.project.name}-xpath.xml -g ${sources}" />
    <classpath>
        <pathelement path="${cs.cp}" />
        <pathelement path="${java.class.path}" />
    </classpath>
</java>
<condition property="file.is.empty" else="false">
     <length file="${ant.project.name}-xpath.xml" when="equal" length="0" />
   </condition>
   <if>
     <equals arg1="${file.is.empty}" arg2="false"/>
     <then>
     <move file="${ant.project.name}-xpath.xml"
      tofile="suppressions-xpath.xml"
      preservelastmodified="true"
      force="true"
      failonerror="true"
  verbose="true"/>
   </then>
</if>
    </target>

O suppressions-xpath.xml é especificado como a fonte de supressões do Xpath na configuração de regras do Checkstyle. No snippet acima, estou carregando o caminho de classe Checkstyle de um arquivo cs.cp em uma propriedade. Você pode optar por especificar o caminho de classe diretamente.

Ou você pode usar groovy no Maven (ou Ant) para fazer o mesmo:


import java.nio.file.Files
import java.nio.file.StandardCopyOption  
import java.nio.file.Paths

def backupSuppressions() {
  def supprFileName = 
      project.properties["checkstyle.suppressionsFile"]
  def suppr = Paths.get(supprFileName)
  def target = null
  if (Files.exists(suppr)) {
    def supprBak = Paths.get(supprFileName + ".bak")
    target = Files.move(suppr, supprBak,
        StandardCopyOption.REPLACE_EXISTING)
    println "Backed up " + supprFileName
  }
  return target
}

def renameSuppressions() {
  def supprFileName = 
      project.properties["checkstyle.suppressionsFile"]
  def suppr = Paths.get(project.name + "-xpath.xml")
  def target = null
  if (Files.exists(suppr)) {
    def supprNew = Paths.get(supprFileName)
    target = Files.move(suppr, supprNew)
    println "Renamed " + suppr + " to " + supprFileName
  }
  return target
}

def getClassPath(classLoader, sb) {
  classLoader.getURLs().each {url->
     sb.append("${url.getFile().toString()}:")
  }
  if (classLoader.parent) {
     getClassPath(classLoader.parent, sb)
  }
  return sb.toString()
}

backupSuppressions()

def cp = getClassPath(this.class.classLoader, 
    new StringBuilder())
def csMainClass = 
      project.properties["cs.main.class"]
def csRules = 
      project.properties["checkstyle.rules"]
def csProps = 
      project.properties["checkstyle.properties"]

String[] args = ["java", "-cp", cp,
    csMainClass,
    "-c", csRules,
"-p", csProps,
"-o", project.name + "-xpath.xml",
"-g", "src"]

ProcessBuilder pb = new ProcessBuilder(args)
pb = pb.inheritIO()
Process proc = pb.start()
proc.waitFor()

renameSuppressions()

A única desvantagem do uso de supressões do Xpath - além das verificações que ele não suporta - é se você tiver um código como o seguinte:

package cstests;

@SuppressWarnings("PMD")
public interface TestMagicNumber {
  static byte[] getAsciiRotator() {
    byte[] rotation = new byte[95 * 2];
    for (byte i = ' '; i <= '~'; i++) {
      rotation[i - ' '] = i;
      rotation[i + 95 - ' '] = i;
    }
    return rotation;
  }
}

A supressão do Xpath gerada nesse caso não é ingerida pelo Checkstyle e o verificador falha com uma exceção:

<suppress-xpath
       files="TestMagicNumber.java"
       checks="MagicNumberCheck"
       query="/INTERFACE_DEF[./IDENT[@text='TestMagicNumber']]/OBJBLOCK/METHOD_DEF[./IDENT[@text='getAsciiRotator']]/SLIST/LITERAL_FOR/SLIST/EXPR/ASSIGN[./IDENT[@text='i']]/INDEX_OP[./IDENT[@text='rotation']]/EXPR/MINUS[./CHAR_LITERAL[@text='' '']]/PLUS[./IDENT[@text='i']]/NUM_INT[@text='95']"/>

É recomendável gerar supressões do Xpath quando você corrige todas as outras violações e deseja suprimir o restante. Não permitirá que você selecione instâncias específicas no código para suprimir. No entanto, você pode escolher supressões do arquivo gerado para fazer exatamente isso.

Para especificar uma regra, arquivo ou mensagem de erro específica, o SuppressionXpathSingleFilter seria mais adequado.

https://checkstyle.sourceforge.io/config_filters.html#SuppressionXpathSingleFilter

fernal73
fonte