001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.modifier;
021
022import java.util.ArrayList;
023import java.util.List;
024
025import com.puppycrawl.tools.checkstyle.StatelessCheck;
026import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
027import com.puppycrawl.tools.checkstyle.api.DetailAST;
028import com.puppycrawl.tools.checkstyle.api.TokenTypes;
029import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
030
031/**
032 * <p>
033 * Checks for redundant modifiers.
034 * </p>
035 * <p>
036 * Rationale: The Java Language Specification strongly discourages the usage
037 * of {@code public} and {@code abstract} for method declarations in interface
038 * definitions as a matter of style.
039 * </p>
040 * <p>The check validates:</p>
041 * <ol>
042 * <li>
043 * Interface and annotation definitions.
044 * </li>
045 * <li>
046 * Final modifier on methods of final and anonymous classes.
047 * </li>
048 * <li>
049 * Inner {@code interface} declarations that are declared as {@code static}.
050 * </li>
051 * <li>
052 * Class constructors.
053 * </li>
054 * <li>
055 * Nested {@code enum} definitions that are declared as {@code static}.
056 * </li>
057 * </ol>
058 * <p>
059 * Interfaces by definition are abstract so the {@code abstract}
060 * modifier on the interface is redundant.
061 * </p>
062 * <p>Classes inside of interfaces by definition are public and static,
063 * so the {@code public} and {@code static} modifiers
064 * on the inner classes are redundant. On the other hand, classes
065 * inside of interfaces can be abstract or non abstract.
066 * So, {@code abstract} modifier is allowed.
067 * </p>
068 * <p>Fields in interfaces and annotations are automatically
069 * public, static and final, so these modifiers are redundant as
070 * well.</p>
071 *
072 * <p>As annotations are a form of interface, their fields are also
073 * automatically public, static and final just as their
074 * annotation fields are automatically public and abstract.</p>
075 *
076 * <p>Enums by definition are static implicit subclasses of java.lang.Enum&#60;E&#62;.
077 * So, the {@code static} modifier on the enums is redundant. In addition,
078 * if enum is inside of interface, {@code public} modifier is also redundant.</p>
079 *
080 * <p>Enums can also contain abstract methods and methods which can be overridden by the declared
081 * enumeration fields.
082 * See the following example:</p>
083 * <pre>
084 * public enum EnumClass {
085 *   FIELD_1,
086 *   FIELD_2 {
087 *     &#64;Override
088 *     public final void method1() {} // violation expected
089 *   };
090 *
091 *   public void method1() {}
092 *   public final void method2() {} // no violation expected
093 * }
094 * </pre>
095 *
096 * <p>Since these methods can be overridden in these situations, the final methods are not
097 * marked as redundant even though they can't be extended by other classes/enums.</p>
098 * <p>
099 * Nested {@code enum} types are always static by default.
100 * </p>
101 * <p>Final classes by definition cannot be extended so the {@code final}
102 * modifier on the method of a final class is redundant.
103 * </p>
104 * <p>Public modifier for constructors in non-public non-protected classes
105 * is always obsolete: </p>
106 *
107 * <pre>
108 * public class PublicClass {
109 *   public PublicClass() {} // OK
110 * }
111 *
112 * class PackagePrivateClass {
113 *   public PackagePrivateClass() {} // violation expected
114 * }
115 * </pre>
116 *
117 * <p>There is no violation in the following example,
118 * because removing public modifier from ProtectedInnerClass
119 * constructor will make this code not compiling: </p>
120 *
121 * <pre>
122 * package a;
123 * public class ClassExample {
124 *   protected class ProtectedInnerClass {
125 *     public ProtectedInnerClass () {}
126 *   }
127 * }
128 *
129 * package b;
130 * import a.ClassExample;
131 * public class ClassExtending extends ClassExample {
132 *   ProtectedInnerClass pc = new ProtectedInnerClass();
133 * }
134 * </pre>
135 * <ul>
136 * <li>
137 * Property {@code tokens} - tokens to check
138 * Default value is:
139 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
140 * METHOD_DEF</a>,
141 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
142 * VARIABLE_DEF</a>,
143 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">
144 * ANNOTATION_FIELD_DEF</a>,
145 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
146 * INTERFACE_DEF</a>,
147 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
148 * CTOR_DEF</a>,
149 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
150 * CLASS_DEF</a>,
151 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
152 * ENUM_DEF</a>,
153 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RESOURCE">
154 * RESOURCE</a>.
155 * </li>
156 * </ul>
157 * <p>
158 * To configure the check:
159 * </p>
160 * <pre>
161 * &lt;module name="RedundantModifier"/&gt;
162 * </pre>
163 * <p>
164 * To configure the check to check only methods and not variables:
165 * </p>
166 * <pre>
167 * &lt;module name="RedundantModifier"&gt;
168 *   &lt;property name="tokens" value="METHOD_DEF"/&gt;
169 * &lt;/module&gt;
170 * </pre>
171 *
172 * @since 3.0
173 */
174@StatelessCheck
175public class RedundantModifierCheck
176    extends AbstractCheck {
177
178    /**
179     * A key is pointing to the warning message text in "messages.properties"
180     * file.
181     */
182    public static final String MSG_KEY = "redundantModifier";
183
184    /**
185     * An array of tokens for interface modifiers.
186     */
187    private static final int[] TOKENS_FOR_INTERFACE_MODIFIERS = {
188        TokenTypes.LITERAL_STATIC,
189        TokenTypes.ABSTRACT,
190    };
191
192    @Override
193    public int[] getDefaultTokens() {
194        return getAcceptableTokens();
195    }
196
197    @Override
198    public int[] getRequiredTokens() {
199        return CommonUtil.EMPTY_INT_ARRAY;
200    }
201
202    @Override
203    public int[] getAcceptableTokens() {
204        return new int[] {
205            TokenTypes.METHOD_DEF,
206            TokenTypes.VARIABLE_DEF,
207            TokenTypes.ANNOTATION_FIELD_DEF,
208            TokenTypes.INTERFACE_DEF,
209            TokenTypes.CTOR_DEF,
210            TokenTypes.CLASS_DEF,
211            TokenTypes.ENUM_DEF,
212            TokenTypes.RESOURCE,
213        };
214    }
215
216    @Override
217    public void visitToken(DetailAST ast) {
218        if (ast.getType() == TokenTypes.INTERFACE_DEF) {
219            checkInterfaceModifiers(ast);
220        }
221        else if (ast.getType() == TokenTypes.ENUM_DEF) {
222            checkEnumDef(ast);
223        }
224        else {
225            if (ast.getType() == TokenTypes.CTOR_DEF) {
226                if (isEnumMember(ast)) {
227                    checkEnumConstructorModifiers(ast);
228                }
229                else {
230                    checkClassConstructorModifiers(ast);
231                }
232            }
233            else if (ast.getType() == TokenTypes.METHOD_DEF) {
234                processMethods(ast);
235            }
236            else if (ast.getType() == TokenTypes.RESOURCE) {
237                processResources(ast);
238            }
239
240            if (isInterfaceOrAnnotationMember(ast)) {
241                processInterfaceOrAnnotation(ast);
242            }
243        }
244    }
245
246    /**
247     * Checks if interface has proper modifiers.
248     *
249     * @param ast interface to check
250     */
251    private void checkInterfaceModifiers(DetailAST ast) {
252        final DetailAST modifiers =
253            ast.findFirstToken(TokenTypes.MODIFIERS);
254
255        for (final int tokenType : TOKENS_FOR_INTERFACE_MODIFIERS) {
256            final DetailAST modifier =
257                    modifiers.findFirstToken(tokenType);
258            if (modifier != null) {
259                log(modifier, MSG_KEY, modifier.getText());
260            }
261        }
262    }
263
264    /**
265     * Check if enum constructor has proper modifiers.
266     *
267     * @param ast constructor of enum
268     */
269    private void checkEnumConstructorModifiers(DetailAST ast) {
270        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
271        final DetailAST modifier = getFirstModifierAst(modifiers);
272
273        if (modifier != null) {
274            log(modifier, MSG_KEY, modifier.getText());
275        }
276    }
277
278    /**
279     * Retrieves the first modifier that is not an annotation.
280     *
281     * @param modifiers The ast to examine.
282     * @return The first modifier or {@code null} if none found.
283     */
284    private static DetailAST getFirstModifierAst(DetailAST modifiers) {
285        DetailAST modifier = modifiers.getFirstChild();
286
287        while (modifier != null && modifier.getType() == TokenTypes.ANNOTATION) {
288            modifier = modifier.getNextSibling();
289        }
290
291        return modifier;
292    }
293
294    /**
295     * Checks whether enum has proper modifiers.
296     *
297     * @param ast enum definition.
298     */
299    private void checkEnumDef(DetailAST ast) {
300        if (isInterfaceOrAnnotationMember(ast)) {
301            processInterfaceOrAnnotation(ast);
302        }
303        else {
304            checkForRedundantModifier(ast, TokenTypes.LITERAL_STATIC);
305        }
306    }
307
308    /**
309     * Do validation of interface of annotation.
310     *
311     * @param ast token AST
312     */
313    private void processInterfaceOrAnnotation(DetailAST ast) {
314        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
315        DetailAST modifier = modifiers.getFirstChild();
316        while (modifier != null) {
317            // javac does not allow final or static in interface methods
318            // order annotation fields hence no need to check that this
319            // is not a method or annotation field
320
321            final int type = modifier.getType();
322            if (type == TokenTypes.LITERAL_PUBLIC
323                || type == TokenTypes.LITERAL_STATIC
324                        && ast.getType() != TokenTypes.METHOD_DEF
325                || type == TokenTypes.ABSTRACT
326                        && ast.getType() != TokenTypes.CLASS_DEF
327                || type == TokenTypes.FINAL
328                        && ast.getType() != TokenTypes.CLASS_DEF) {
329                log(modifier, MSG_KEY, modifier.getText());
330                break;
331            }
332
333            modifier = modifier.getNextSibling();
334        }
335    }
336
337    /**
338     * Process validation of Methods.
339     *
340     * @param ast method AST
341     */
342    private void processMethods(DetailAST ast) {
343        final DetailAST modifiers =
344                        ast.findFirstToken(TokenTypes.MODIFIERS);
345        // private method?
346        boolean checkFinal =
347            modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) != null;
348        // declared in a final class?
349        DetailAST parent = ast.getParent();
350        while (parent != null && !checkFinal) {
351            if (parent.getType() == TokenTypes.CLASS_DEF) {
352                final DetailAST classModifiers =
353                    parent.findFirstToken(TokenTypes.MODIFIERS);
354                checkFinal = classModifiers.findFirstToken(TokenTypes.FINAL) != null;
355                parent = null;
356            }
357            else if (parent.getType() == TokenTypes.LITERAL_NEW
358                    || parent.getType() == TokenTypes.ENUM_CONSTANT_DEF) {
359                checkFinal = true;
360                parent = null;
361            }
362            else if (parent.getType() == TokenTypes.ENUM_DEF) {
363                checkFinal = modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
364                parent = null;
365            }
366            else {
367                parent = parent.getParent();
368            }
369        }
370        if (checkFinal && !isAnnotatedWithSafeVarargs(ast)) {
371            checkForRedundantModifier(ast, TokenTypes.FINAL);
372        }
373
374        if (ast.findFirstToken(TokenTypes.SLIST) == null) {
375            processAbstractMethodParameters(ast);
376        }
377    }
378
379    /**
380     * Process validation of parameters for Methods with no definition.
381     *
382     * @param ast method AST
383     */
384    private void processAbstractMethodParameters(DetailAST ast) {
385        final DetailAST parameters = ast.findFirstToken(TokenTypes.PARAMETERS);
386
387        for (DetailAST child = parameters.getFirstChild(); child != null; child = child
388                .getNextSibling()) {
389            if (child.getType() == TokenTypes.PARAMETER_DEF) {
390                checkForRedundantModifier(child, TokenTypes.FINAL);
391            }
392        }
393    }
394
395    /**
396     * Check if class constructor has proper modifiers.
397     *
398     * @param classCtorAst class constructor ast
399     */
400    private void checkClassConstructorModifiers(DetailAST classCtorAst) {
401        final DetailAST classDef = classCtorAst.getParent().getParent();
402        if (!isClassPublic(classDef) && !isClassProtected(classDef)) {
403            checkForRedundantModifier(classCtorAst, TokenTypes.LITERAL_PUBLIC);
404        }
405    }
406
407    /**
408     * Checks if given resource has redundant modifiers.
409     *
410     * @param ast ast
411     */
412    private void processResources(DetailAST ast) {
413        checkForRedundantModifier(ast, TokenTypes.FINAL);
414    }
415
416    /**
417     * Checks if given ast has a redundant modifier.
418     *
419     * @param ast ast
420     * @param modifierType The modifier to check for.
421     */
422    private void checkForRedundantModifier(DetailAST ast, int modifierType) {
423        final DetailAST astModifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
424        DetailAST astModifier = astModifiers.getFirstChild();
425        while (astModifier != null) {
426            if (astModifier.getType() == modifierType) {
427                log(astModifier, MSG_KEY, astModifier.getText());
428            }
429
430            astModifier = astModifier.getNextSibling();
431        }
432    }
433
434    /**
435     * Checks if given class ast has protected modifier.
436     *
437     * @param classDef class ast
438     * @return true if class is protected, false otherwise
439     */
440    private static boolean isClassProtected(DetailAST classDef) {
441        final DetailAST classModifiers =
442                classDef.findFirstToken(TokenTypes.MODIFIERS);
443        return classModifiers.findFirstToken(TokenTypes.LITERAL_PROTECTED) != null;
444    }
445
446    /**
447     * Checks if given class is accessible from "public" scope.
448     *
449     * @param ast class def to check
450     * @return true if class is accessible from public scope,false otherwise
451     */
452    private static boolean isClassPublic(DetailAST ast) {
453        boolean isAccessibleFromPublic = false;
454        final boolean isMostOuterScope = ast.getParent() == null;
455        final DetailAST modifiersAst = ast.findFirstToken(TokenTypes.MODIFIERS);
456        final boolean hasPublicModifier =
457                modifiersAst.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null;
458
459        if (isMostOuterScope) {
460            isAccessibleFromPublic = hasPublicModifier;
461        }
462        else {
463            final DetailAST parentClassAst = ast.getParent().getParent();
464
465            if (hasPublicModifier || parentClassAst.getType() == TokenTypes.INTERFACE_DEF) {
466                isAccessibleFromPublic = isClassPublic(parentClassAst);
467            }
468        }
469
470        return isAccessibleFromPublic;
471    }
472
473    /**
474     * Checks if current AST node is member of Enum.
475     *
476     * @param ast AST node
477     * @return true if it is an enum member
478     */
479    private static boolean isEnumMember(DetailAST ast) {
480        final DetailAST parentTypeDef = ast.getParent().getParent();
481        return parentTypeDef.getType() == TokenTypes.ENUM_DEF;
482    }
483
484    /**
485     * Checks if current AST node is member of Interface or Annotation, not of their subnodes.
486     *
487     * @param ast AST node
488     * @return true or false
489     */
490    private static boolean isInterfaceOrAnnotationMember(DetailAST ast) {
491        DetailAST parentTypeDef = ast.getParent();
492
493        if (parentTypeDef != null) {
494            parentTypeDef = parentTypeDef.getParent();
495        }
496        return parentTypeDef != null
497                && (parentTypeDef.getType() == TokenTypes.INTERFACE_DEF
498                    || parentTypeDef.getType() == TokenTypes.ANNOTATION_DEF);
499    }
500
501    /**
502     * Checks if method definition is annotated with.
503     * <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/SafeVarargs.html">
504     * SafeVarargs</a> annotation
505     *
506     * @param methodDef method definition node
507     * @return true or false
508     */
509    private static boolean isAnnotatedWithSafeVarargs(DetailAST methodDef) {
510        boolean result = false;
511        final List<DetailAST> methodAnnotationsList = getMethodAnnotationsList(methodDef);
512        for (DetailAST annotationNode : methodAnnotationsList) {
513            if ("SafeVarargs".equals(annotationNode.getLastChild().getText())) {
514                result = true;
515                break;
516            }
517        }
518        return result;
519    }
520
521    /**
522     * Gets the list of annotations on method definition.
523     *
524     * @param methodDef method definition node
525     * @return List of annotations
526     */
527    private static List<DetailAST> getMethodAnnotationsList(DetailAST methodDef) {
528        final List<DetailAST> annotationsList = new ArrayList<>();
529        final DetailAST modifiers = methodDef.findFirstToken(TokenTypes.MODIFIERS);
530        DetailAST modifier = modifiers.getFirstChild();
531        while (modifier != null) {
532            if (modifier.getType() == TokenTypes.ANNOTATION) {
533                annotationsList.add(modifier);
534            }
535            modifier = modifier.getNextSibling();
536        }
537        return annotationsList;
538    }
539
540}