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