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 int[]}.
141 * Default value is:
142 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
143 * METHOD_DEF</a>,
144 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
145 * VARIABLE_DEF</a>,
146 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">
147 * ANNOTATION_FIELD_DEF</a>,
148 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
149 * INTERFACE_DEF</a>,
150 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
151 * CTOR_DEF</a>,
152 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
153 * CLASS_DEF</a>,
154 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
155 * ENUM_DEF</a>,
156 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RESOURCE">
157 * RESOURCE</a>.
158 * </li>
159 * </ul>
160 * <p>
161 * To configure the check:
162 * </p>
163 * <pre>
164 * &lt;module name="RedundantModifier"/&gt;
165 * </pre>
166 * <p>
167 * To configure the check to check only methods and not variables:
168 * </p>
169 * <pre>
170 * &lt;module name="RedundantModifier"&gt;
171 *   &lt;property name="tokens" value="METHOD_DEF"/&gt;
172 * &lt;/module&gt;
173 * </pre>
174 * <p>
175 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
176 * </p>
177 * <p>
178 * Violation Message Keys:
179 * </p>
180 * <ul>
181 * <li>
182 * {@code redundantModifier}
183 * </li>
184 * </ul>
185 *
186 * @since 3.0
187 */
188@StatelessCheck
189public class RedundantModifierCheck
190    extends AbstractCheck {
191
192    /**
193     * A key is pointing to the warning message text in "messages.properties"
194     * file.
195     */
196    public static final String MSG_KEY = "redundantModifier";
197
198    /**
199     * An array of tokens for interface modifiers.
200     */
201    private static final int[] TOKENS_FOR_INTERFACE_MODIFIERS = {
202        TokenTypes.LITERAL_STATIC,
203        TokenTypes.ABSTRACT,
204    };
205
206    @Override
207    public int[] getDefaultTokens() {
208        return getAcceptableTokens();
209    }
210
211    @Override
212    public int[] getRequiredTokens() {
213        return CommonUtil.EMPTY_INT_ARRAY;
214    }
215
216    @Override
217    public int[] getAcceptableTokens() {
218        return new int[] {
219            TokenTypes.METHOD_DEF,
220            TokenTypes.VARIABLE_DEF,
221            TokenTypes.ANNOTATION_FIELD_DEF,
222            TokenTypes.INTERFACE_DEF,
223            TokenTypes.CTOR_DEF,
224            TokenTypes.CLASS_DEF,
225            TokenTypes.ENUM_DEF,
226            TokenTypes.RESOURCE,
227        };
228    }
229
230    @Override
231    public void visitToken(DetailAST ast) {
232        if (ast.getType() == TokenTypes.INTERFACE_DEF) {
233            checkInterfaceModifiers(ast);
234        }
235        else if (ast.getType() == TokenTypes.ENUM_DEF) {
236            checkEnumDef(ast);
237        }
238        else {
239            if (ast.getType() == TokenTypes.CTOR_DEF) {
240                if (isEnumMember(ast)) {
241                    checkEnumConstructorModifiers(ast);
242                }
243                else {
244                    checkClassConstructorModifiers(ast);
245                }
246            }
247            else if (ast.getType() == TokenTypes.METHOD_DEF) {
248                processMethods(ast);
249            }
250            else if (ast.getType() == TokenTypes.RESOURCE) {
251                processResources(ast);
252            }
253
254            if (isInterfaceOrAnnotationMember(ast)) {
255                processInterfaceOrAnnotation(ast);
256            }
257        }
258    }
259
260    /**
261     * Checks if interface has proper modifiers.
262     *
263     * @param ast interface to check
264     */
265    private void checkInterfaceModifiers(DetailAST ast) {
266        final DetailAST modifiers =
267            ast.findFirstToken(TokenTypes.MODIFIERS);
268
269        for (final int tokenType : TOKENS_FOR_INTERFACE_MODIFIERS) {
270            final DetailAST modifier =
271                    modifiers.findFirstToken(tokenType);
272            if (modifier != null) {
273                log(modifier, MSG_KEY, modifier.getText());
274            }
275        }
276    }
277
278    /**
279     * Check if enum constructor has proper modifiers.
280     *
281     * @param ast constructor of enum
282     */
283    private void checkEnumConstructorModifiers(DetailAST ast) {
284        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
285        TokenUtil.findFirstTokenByPredicate(
286            modifiers, mod -> mod.getType() != TokenTypes.ANNOTATION
287        ).ifPresent(modifier -> log(modifier, MSG_KEY, modifier.getText()));
288    }
289
290    /**
291     * Checks whether enum has proper modifiers.
292     *
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     *
307     * @param ast token AST
308     */
309    private void processInterfaceOrAnnotation(DetailAST ast) {
310        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
311        DetailAST modifier = modifiers.getFirstChild();
312        while (modifier != null) {
313            // javac does not allow final or static in interface methods
314            // order annotation fields hence no need to check that this
315            // is not a method or annotation field
316
317            final int type = modifier.getType();
318            if (type == TokenTypes.LITERAL_PUBLIC
319                || type == TokenTypes.LITERAL_STATIC
320                        && ast.getType() != TokenTypes.METHOD_DEF
321                || type == TokenTypes.ABSTRACT
322                        && ast.getType() != TokenTypes.CLASS_DEF
323                || type == TokenTypes.FINAL
324                        && ast.getType() != TokenTypes.CLASS_DEF) {
325                log(modifier, MSG_KEY, modifier.getText());
326                break;
327            }
328
329            modifier = modifier.getNextSibling();
330        }
331    }
332
333    /**
334     * Process validation of Methods.
335     *
336     * @param ast method AST
337     */
338    private void processMethods(DetailAST ast) {
339        final DetailAST modifiers =
340                        ast.findFirstToken(TokenTypes.MODIFIERS);
341        // private method?
342        boolean checkFinal =
343            modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) != null;
344        // declared in a final class?
345        DetailAST parent = ast.getParent();
346        while (parent != null && !checkFinal) {
347            if (parent.getType() == TokenTypes.CLASS_DEF) {
348                final DetailAST classModifiers =
349                    parent.findFirstToken(TokenTypes.MODIFIERS);
350                checkFinal = classModifiers.findFirstToken(TokenTypes.FINAL) != null;
351                parent = null;
352            }
353            else if (parent.getType() == TokenTypes.LITERAL_NEW
354                    || parent.getType() == TokenTypes.ENUM_CONSTANT_DEF) {
355                checkFinal = true;
356                parent = null;
357            }
358            else if (parent.getType() == TokenTypes.ENUM_DEF) {
359                checkFinal = modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
360                parent = null;
361            }
362            else {
363                parent = parent.getParent();
364            }
365        }
366        if (checkFinal && !isAnnotatedWithSafeVarargs(ast)) {
367            checkForRedundantModifier(ast, TokenTypes.FINAL);
368        }
369
370        if (ast.findFirstToken(TokenTypes.SLIST) == null) {
371            processAbstractMethodParameters(ast);
372        }
373    }
374
375    /**
376     * Process validation of parameters for Methods with no definition.
377     *
378     * @param ast method AST
379     */
380    private void processAbstractMethodParameters(DetailAST ast) {
381        final DetailAST parameters = ast.findFirstToken(TokenTypes.PARAMETERS);
382        TokenUtil.forEachChild(parameters, TokenTypes.PARAMETER_DEF, paramDef -> {
383            checkForRedundantModifier(paramDef, TokenTypes.FINAL);
384        });
385    }
386
387    /**
388     * Check if class constructor has proper modifiers.
389     *
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     *
402     * @param ast ast
403     */
404    private void processResources(DetailAST ast) {
405        checkForRedundantModifier(ast, TokenTypes.FINAL);
406    }
407
408    /**
409     * Checks if given ast has a redundant modifier.
410     *
411     * @param ast ast
412     * @param modifierType The modifier to check for.
413     */
414    private void checkForRedundantModifier(DetailAST ast, int modifierType) {
415        Optional.ofNullable(ast.findFirstToken(TokenTypes.MODIFIERS))
416            .ifPresent(modifiers -> {
417                TokenUtil.forEachChild(modifiers, modifierType, modifier -> {
418                    log(modifier, MSG_KEY, modifier.getText());
419                });
420            });
421    }
422
423    /**
424     * Checks if given class ast has protected modifier.
425     *
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     *
438     * @param ast class def to check
439     * @return true if class is accessible from public scope,false otherwise
440     */
441    private static boolean isClassPublic(DetailAST ast) {
442        boolean isAccessibleFromPublic = false;
443        final boolean isMostOuterScope = ast.getParent() == null;
444        final DetailAST modifiersAst = ast.findFirstToken(TokenTypes.MODIFIERS);
445        final boolean hasPublicModifier =
446                modifiersAst.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null;
447
448        if (isMostOuterScope) {
449            isAccessibleFromPublic = hasPublicModifier;
450        }
451        else {
452            final DetailAST parentClassAst = ast.getParent().getParent();
453
454            if (hasPublicModifier || parentClassAst.getType() == TokenTypes.INTERFACE_DEF) {
455                isAccessibleFromPublic = isClassPublic(parentClassAst);
456            }
457        }
458
459        return isAccessibleFromPublic;
460    }
461
462    /**
463     * Checks if current AST node is member of Enum.
464     *
465     * @param ast AST node
466     * @return true if it is an enum member
467     */
468    private static boolean isEnumMember(DetailAST ast) {
469        final DetailAST parentTypeDef = ast.getParent().getParent();
470        return parentTypeDef.getType() == TokenTypes.ENUM_DEF;
471    }
472
473    /**
474     * Checks if current AST node is member of Interface or Annotation, not of their subnodes.
475     *
476     * @param ast AST node
477     * @return true or false
478     */
479    private static boolean isInterfaceOrAnnotationMember(DetailAST ast) {
480        DetailAST parentTypeDef = ast.getParent();
481
482        if (parentTypeDef != null) {
483            parentTypeDef = parentTypeDef.getParent();
484        }
485        return parentTypeDef != null
486                && (parentTypeDef.getType() == TokenTypes.INTERFACE_DEF
487                    || parentTypeDef.getType() == TokenTypes.ANNOTATION_DEF);
488    }
489
490    /**
491     * Checks if method definition is annotated with.
492     * <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/SafeVarargs.html">
493     * SafeVarargs</a> annotation
494     *
495     * @param methodDef method definition node
496     * @return true or false
497     */
498    private static boolean isAnnotatedWithSafeVarargs(DetailAST methodDef) {
499        boolean result = false;
500        final List<DetailAST> methodAnnotationsList = getMethodAnnotationsList(methodDef);
501        for (DetailAST annotationNode : methodAnnotationsList) {
502            if ("SafeVarargs".equals(annotationNode.getLastChild().getText())) {
503                result = true;
504                break;
505            }
506        }
507        return result;
508    }
509
510    /**
511     * Gets the list of annotations on method definition.
512     *
513     * @param methodDef method definition node
514     * @return List of annotations
515     */
516    private static List<DetailAST> getMethodAnnotationsList(DetailAST methodDef) {
517        final List<DetailAST> annotationsList = new ArrayList<>();
518        final DetailAST modifiers = methodDef.findFirstToken(TokenTypes.MODIFIERS);
519        TokenUtil.forEachChild(modifiers, TokenTypes.ANNOTATION, annotationsList::add);
520        return annotationsList;
521    }
522
523}