1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.CommonUtil;
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  */
114 @StatelessCheck
115 public class RedundantModifierCheck
116     extends AbstractCheck {
117 
118     /**
119      * A key is pointing to the warning message text in "messages.properties"
120      * file.
121      */
122     public static final String MSG_KEY = "redundantModifier";
123 
124     /**
125      * An array of tokens for interface modifiers.
126      */
127     private static final int[] TOKENS_FOR_INTERFACE_MODIFIERS = {
128         TokenTypes.LITERAL_STATIC,
129         TokenTypes.ABSTRACT,
130     };
131 
132     @Override
133     public int[] getDefaultTokens() {
134         return getAcceptableTokens();
135     }
136 
137     @Override
138     public int[] getRequiredTokens() {
139         return CommonUtil.EMPTY_INT_ARRAY;
140     }
141 
142     @Override
143     public int[] getAcceptableTokens() {
144         return new int[] {
145             TokenTypes.METHOD_DEF,
146             TokenTypes.VARIABLE_DEF,
147             TokenTypes.ANNOTATION_FIELD_DEF,
148             TokenTypes.INTERFACE_DEF,
149             TokenTypes.CTOR_DEF,
150             TokenTypes.CLASS_DEF,
151             TokenTypes.ENUM_DEF,
152             TokenTypes.RESOURCE,
153         };
154     }
155 
156     @Override
157     public void visitToken(DetailAST ast) {
158         if (ast.getType() == TokenTypes.INTERFACE_DEF) {
159             checkInterfaceModifiers(ast);
160         }
161         else if (ast.getType() == TokenTypes.ENUM_DEF) {
162             checkEnumDef(ast);
163         }
164         else {
165             if (ast.getType() == TokenTypes.CTOR_DEF) {
166                 if (isEnumMember(ast)) {
167                     checkEnumConstructorModifiers(ast);
168                 }
169                 else {
170                     checkClassConstructorModifiers(ast);
171                 }
172             }
173             else if (ast.getType() == TokenTypes.METHOD_DEF) {
174                 processMethods(ast);
175             }
176             else if (ast.getType() == TokenTypes.RESOURCE) {
177                 processResources(ast);
178             }
179 
180             if (isInterfaceOrAnnotationMember(ast)) {
181                 processInterfaceOrAnnotation(ast);
182             }
183         }
184     }
185 
186     /**
187      * Checks if interface has proper modifiers.
188      * @param ast interface to check
189      */
190     private void checkInterfaceModifiers(DetailAST ast) {
191         final DetailAST modifiers =
192             ast.findFirstToken(TokenTypes.MODIFIERS);
193 
194         for (final int tokenType : TOKENS_FOR_INTERFACE_MODIFIERS) {
195             final DetailAST modifier =
196                     modifiers.findFirstToken(tokenType);
197             if (modifier != null) {
198                 log(modifier, MSG_KEY, modifier.getText());
199             }
200         }
201     }
202 
203     /**
204      * Check if enum constructor has proper modifiers.
205      * @param ast constructor of enum
206      */
207     private void checkEnumConstructorModifiers(DetailAST ast) {
208         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
209         final DetailAST modifier = getFirstModifierAst(modifiers);
210 
211         if (modifier != null) {
212             log(modifier, MSG_KEY, modifier.getText());
213         }
214     }
215 
216     /**
217      * Retrieves the first modifier that is not an annotation.
218      * @param modifiers The ast to examine.
219      * @return The first modifier or {@code null} if none found.
220      */
221     private static DetailAST getFirstModifierAst(DetailAST modifiers) {
222         DetailAST modifier = modifiers.getFirstChild();
223 
224         while (modifier != null && modifier.getType() == TokenTypes.ANNOTATION) {
225             modifier = modifier.getNextSibling();
226         }
227 
228         return modifier;
229     }
230 
231     /**
232      * Checks whether enum has proper modifiers.
233      * @param ast enum definition.
234      */
235     private void checkEnumDef(DetailAST ast) {
236         if (isInterfaceOrAnnotationMember(ast)) {
237             processInterfaceOrAnnotation(ast);
238         }
239         else {
240             checkForRedundantModifier(ast, TokenTypes.LITERAL_STATIC);
241         }
242     }
243 
244     /**
245      * Do validation of interface of annotation.
246      * @param ast token AST
247      */
248     private void processInterfaceOrAnnotation(DetailAST ast) {
249         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
250         DetailAST modifier = modifiers.getFirstChild();
251         while (modifier != null) {
252             // javac does not allow final or static in interface methods
253             // order annotation fields hence no need to check that this
254             // is not a method or annotation field
255 
256             final int type = modifier.getType();
257             if (type == TokenTypes.LITERAL_PUBLIC
258                 || type == TokenTypes.LITERAL_STATIC
259                         && ast.getType() != TokenTypes.METHOD_DEF
260                 || type == TokenTypes.ABSTRACT
261                         && ast.getType() != TokenTypes.CLASS_DEF
262                 || type == TokenTypes.FINAL
263                         && ast.getType() != TokenTypes.CLASS_DEF) {
264                 log(modifier, MSG_KEY, modifier.getText());
265                 break;
266             }
267 
268             modifier = modifier.getNextSibling();
269         }
270     }
271 
272     /**
273      * Process validation of Methods.
274      * @param ast method AST
275      */
276     private void processMethods(DetailAST ast) {
277         final DetailAST modifiers =
278                         ast.findFirstToken(TokenTypes.MODIFIERS);
279         // private method?
280         boolean checkFinal =
281             modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) != null;
282         // declared in a final class?
283         DetailAST parent = ast.getParent();
284         while (parent != null && !checkFinal) {
285             if (parent.getType() == TokenTypes.CLASS_DEF) {
286                 final DetailAST classModifiers =
287                     parent.findFirstToken(TokenTypes.MODIFIERS);
288                 checkFinal = classModifiers.findFirstToken(TokenTypes.FINAL) != null;
289                 parent = null;
290             }
291             else if (parent.getType() == TokenTypes.LITERAL_NEW
292                     || parent.getType() == TokenTypes.ENUM_CONSTANT_DEF) {
293                 checkFinal = true;
294                 parent = null;
295             }
296             else if (parent.getType() == TokenTypes.ENUM_DEF) {
297                 checkFinal = modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
298                 parent = null;
299             }
300             else {
301                 parent = parent.getParent();
302             }
303         }
304         if (checkFinal && !isAnnotatedWithSafeVarargs(ast)) {
305             checkForRedundantModifier(ast, TokenTypes.FINAL);
306         }
307 
308         if (ast.findFirstToken(TokenTypes.SLIST) == null) {
309             processAbstractMethodParameters(ast);
310         }
311     }
312 
313     /**
314      * Process validation of parameters for Methods with no definition.
315      * @param ast method AST
316      */
317     private void processAbstractMethodParameters(DetailAST ast) {
318         final DetailAST parameters = ast.findFirstToken(TokenTypes.PARAMETERS);
319 
320         for (DetailAST child = parameters.getFirstChild(); child != null; child = child
321                 .getNextSibling()) {
322             if (child.getType() == TokenTypes.PARAMETER_DEF) {
323                 checkForRedundantModifier(child, TokenTypes.FINAL);
324             }
325         }
326     }
327 
328     /**
329      * Check if class constructor has proper modifiers.
330      * @param classCtorAst class constructor ast
331      */
332     private void checkClassConstructorModifiers(DetailAST classCtorAst) {
333         final DetailAST classDef = classCtorAst.getParent().getParent();
334         if (!isClassPublic(classDef) && !isClassProtected(classDef)) {
335             checkForRedundantModifier(classCtorAst, TokenTypes.LITERAL_PUBLIC);
336         }
337     }
338 
339     /**
340      * Checks if given resource has redundant modifiers.
341      * @param ast ast
342      */
343     private void processResources(DetailAST ast) {
344         checkForRedundantModifier(ast, TokenTypes.FINAL);
345     }
346 
347     /**
348      * Checks if given ast has a redundant modifier.
349      * @param ast ast
350      * @param modifierType The modifier to check for.
351      */
352     private void checkForRedundantModifier(DetailAST ast, int modifierType) {
353         final DetailAST astModifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
354         DetailAST astModifier = astModifiers.getFirstChild();
355         while (astModifier != null) {
356             if (astModifier.getType() == modifierType) {
357                 log(astModifier, MSG_KEY, astModifier.getText());
358             }
359 
360             astModifier = astModifier.getNextSibling();
361         }
362     }
363 
364     /**
365      * Checks if given class ast has protected modifier.
366      * @param classDef class ast
367      * @return true if class is protected, false otherwise
368      */
369     private static boolean isClassProtected(DetailAST classDef) {
370         final DetailAST classModifiers =
371                 classDef.findFirstToken(TokenTypes.MODIFIERS);
372         return classModifiers.findFirstToken(TokenTypes.LITERAL_PROTECTED) != null;
373     }
374 
375     /**
376      * Checks if given class is accessible from "public" scope.
377      * @param ast class def to check
378      * @return true if class is accessible from public scope,false otherwise
379      */
380     private static boolean isClassPublic(DetailAST ast) {
381         boolean isAccessibleFromPublic = false;
382         final boolean isMostOuterScope = ast.getParent() == null;
383         final DetailAST modifiersAst = ast.findFirstToken(TokenTypes.MODIFIERS);
384         final boolean hasPublicModifier =
385                 modifiersAst.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null;
386 
387         if (isMostOuterScope) {
388             isAccessibleFromPublic = hasPublicModifier;
389         }
390         else {
391             final DetailAST parentClassAst = ast.getParent().getParent();
392 
393             if (hasPublicModifier || parentClassAst.getType() == TokenTypes.INTERFACE_DEF) {
394                 isAccessibleFromPublic = isClassPublic(parentClassAst);
395             }
396         }
397 
398         return isAccessibleFromPublic;
399     }
400 
401     /**
402      * Checks if current AST node is member of Enum.
403      * @param ast AST node
404      * @return true if it is an enum member
405      */
406     private static boolean isEnumMember(DetailAST ast) {
407         final DetailAST parentTypeDef = ast.getParent().getParent();
408         return parentTypeDef.getType() == TokenTypes.ENUM_DEF;
409     }
410 
411     /**
412      * Checks if current AST node is member of Interface or Annotation, not of their subnodes.
413      * @param ast AST node
414      * @return true or false
415      */
416     private static boolean isInterfaceOrAnnotationMember(DetailAST ast) {
417         DetailAST parentTypeDef = ast.getParent();
418 
419         if (parentTypeDef != null) {
420             parentTypeDef = parentTypeDef.getParent();
421         }
422         return parentTypeDef != null
423                 && (parentTypeDef.getType() == TokenTypes.INTERFACE_DEF
424                     || parentTypeDef.getType() == TokenTypes.ANNOTATION_DEF);
425     }
426 
427     /**
428      * Checks if method definition is annotated with.
429      * <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/SafeVarargs.html">
430      * SafeVarargs</a> annotation
431      * @param methodDef method definition node
432      * @return true or false
433      */
434     private static boolean isAnnotatedWithSafeVarargs(DetailAST methodDef) {
435         boolean result = false;
436         final List<DetailAST> methodAnnotationsList = getMethodAnnotationsList(methodDef);
437         for (DetailAST annotationNode : methodAnnotationsList) {
438             if ("SafeVarargs".equals(annotationNode.getLastChild().getText())) {
439                 result = true;
440                 break;
441             }
442         }
443         return result;
444     }
445 
446     /**
447      * Gets the list of annotations on method definition.
448      * @param methodDef method definition node
449      * @return List of annotations
450      */
451     private static List<DetailAST> getMethodAnnotationsList(DetailAST methodDef) {
452         final List<DetailAST> annotationsList = new ArrayList<>();
453         final DetailAST modifiers = methodDef.findFirstToken(TokenTypes.MODIFIERS);
454         DetailAST modifier = modifiers.getFirstChild();
455         while (modifier != null) {
456             if (modifier.getType() == TokenTypes.ANNOTATION) {
457                 annotationsList.add(modifier);
458             }
459             modifier = modifier.getNextSibling();
460         }
461         return annotationsList;
462     }
463 
464 }