001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2019 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     * @param ast interface to check
249     */
250    private void checkInterfaceModifiers(DetailAST ast) {
251        final DetailAST modifiers =
252            ast.findFirstToken(TokenTypes.MODIFIERS);
253
254        for (final int tokenType : TOKENS_FOR_INTERFACE_MODIFIERS) {
255            final DetailAST modifier =
256                    modifiers.findFirstToken(tokenType);
257            if (modifier != null) {
258                log(modifier, MSG_KEY, modifier.getText());
259            }
260        }
261    }
262
263    /**
264     * Check if enum constructor has proper modifiers.
265     * @param ast constructor of enum
266     */
267    private void checkEnumConstructorModifiers(DetailAST ast) {
268        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
269        final DetailAST modifier = getFirstModifierAst(modifiers);
270
271        if (modifier != null) {
272            log(modifier, MSG_KEY, modifier.getText());
273        }
274    }
275
276    /**
277     * Retrieves the first modifier that is not an annotation.
278     * @param modifiers The ast to examine.
279     * @return The first modifier or {@code null} if none found.
280     */
281    private static DetailAST getFirstModifierAst(DetailAST modifiers) {
282        DetailAST modifier = modifiers.getFirstChild();
283
284        while (modifier != null && modifier.getType() == TokenTypes.ANNOTATION) {
285            modifier = modifier.getNextSibling();
286        }
287
288        return modifier;
289    }
290
291    /**
292     * Checks whether enum has proper modifiers.
293     * @param ast enum definition.
294     */
295    private void checkEnumDef(DetailAST ast) {
296        if (isInterfaceOrAnnotationMember(ast)) {
297            processInterfaceOrAnnotation(ast);
298        }
299        else {
300            checkForRedundantModifier(ast, TokenTypes.LITERAL_STATIC);
301        }
302    }
303
304    /**
305     * Do validation of interface of annotation.
306     * @param ast token AST
307     */
308    private void processInterfaceOrAnnotation(DetailAST ast) {
309        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
310        DetailAST modifier = modifiers.getFirstChild();
311        while (modifier != null) {
312            // javac does not allow final or static in interface methods
313            // order annotation fields hence no need to check that this
314            // is not a method or annotation field
315
316            final int type = modifier.getType();
317            if (type == TokenTypes.LITERAL_PUBLIC
318                || type == TokenTypes.LITERAL_STATIC
319                        && ast.getType() != TokenTypes.METHOD_DEF
320                || type == TokenTypes.ABSTRACT
321                        && ast.getType() != TokenTypes.CLASS_DEF
322                || type == TokenTypes.FINAL
323                        && ast.getType() != TokenTypes.CLASS_DEF) {
324                log(modifier, MSG_KEY, modifier.getText());
325                break;
326            }
327
328            modifier = modifier.getNextSibling();
329        }
330    }
331
332    /**
333     * Process validation of Methods.
334     * @param ast method AST
335     */
336    private void processMethods(DetailAST ast) {
337        final DetailAST modifiers =
338                        ast.findFirstToken(TokenTypes.MODIFIERS);
339        // private method?
340        boolean checkFinal =
341            modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) != null;
342        // declared in a final class?
343        DetailAST parent = ast.getParent();
344        while (parent != null && !checkFinal) {
345            if (parent.getType() == TokenTypes.CLASS_DEF) {
346                final DetailAST classModifiers =
347                    parent.findFirstToken(TokenTypes.MODIFIERS);
348                checkFinal = classModifiers.findFirstToken(TokenTypes.FINAL) != null;
349                parent = null;
350            }
351            else if (parent.getType() == TokenTypes.LITERAL_NEW
352                    || parent.getType() == TokenTypes.ENUM_CONSTANT_DEF) {
353                checkFinal = true;
354                parent = null;
355            }
356            else if (parent.getType() == TokenTypes.ENUM_DEF) {
357                checkFinal = modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
358                parent = null;
359            }
360            else {
361                parent = parent.getParent();
362            }
363        }
364        if (checkFinal && !isAnnotatedWithSafeVarargs(ast)) {
365            checkForRedundantModifier(ast, TokenTypes.FINAL);
366        }
367
368        if (ast.findFirstToken(TokenTypes.SLIST) == null) {
369            processAbstractMethodParameters(ast);
370        }
371    }
372
373    /**
374     * Process validation of parameters for Methods with no definition.
375     * @param ast method AST
376     */
377    private void processAbstractMethodParameters(DetailAST ast) {
378        final DetailAST parameters = ast.findFirstToken(TokenTypes.PARAMETERS);
379
380        for (DetailAST child = parameters.getFirstChild(); child != null; child = child
381                .getNextSibling()) {
382            if (child.getType() == TokenTypes.PARAMETER_DEF) {
383                checkForRedundantModifier(child, TokenTypes.FINAL);
384            }
385        }
386    }
387
388    /**
389     * Check if class constructor has proper modifiers.
390     * @param classCtorAst class constructor ast
391     */
392    private void checkClassConstructorModifiers(DetailAST classCtorAst) {
393        final DetailAST classDef = classCtorAst.getParent().getParent();
394        if (!isClassPublic(classDef) && !isClassProtected(classDef)) {
395            checkForRedundantModifier(classCtorAst, TokenTypes.LITERAL_PUBLIC);
396        }
397    }
398
399    /**
400     * Checks if given resource has redundant modifiers.
401     * @param ast ast
402     */
403    private void processResources(DetailAST ast) {
404        checkForRedundantModifier(ast, TokenTypes.FINAL);
405    }
406
407    /**
408     * Checks if given ast has a redundant modifier.
409     * @param ast ast
410     * @param modifierType The modifier to check for.
411     */
412    private void checkForRedundantModifier(DetailAST ast, int modifierType) {
413        final DetailAST astModifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
414        DetailAST astModifier = astModifiers.getFirstChild();
415        while (astModifier != null) {
416            if (astModifier.getType() == modifierType) {
417                log(astModifier, MSG_KEY, astModifier.getText());
418            }
419
420            astModifier = astModifier.getNextSibling();
421        }
422    }
423
424    /**
425     * Checks if given class ast has protected modifier.
426     * @param classDef class ast
427     * @return true if class is protected, false otherwise
428     */
429    private static boolean isClassProtected(DetailAST classDef) {
430        final DetailAST classModifiers =
431                classDef.findFirstToken(TokenTypes.MODIFIERS);
432        return classModifiers.findFirstToken(TokenTypes.LITERAL_PROTECTED) != null;
433    }
434
435    /**
436     * Checks if given class is accessible from "public" scope.
437     * @param ast class def to check
438     * @return true if class is accessible from public scope,false otherwise
439     */
440    private static boolean isClassPublic(DetailAST ast) {
441        boolean isAccessibleFromPublic = false;
442        final boolean isMostOuterScope = ast.getParent() == null;
443        final DetailAST modifiersAst = ast.findFirstToken(TokenTypes.MODIFIERS);
444        final boolean hasPublicModifier =
445                modifiersAst.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null;
446
447        if (isMostOuterScope) {
448            isAccessibleFromPublic = hasPublicModifier;
449        }
450        else {
451            final DetailAST parentClassAst = ast.getParent().getParent();
452
453            if (hasPublicModifier || parentClassAst.getType() == TokenTypes.INTERFACE_DEF) {
454                isAccessibleFromPublic = isClassPublic(parentClassAst);
455            }
456        }
457
458        return isAccessibleFromPublic;
459    }
460
461    /**
462     * Checks if current AST node is member of Enum.
463     * @param ast AST node
464     * @return true if it is an enum member
465     */
466    private static boolean isEnumMember(DetailAST ast) {
467        final DetailAST parentTypeDef = ast.getParent().getParent();
468        return parentTypeDef.getType() == TokenTypes.ENUM_DEF;
469    }
470
471    /**
472     * Checks if current AST node is member of Interface or Annotation, not of their subnodes.
473     * @param ast AST node
474     * @return true or false
475     */
476    private static boolean isInterfaceOrAnnotationMember(DetailAST ast) {
477        DetailAST parentTypeDef = ast.getParent();
478
479        if (parentTypeDef != null) {
480            parentTypeDef = parentTypeDef.getParent();
481        }
482        return parentTypeDef != null
483                && (parentTypeDef.getType() == TokenTypes.INTERFACE_DEF
484                    || parentTypeDef.getType() == TokenTypes.ANNOTATION_DEF);
485    }
486
487    /**
488     * Checks if method definition is annotated with.
489     * <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/SafeVarargs.html">
490     * SafeVarargs</a> annotation
491     * @param methodDef method definition node
492     * @return true or false
493     */
494    private static boolean isAnnotatedWithSafeVarargs(DetailAST methodDef) {
495        boolean result = false;
496        final List<DetailAST> methodAnnotationsList = getMethodAnnotationsList(methodDef);
497        for (DetailAST annotationNode : methodAnnotationsList) {
498            if ("SafeVarargs".equals(annotationNode.getLastChild().getText())) {
499                result = true;
500                break;
501            }
502        }
503        return result;
504    }
505
506    /**
507     * Gets the list of annotations on method definition.
508     * @param methodDef method definition node
509     * @return List of annotations
510     */
511    private static List<DetailAST> getMethodAnnotationsList(DetailAST methodDef) {
512        final List<DetailAST> annotationsList = new ArrayList<>();
513        final DetailAST modifiers = methodDef.findFirstToken(TokenTypes.MODIFIERS);
514        DetailAST modifier = modifiers.getFirstChild();
515        while (modifier != null) {
516            if (modifier.getType() == TokenTypes.ANNOTATION) {
517                annotationsList.add(modifier);
518            }
519            modifier = modifier.getNextSibling();
520        }
521        return annotationsList;
522    }
523
524}