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