Как отключить конкретное правило контрольного стиля для конкретной строки кода?

В моем проекте настроено правило проверки контрольного стиля, которое запрещает определять методы класса с более чем 3 входными параметрами. Правило отлично работает для моих классов, но иногда мне приходится расширять сторонние классы, которые не подчиняются этому конкретному правилу.

Есть ли возможность проинструктировать "checkstyle", что определенный метод должен игнорироваться?

Кстати, у меня появилась своя собственная оболочка checkstyle: http://www.qulice.com/ (см. Строгий контроль качества кода Java)

9 ответов

Решение

Проверьте использование supressionCommentFilter по адресу http://checkstyle.sourceforge.net/config_filters.html. Вам нужно будет добавить модуль в ваш checkstyle.xml

<module name="SuppressionCommentFilter"/>

и это настраивается. Таким образом, вы можете добавить комментарии к своему коду, чтобы отключить контрольный стиль (на разных уровнях), а затем снова включить его, используя комментарии в вашем коде. Например

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

Или даже лучше, используйте эту более подправленную версию:

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

который позволяет отключить определенные проверки для определенных строк кода:

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

* Примечание: вам также нужно добавить FileContentsHolder:

<module name="FileContentsHolder"/>

Смотрите также

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

в разделе SuppressionFilter на той же странице, который позволяет отключить отдельные проверки для ресурсов, соответствующих шаблону.

Итак, если у вас есть в вашем checkstyle.xml:

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

Вы можете отключить его в вашем XML-файле подавления с помощью:

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

Другой метод, теперь доступный в Checkstyle 5.7, заключается в подавлении нарушений с помощью @SuppressWarnings Java аннотация. Для этого вам понадобятся новые модули (SuppressWarningsFilter и SuppressWarningsHolder) в вашем файле конфигурации:

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

Затем в вашем коде вы можете сделать следующее:

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

или для многократных подавлений:

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

NB: "checkstyle:"Префикс является необязательным (но рекомендуется). Согласно документам, имя параметра должно быть написано строчными буквами, но практика показывает, что любой случай работает.

Если вы предпочитаете использовать аннотации для выборочного молчания правил, теперь это возможно с помощью @SuppressWarnings аннотация, начиная с Checkstyle 5.7 (и поддерживается Checkstyle Maven Plugin 2.12+).

Во-первых, в вашем checkstyle.xml, добавить SuppressWarningsHolder модуль к TreeWalker:

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

Затем включите SuppressWarningsFilter там (как брат TreeWalker):

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

<module name="TreeWalker">
...

Теперь вы можете комментировать, например, метод, который вы хотите исключить из определенного правила Checkstyle:

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

checkstyle: префикс в аргументе @SuppressWarnings необязательно, но мне нравится напоминание, откуда пришло это предупреждение. Имя правила должно быть в нижнем регистре.

Наконец, если вы используете Eclipse, он будет жаловаться на то, что аргумент ему неизвестен:

Неподдерживаемый @SuppressWarnings("checkstyle:methodlength")

Вы можете отключить это предупреждение Eclipse в настройках, если вам нравится:

Preferences:
  Java
  --> Compiler
  --> Errors/Warnings
  --> Annotations
  --> Unhandled token in '@SuppressWarnings': set to 'Ignore'

Что также хорошо работает, так это SuppressWithNearbyCommentFilter, который использует отдельные комментарии для подавления событий аудита.

Например

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

Чтобы настроить фильтр таким образом, чтобы проверка CHECKSTYLE IGNORE FOR NEXT var LINES исключала запуск любых проверок для данной проверки для текущей строки и следующих строк var (всего для строк var+1):

<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

В каждом ответе на SuppressWarningsFilter отсутствует важная деталь. Вы можете использовать только строчный идентификатор, если он определен как таковой в вашем файле checkstyle-config.xml. Если нет, вы должны использовать оригинальное имя модуля.

Например, если в моем checkstyle-config.xml у меня есть:

<module name="NoWhitespaceBefore"/>

Я не могу использовать:

@SuppressWarnings({"nowhitespacebefore"})

Я должен, однако, использовать:

@SuppressWarnings({"NoWhitespaceBefore"})

Чтобы первый синтаксис работал, checkstyle-config.xml должен иметь:

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

Это то, что у меня сработало, по крайней мере, в версии CheckStyle 6.17.

У меня были трудности с ответами выше, возможно потому, что я установил предупреждения checkStyle как ошибки. Что сработало, так это SuppressionFilter: http://checkstyle.sourceforge.net/config_filters.html

Недостатком этого является то, что диапазон строк хранится в отдельном файле suppresssions.xml, поэтому незнакомый разработчик может не сразу установить соединение.

Если вы используете checkstyle из qulicemvn plugin (https://github.com/teamed/qulice), вы можете использовать следующее подавление:

      // @checkstyle <Rulename> (N lines)
... code with violation(s)

или

      
/**
 * ...
 * @checkstyle <Rulename> (N lines)
 * ...
 */
 ... code with violation(s)
<module name="Checker">
    <module name="SuppressionCommentFilter"/>
    <module name="TreeWalker">
        <module name="FileContentsHolder"/>
    </module>
</module>

Чтобы настроить фильтр для подавления событий аудита между комментарием, содержащим строку BEGIN GENERATED CODE, и комментарием, содержащим строку 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

Узнать больше

Вы также можете окружить код, для которого вы хотите отключить конкретное предупреждение, следующими специальными комментариями:

      // CHECKSTYLE:DISABLE:<CheckName>
<Your code goes here>
// CHECKSTYLE:ENABLE:<CheckName>

например: // CHECKSTYLE:DISABLE:ParameterNumberCheck

Вы можете найти полный список поддерживаемых проверок здесь (см. Прямые известные подклассы).

Вы можете попробовать https://checkstyle.sourceforge.io/config_filters.html

Вы можете настроить его как:


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

Сгенерируйте подавление Xpath с помощью CLI с параметром -g и укажите вывод с помощью переключателя -o.

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

Вот фрагмент муравья, который поможет вам настроить автоматическую генерацию подавления Checkstyle; вы можете интегрировать его в Maven с помощью плагина Antrun.


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

Suppressions-xpath.xml указан как источник подавления Xpath в конфигурации правил Checkstyle. В приведенном выше фрагменте я загружаю путь к классам Checkstyle из файла cs.cp в свойство. Вы можете указать путь к классам напрямую.

Или вы можете использовать Groovy в Maven ( или Ant), чтобы сделать то же самое:


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()

Единственный недостаток использования подавления Xpath --- помимо проверок, которые он не поддерживает --- - это наличие у вас следующего кода:

package cstests;

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;
  }
}

Подавление Xpath, сгенерированное в этом случае, не принимается Checkstyle, и программа проверки завершается ошибкой с исключением сгенерированного подавления:

<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']"/>

Генерация подавления Xpath рекомендуется, когда вы устранили все другие нарушения и хотите подавить остальные. Это не позволит вам выбрать определенные экземпляры в коде для подавления. Однако для этого вы можете выбрать подавление из сгенерированного файла.

SuppressionXpathSingleFilter лучше подходит для идентификации и подавления определенного правила, файла или сообщения об ошибке. Вы можете настроить несколько фильтров, идентифицируя каждый по атрибуту id.

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

Другие вопросы по тегам