Coverage Report - com.puppycrawl.tools.checkstyle.checks.modifier.RedundantModifierCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
RedundantModifierCheck
100%
141/141
100%
110/110
3.35
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 the original author or authors.
 4  
 //
 5  
 // This library is free software; you can redistribute it and/or
 6  
 // modify it under the terms of the GNU Lesser General Public
 7  
 // License as published by the Free Software Foundation; either
 8  
 // version 2.1 of the License, or (at your option) any later version.
 9  
 //
 10  
 // This library is distributed in the hope that it will be useful,
 11  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // Lesser General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU Lesser General Public
 16  
 // License along with this library; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 ////////////////////////////////////////////////////////////////////////////////
 19  
 
 20  
 package com.puppycrawl.tools.checkstyle.checks.modifier;
 21  
 
 22  
 import java.util.ArrayList;
 23  
 import java.util.List;
 24  
 
 25  
 import com.puppycrawl.tools.checkstyle.StatelessCheck;
 26  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 27  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 28  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 29  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 30  
 
 31  
 /**
 32  
  * Checks for redundant modifiers in interface and annotation definitions,
 33  
  * final modifier on methods of final classes, inner {@code interface}
 34  
  * declarations that are declared as {@code static}, non public class
 35  
  * constructors and enum constructors, nested enum definitions that are declared
 36  
  * as {@code static}.
 37  
  *
 38  
  * <p>Interfaces by definition are abstract so the {@code abstract}
 39  
  * modifier on the interface is redundant.
 40  
  *
 41  
  * <p>Classes inside of interfaces by definition are public and static,
 42  
  * so the {@code public} and {@code static} modifiers
 43  
  * on the inner classes are redundant. On the other hand, classes
 44  
  * inside of interfaces can be abstract or non abstract.
 45  
  * So, {@code abstract} modifier is allowed.
 46  
  *
 47  
  * <p>Fields in interfaces and annotations are automatically
 48  
  * public, static and final, so these modifiers are redundant as
 49  
  * well.</p>
 50  
  *
 51  
  * <p>As annotations are a form of interface, their fields are also
 52  
  * automatically public, static and final just as their
 53  
  * annotation fields are automatically public and abstract.</p>
 54  
  *
 55  
  * <p>Enums by definition are static implicit subclasses of java.lang.Enum&#60;E&#62;.
 56  
  * So, the {@code static} modifier on the enums is redundant. In addition,
 57  
  * if enum is inside of interface, {@code public} modifier is also redundant.</p>
 58  
  *
 59  
  * <p>Enums can also contain abstract methods and methods which can be overridden by the declared
 60  
  * enumeration fields.
 61  
  * See the following example:</p>
 62  
  * <pre>
 63  
  * public enum EnumClass {
 64  
  *    FIELD_1,
 65  
  *    FIELD_2 {
 66  
  *        &#64;Override
 67  
  *        public final void method1() {} // violation expected
 68  
  *    };
 69  
  *
 70  
  *    public void method1() {}
 71  
  *    public final void method2() {} // no violation expected
 72  
  * }
 73  
  * </pre>
 74  
  *
 75  
  * <p>Since these methods can be overridden in these situations, the final methods are not
 76  
  * marked as redundant even though they can't be extended by other classes/enums.</p>
 77  
  *
 78  
  * <p>Final classes by definition cannot be extended so the {@code final}
 79  
  * modifier on the method of a final class is redundant.
 80  
  *
 81  
  * <p>Public modifier for constructors in non-public non-protected classes
 82  
  * is always obsolete: </p>
 83  
  *
 84  
  * <pre>
 85  
  * public class PublicClass {
 86  
  *     public PublicClass() {} // OK
 87  
  * }
 88  
  *
 89  
  * class PackagePrivateClass {
 90  
  *     public PackagePrivateClass() {} // violation expected
 91  
  * }
 92  
  * </pre>
 93  
  *
 94  
  * <p>There is no violation in the following example,
 95  
  * because removing public modifier from ProtectedInnerClass
 96  
  * constructor will make this code not compiling: </p>
 97  
  *
 98  
  * <pre>
 99  
  * package a;
 100  
  * public class ClassExample {
 101  
  *     protected class ProtectedInnerClass {
 102  
  *         public ProtectedInnerClass () {}
 103  
  *     }
 104  
  * }
 105  
  *
 106  
  * package b;
 107  
  * import a.ClassExample;
 108  
  * public class ClassExtending extends ClassExample {
 109  
  *     ProtectedInnerClass pc = new ProtectedInnerClass();
 110  
  * }
 111  
  * </pre>
 112  
  *
 113  
  * @author lkuehne
 114  
  * @author <a href="mailto:piotr.listkiewicz@gmail.com">liscju</a>
 115  
  * @author <a href="mailto:andreyselkin@gmail.com">Andrei Selkin</a>
 116  
  * @author Vladislav Lisetskiy
 117  
  */
 118  
 @StatelessCheck
 119  25
 public class RedundantModifierCheck
 120  
     extends AbstractCheck {
 121  
 
 122  
     /**
 123  
      * A key is pointing to the warning message text in "messages.properties"
 124  
      * file.
 125  
      */
 126  
     public static final String MSG_KEY = "redundantModifier";
 127  
 
 128  
     /**
 129  
      * An array of tokens for interface modifiers.
 130  
      */
 131  1
     private static final int[] TOKENS_FOR_INTERFACE_MODIFIERS = {
 132  
         TokenTypes.LITERAL_STATIC,
 133  
         TokenTypes.ABSTRACT,
 134  
     };
 135  
 
 136  
     @Override
 137  
     public int[] getDefaultTokens() {
 138  22
         return getAcceptableTokens();
 139  
     }
 140  
 
 141  
     @Override
 142  
     public int[] getRequiredTokens() {
 143  26
         return CommonUtils.EMPTY_INT_ARRAY;
 144  
     }
 145  
 
 146  
     @Override
 147  
     public int[] getAcceptableTokens() {
 148  29
         return new int[] {
 149  
             TokenTypes.METHOD_DEF,
 150  
             TokenTypes.VARIABLE_DEF,
 151  
             TokenTypes.ANNOTATION_FIELD_DEF,
 152  
             TokenTypes.INTERFACE_DEF,
 153  
             TokenTypes.CTOR_DEF,
 154  
             TokenTypes.CLASS_DEF,
 155  
             TokenTypes.ENUM_DEF,
 156  
             TokenTypes.RESOURCE,
 157  
         };
 158  
     }
 159  
 
 160  
     @Override
 161  
     public void visitToken(DetailAST ast) {
 162  150
         if (ast.getType() == TokenTypes.INTERFACE_DEF) {
 163  17
             checkInterfaceModifiers(ast);
 164  
         }
 165  133
         else if (ast.getType() == TokenTypes.ENUM_DEF) {
 166  16
             checkEnumDef(ast);
 167  
         }
 168  
         else {
 169  117
             if (ast.getType() == TokenTypes.CTOR_DEF) {
 170  18
                 if (isEnumMember(ast)) {
 171  5
                     checkEnumConstructorModifiers(ast);
 172  
                 }
 173  
                 else {
 174  13
                     checkClassConstructorModifiers(ast);
 175  
                 }
 176  
             }
 177  99
             else if (ast.getType() == TokenTypes.METHOD_DEF) {
 178  46
                 processMethods(ast);
 179  
             }
 180  53
             else if (ast.getType() == TokenTypes.RESOURCE) {
 181  4
                 processResources(ast);
 182  
             }
 183  
 
 184  117
             if (isInterfaceOrAnnotationMember(ast)) {
 185  36
                 processInterfaceOrAnnotation(ast);
 186  
             }
 187  
         }
 188  150
     }
 189  
 
 190  
     /**
 191  
      * Checks if interface has proper modifiers.
 192  
      * @param ast interface to check
 193  
      */
 194  
     private void checkInterfaceModifiers(DetailAST ast) {
 195  17
         final DetailAST modifiers =
 196  17
             ast.findFirstToken(TokenTypes.MODIFIERS);
 197  
 
 198  51
         for (final int tokenType : TOKENS_FOR_INTERFACE_MODIFIERS) {
 199  34
             final DetailAST modifier =
 200  34
                     modifiers.findFirstToken(tokenType);
 201  34
             if (modifier != null) {
 202  4
                 log(modifier.getLineNo(), modifier.getColumnNo(),
 203  2
                         MSG_KEY, modifier.getText());
 204  
             }
 205  
         }
 206  17
     }
 207  
 
 208  
     /**
 209  
      * Check if enum constructor has proper modifiers.
 210  
      * @param ast constructor of enum
 211  
      */
 212  
     private void checkEnumConstructorModifiers(DetailAST ast) {
 213  5
         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
 214  5
         final DetailAST modifier = getFirstModifierAst(modifiers);
 215  
 
 216  5
         if (modifier != null) {
 217  4
             log(modifier.getLineNo(), modifier.getColumnNo(),
 218  2
                     MSG_KEY, modifier.getText());
 219  
         }
 220  5
     }
 221  
 
 222  
     /**
 223  
      * Retrieves the first modifier that is not an annotation.
 224  
      * @param modifiers The ast to examine.
 225  
      * @return The first modifier or {@code null} if none found.
 226  
      */
 227  
     private static DetailAST getFirstModifierAst(DetailAST modifiers) {
 228  5
         DetailAST modifier = modifiers.getFirstChild();
 229  
 
 230  7
         while (modifier != null && modifier.getType() == TokenTypes.ANNOTATION) {
 231  2
             modifier = modifier.getNextSibling();
 232  
         }
 233  
 
 234  5
         return modifier;
 235  
     }
 236  
 
 237  
     /**
 238  
      * Checks whether enum has proper modifiers.
 239  
      * @param ast enum definition.
 240  
      */
 241  
     private void checkEnumDef(DetailAST ast) {
 242  16
         if (isInterfaceOrAnnotationMember(ast)) {
 243  5
             processInterfaceOrAnnotation(ast);
 244  
         }
 245  11
         else if (ast.getParent() != null) {
 246  4
             checkForRedundantModifier(ast, TokenTypes.LITERAL_STATIC);
 247  
         }
 248  16
     }
 249  
 
 250  
     /**
 251  
      * Do validation of interface of annotation.
 252  
      * @param ast token AST
 253  
      */
 254  
     private void processInterfaceOrAnnotation(DetailAST ast) {
 255  41
         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
 256  41
         DetailAST modifier = modifiers.getFirstChild();
 257  50
         while (modifier != null) {
 258  
 
 259  
             // javac does not allow final or static in interface methods
 260  
             // order annotation fields hence no need to check that this
 261  
             // is not a method or annotation field
 262  
 
 263  26
             final int type = modifier.getType();
 264  26
             if (type == TokenTypes.LITERAL_PUBLIC
 265  
                 || type == TokenTypes.LITERAL_STATIC
 266  7
                         && ast.getType() != TokenTypes.METHOD_DEF
 267  
                 || type == TokenTypes.ABSTRACT
 268  3
                         && ast.getType() != TokenTypes.CLASS_DEF
 269  
                 || type == TokenTypes.FINAL
 270  5
                         && ast.getType() != TokenTypes.CLASS_DEF) {
 271  34
                 log(modifier.getLineNo(), modifier.getColumnNo(),
 272  17
                         MSG_KEY, modifier.getText());
 273  17
                 break;
 274  
             }
 275  
 
 276  9
             modifier = modifier.getNextSibling();
 277  9
         }
 278  41
     }
 279  
 
 280  
     /**
 281  
      * Process validation of Methods.
 282  
      * @param ast method AST
 283  
      */
 284  
     private void processMethods(DetailAST ast) {
 285  46
         final DetailAST modifiers =
 286  46
                         ast.findFirstToken(TokenTypes.MODIFIERS);
 287  
         // private method?
 288  46
         boolean checkFinal =
 289  46
             modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) != null;
 290  
         // declared in a final class?
 291  46
         DetailAST parent = ast.getParent();
 292  128
         while (parent != null && !checkFinal) {
 293  82
             if (parent.getType() == TokenTypes.CLASS_DEF) {
 294  16
                 final DetailAST classModifiers =
 295  16
                     parent.findFirstToken(TokenTypes.MODIFIERS);
 296  16
                 checkFinal = classModifiers.findFirstToken(TokenTypes.FINAL) != null;
 297  16
                 parent = null;
 298  16
             }
 299  66
             else if (parent.getType() == TokenTypes.LITERAL_NEW
 300  64
                     || parent.getType() == TokenTypes.ENUM_CONSTANT_DEF) {
 301  7
                 checkFinal = true;
 302  7
                 parent = null;
 303  
             }
 304  59
             else if (parent.getType() == TokenTypes.ENUM_DEF) {
 305  8
                 checkFinal = modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
 306  8
                 parent = null;
 307  
             }
 308  
             else {
 309  51
                 parent = parent.getParent();
 310  
             }
 311  
         }
 312  46
         if (checkFinal && !isAnnotatedWithSafeVarargs(ast)) {
 313  21
             checkForRedundantModifier(ast, TokenTypes.FINAL);
 314  
         }
 315  
 
 316  46
         if (ast.findFirstToken(TokenTypes.SLIST) == null) {
 317  14
             processAbstractMethodParameters(ast);
 318  
         }
 319  46
     }
 320  
 
 321  
     /**
 322  
      * Process validation of parameters for Methods with no definition.
 323  
      * @param ast method AST
 324  
      */
 325  
     private void processAbstractMethodParameters(DetailAST ast) {
 326  14
         final DetailAST parameters = ast.findFirstToken(TokenTypes.PARAMETERS);
 327  
 
 328  24
         for (DetailAST child = parameters.getFirstChild(); child != null; child = child
 329  10
                 .getNextSibling()) {
 330  10
             if (child.getType() == TokenTypes.PARAMETER_DEF) {
 331  9
                 checkForRedundantModifier(child, TokenTypes.FINAL);
 332  
             }
 333  
         }
 334  14
     }
 335  
 
 336  
     /**
 337  
      * Check if class constructor has proper modifiers.
 338  
      * @param classCtorAst class constructor ast
 339  
      */
 340  
     private void checkClassConstructorModifiers(DetailAST classCtorAst) {
 341  13
         final DetailAST classDef = classCtorAst.getParent().getParent();
 342  13
         if (!isClassPublic(classDef) && !isClassProtected(classDef)) {
 343  7
             checkForRedundantModifier(classCtorAst, TokenTypes.LITERAL_PUBLIC);
 344  
         }
 345  13
     }
 346  
 
 347  
     /**
 348  
      * Checks if given resource has redundant modifiers.
 349  
      * @param ast ast
 350  
      */
 351  
     private void processResources(DetailAST ast) {
 352  4
         checkForRedundantModifier(ast, TokenTypes.FINAL);
 353  4
     }
 354  
 
 355  
     /**
 356  
      * Checks if given ast has a redundant modifier.
 357  
      * @param ast ast
 358  
      * @param modifierType The modifier to check for.
 359  
      */
 360  
     private void checkForRedundantModifier(DetailAST ast, int modifierType) {
 361  45
         final DetailAST astModifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
 362  45
         DetailAST astModifier = astModifiers.getFirstChild();
 363  106
         while (astModifier != null) {
 364  61
             if (astModifier.getType() == modifierType) {
 365  42
                 log(astModifier.getLineNo(), astModifier.getColumnNo(),
 366  21
                         MSG_KEY, astModifier.getText());
 367  
             }
 368  
 
 369  61
             astModifier = astModifier.getNextSibling();
 370  
         }
 371  45
     }
 372  
 
 373  
     /**
 374  
      * Checks if given class ast has protected modifier.
 375  
      * @param classDef class ast
 376  
      * @return true if class is protected, false otherwise
 377  
      */
 378  
     private static boolean isClassProtected(DetailAST classDef) {
 379  8
         final DetailAST classModifiers =
 380  8
                 classDef.findFirstToken(TokenTypes.MODIFIERS);
 381  8
         return classModifiers.findFirstToken(TokenTypes.LITERAL_PROTECTED) != null;
 382  
     }
 383  
 
 384  
     /**
 385  
      * Checks if given class is accessible from "public" scope.
 386  
      * @param ast class def to check
 387  
      * @return true if class is accessible from public scope,false otherwise
 388  
      */
 389  
     private static boolean isClassPublic(DetailAST ast) {
 390  26
         boolean isAccessibleFromPublic = false;
 391  26
         final boolean isMostOuterScope = ast.getParent() == null;
 392  26
         final DetailAST modifiersAst = ast.findFirstToken(TokenTypes.MODIFIERS);
 393  26
         final boolean hasPublicModifier =
 394  26
                 modifiersAst.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null;
 395  
 
 396  26
         if (isMostOuterScope) {
 397  8
             isAccessibleFromPublic = hasPublicModifier;
 398  
         }
 399  
         else {
 400  18
             final DetailAST parentClassAst = ast.getParent().getParent();
 401  
 
 402  18
             if (hasPublicModifier || parentClassAst.getType() == TokenTypes.INTERFACE_DEF) {
 403  13
                 isAccessibleFromPublic = isClassPublic(parentClassAst);
 404  
             }
 405  
         }
 406  
 
 407  26
         return isAccessibleFromPublic;
 408  
     }
 409  
 
 410  
     /**
 411  
      * Checks if current AST node is member of Enum.
 412  
      * @param ast AST node
 413  
      * @return true if it is an enum member
 414  
      */
 415  
     private static boolean isEnumMember(DetailAST ast) {
 416  18
         final DetailAST parentTypeDef = ast.getParent().getParent();
 417  18
         return parentTypeDef.getType() == TokenTypes.ENUM_DEF;
 418  
     }
 419  
 
 420  
     /**
 421  
      * Checks if current AST node is member of Interface or Annotation, not of their subnodes.
 422  
      * @param ast AST node
 423  
      * @return true or false
 424  
      */
 425  
     private static boolean isInterfaceOrAnnotationMember(DetailAST ast) {
 426  133
         DetailAST parentTypeDef = ast.getParent();
 427  
 
 428  133
         if (parentTypeDef != null) {
 429  113
             parentTypeDef = parentTypeDef.getParent();
 430  
         }
 431  266
         return parentTypeDef != null
 432  113
                 && (parentTypeDef.getType() == TokenTypes.INTERFACE_DEF
 433  78
                     || parentTypeDef.getType() == TokenTypes.ANNOTATION_DEF);
 434  
     }
 435  
 
 436  
     /**
 437  
      * Checks if method definition is annotated with.
 438  
      * <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/SafeVarargs.html">
 439  
      * SafeVarargs</a> annotation
 440  
      * @param methodDef method definition node
 441  
      * @return true or false
 442  
      */
 443  
     private static boolean isAnnotatedWithSafeVarargs(DetailAST methodDef) {
 444  23
         boolean result = false;
 445  23
         final List<DetailAST> methodAnnotationsList = getMethodAnnotationsList(methodDef);
 446  23
         for (DetailAST annotationNode : methodAnnotationsList) {
 447  15
             if ("SafeVarargs".equals(annotationNode.getLastChild().getText())) {
 448  2
                 result = true;
 449  2
                 break;
 450  
             }
 451  13
         }
 452  23
         return result;
 453  
     }
 454  
 
 455  
     /**
 456  
      * Gets the list of annotations on method definition.
 457  
      * @param methodDef method definition node
 458  
      * @return List of annotations
 459  
      */
 460  
     private static List<DetailAST> getMethodAnnotationsList(DetailAST methodDef) {
 461  23
         final List<DetailAST> annotationsList = new ArrayList<>();
 462  23
         final DetailAST modifiers = methodDef.findFirstToken(TokenTypes.MODIFIERS);
 463  23
         DetailAST modifier = modifiers.getFirstChild();
 464  75
         while (modifier != null) {
 465  52
             if (modifier.getType() == TokenTypes.ANNOTATION) {
 466  16
                 annotationsList.add(modifier);
 467  
             }
 468  52
             modifier = modifier.getNextSibling();
 469  
         }
 470  23
         return annotationsList;
 471  
     }
 472  
 }