001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 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 * <div>
035 * Checks for redundant modifiers.
036 * </div>
037 *
038 * <p>
039 * Rationale: The Java Language Specification strongly discourages the usage
040 * of {@code public} and {@code abstract} for method declarations in interface
041 * definitions as a matter of style.
042 * </p>
043 *
044 * <p>The check validates:</p>
045 * <ol>
046 * <li>
047 * Interface and annotation definitions.
048 * </li>
049 * <li>
050 * Final modifier on methods of final and anonymous classes.
051 * </li>
052 * <li>
053 * Type declarations nested under interfaces that are declared as {@code public} or {@code static}.
054 * </li>
055 * <li>
056 * Class constructors.
057 * </li>
058 * <li>
059 * Nested {@code enum} definitions that are declared as {@code static}.
060 * </li>
061 * <li>
062 * {@code record} definitions that are declared as {@code final} and nested
063 * {@code record} definitions that are declared as {@code static}.
064 * </li>
065 * <li>
066 * {@code strictfp} modifier when using JDK 17 or later. See reason at
067 * <a href="https://openjdk.org/jeps/306">JEP 306</a>
068 * </li>
069 * <li>
070 * {@code final} modifier on unnamed variables when using JDK 22 or later.
071 * </li>
072 * </ol>
073 *
074 * <p>
075 * interfaces by definition are abstract so the {@code abstract} modifier is redundant on them.
076 * </p>
077 *
078 * <p>Type declarations nested under interfaces by definition are public and static,
079 * so the {@code public} and {@code static} modifiers on nested type declarations are redundant.
080 * On the other hand, classes inside of interfaces can be abstract or non abstract.
081 * So, {@code abstract} modifier is allowed.
082 * </p>
083 *
084 * <p>Fields in interfaces and annotations are automatically
085 * public, static and final, so these modifiers are redundant as
086 * well.</p>
087 *
088 * <p>As annotations are a form of interface, their fields are also
089 * automatically public, static and final just as their
090 * annotation fields are automatically public and abstract.</p>
091 *
092 * <p>A record class is implicitly final and cannot be abstract, these restrictions emphasize
093 * that the API of a record class is defined solely by its state description, and
094 * cannot be enhanced later by another class. Nested records are implicitly static. This avoids an
095 * immediately enclosing instance which would silently add state to the record class.
096 * See <a href="https://openjdk.org/jeps/395">JEP 395</a> for more info.</p>
097 *
098 * <p>Enums by definition are static implicit subclasses of java.lang.Enum&#60;E&#62;.
099 * So, the {@code static} modifier on the enums is redundant. In addition,
100 * if enum is inside of interface, {@code public} modifier is also redundant.</p>
101 *
102 * <p>Enums can also contain abstract methods and methods which can be overridden by the declared
103 * enumeration fields.
104 * See the following example:</p>
105 * <pre>
106 * public enum EnumClass {
107 *   FIELD_1,
108 *   FIELD_2 {
109 *     &#64;Override
110 *     public final void method1() {} // violation expected
111 *   };
112 *
113 *   public void method1() {}
114 *   public final void method2() {} // no violation expected
115 * }
116 * </pre>
117 *
118 * <p>Since these methods can be overridden in these situations, the final methods are not
119 * marked as redundant even though they can't be extended by other classes/enums.</p>
120 *
121 * <p>
122 * Nested {@code enum} types are always static by default.
123 * </p>
124 *
125 * <p>Final classes by definition cannot be extended so the {@code final}
126 * modifier on the method of a final class is redundant.
127 * </p>
128 *
129 * <p>Public modifier for constructors in non-public non-protected classes
130 * is always obsolete: </p>
131 *
132 * <pre>
133 * public class PublicClass {
134 *   public PublicClass() {} // OK
135 * }
136 *
137 * class PackagePrivateClass {
138 *   public PackagePrivateClass() {} // violation expected
139 * }
140 * </pre>
141 *
142 * <p>There is no violation in the following example,
143 * because removing public modifier from ProtectedInnerClass
144 * constructor will make this code not compiling: </p>
145 *
146 * <pre>
147 * package a;
148 * public class ClassExample {
149 *   protected class ProtectedInnerClass {
150 *     public ProtectedInnerClass () {}
151 *   }
152 * }
153 *
154 * package b;
155 * import a.ClassExample;
156 * public class ClassExtending extends ClassExample {
157 *   ProtectedInnerClass pc = new ProtectedInnerClass();
158 * }
159 * </pre>
160 * <ul>
161 * <li>
162 * Property {@code jdkVersion} - Set the JDK version that you are using.
163 * Old JDK version numbering is supported (e.g. 1.8 for Java 8)
164 * as well as just the major JDK version alone (e.g. 8) is supported.
165 * This property only considers features from officially released
166 * Java versions as supported. Features introduced in preview releases are not considered
167 * supported until they are included in a non-preview release.
168 * Type is {@code java.lang.String}.
169 * Default value is {@code "22"}.
170 * </li>
171 * <li>
172 * Property {@code tokens} - tokens to check
173 * Type is {@code java.lang.String[]}.
174 * Validation type is {@code tokenSet}.
175 * Default value is:
176 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
177 * METHOD_DEF</a>,
178 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
179 * VARIABLE_DEF</a>,
180 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">
181 * ANNOTATION_FIELD_DEF</a>,
182 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
183 * INTERFACE_DEF</a>,
184 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF">
185 * CTOR_DEF</a>,
186 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
187 * CLASS_DEF</a>,
188 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
189 * ENUM_DEF</a>,
190 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RESOURCE">
191 * RESOURCE</a>,
192 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_DEF">
193 * ANNOTATION_DEF</a>,
194 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF">
195 * RECORD_DEF</a>,
196 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PATTERN_VARIABLE_DEF">
197 * PATTERN_VARIABLE_DEF</a>,
198 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
199 * LITERAL_CATCH</a>,
200 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
201 * LAMBDA</a>.
202 * </li>
203 * </ul>
204 *
205 * <p>
206 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
207 * </p>
208 *
209 * <p>
210 * Violation Message Keys:
211 * </p>
212 * <ul>
213 * <li>
214 * {@code redundantModifier}
215 * </li>
216 * </ul>
217 *
218 * @since 3.0
219 */
220@StatelessCheck
221public class RedundantModifierCheck
222    extends AbstractCheck {
223
224    /**
225     * A key is pointing to the warning message text in "messages.properties"
226     * file.
227     */
228    public static final String MSG_KEY = "redundantModifier";
229
230    /**
231     * An array of tokens for interface modifiers.
232     */
233    private static final int[] TOKENS_FOR_INTERFACE_MODIFIERS = {
234        TokenTypes.LITERAL_STATIC,
235        TokenTypes.ABSTRACT,
236    };
237
238    /**
239     *  Constant for jdk 22 version number.
240     */
241    private static final int JDK_22 = 22;
242
243    /**
244     *  Constant for jdk 17 version number.
245     *
246     */
247    private static final int JDK_17 = 17;
248
249    /**
250     * Set the JDK version that you are using.
251     * Old JDK version numbering is supported (e.g. 1.8 for Java 8)
252     * as well as just the major JDK version alone (e.g. 8) is supported.
253     * This property only considers features from officially released
254     * Java versions as supported. Features introduced in preview releases are not considered
255     * supported until they are included in a non-preview release.
256     *
257     */
258    private int jdkVersion = JDK_22;
259
260    /**
261     * Setter to set the JDK version that you are using.
262     * Old JDK version numbering is supported (e.g. 1.8 for Java 8)
263     * as well as just the major JDK version alone (e.g. 8) is supported.
264     * This property only considers features from officially released
265     * Java versions as supported. Features introduced in preview releases are not considered
266     * supported until they are included in a non-preview release.
267     *
268     * @param jdkVersion the Java version
269     * @since 10.18.0
270     */
271    public void setJdkVersion(String jdkVersion) {
272        final String singleVersionNumber;
273        if (jdkVersion.startsWith("1.")) {
274            singleVersionNumber = jdkVersion.substring(2);
275        }
276        else {
277            singleVersionNumber = jdkVersion;
278        }
279
280        this.jdkVersion = Integer.parseInt(singleVersionNumber);
281    }
282
283    @Override
284    public int[] getDefaultTokens() {
285        return getAcceptableTokens();
286    }
287
288    @Override
289    public int[] getRequiredTokens() {
290        return CommonUtil.EMPTY_INT_ARRAY;
291    }
292
293    @Override
294    public int[] getAcceptableTokens() {
295        return new int[] {
296            TokenTypes.METHOD_DEF,
297            TokenTypes.VARIABLE_DEF,
298            TokenTypes.ANNOTATION_FIELD_DEF,
299            TokenTypes.INTERFACE_DEF,
300            TokenTypes.CTOR_DEF,
301            TokenTypes.CLASS_DEF,
302            TokenTypes.ENUM_DEF,
303            TokenTypes.RESOURCE,
304            TokenTypes.ANNOTATION_DEF,
305            TokenTypes.RECORD_DEF,
306            TokenTypes.PATTERN_VARIABLE_DEF,
307            TokenTypes.LITERAL_CATCH,
308            TokenTypes.LAMBDA,
309        };
310    }
311
312    @Override
313    public void visitToken(DetailAST ast) {
314        switch (ast.getType()) {
315            case TokenTypes.INTERFACE_DEF:
316            case TokenTypes.ANNOTATION_DEF:
317                checkInterfaceModifiers(ast);
318                break;
319            case TokenTypes.ENUM_DEF:
320                checkForRedundantModifier(ast, TokenTypes.LITERAL_STATIC);
321                break;
322            case TokenTypes.CTOR_DEF:
323                checkConstructorModifiers(ast);
324                break;
325            case TokenTypes.METHOD_DEF:
326                processMethods(ast);
327                break;
328            case TokenTypes.RESOURCE:
329                processResources(ast);
330                break;
331            case TokenTypes.RECORD_DEF:
332                checkForRedundantModifier(ast, TokenTypes.FINAL, TokenTypes.LITERAL_STATIC);
333                break;
334            case TokenTypes.VARIABLE_DEF:
335            case TokenTypes.PATTERN_VARIABLE_DEF:
336                checkUnnamedVariables(ast);
337                break;
338            case TokenTypes.LITERAL_CATCH:
339                checkUnnamedVariables(ast.findFirstToken(TokenTypes.PARAMETER_DEF));
340                break;
341            case TokenTypes.LAMBDA:
342                processLambdaParameters(ast);
343                break;
344            case TokenTypes.CLASS_DEF:
345            case TokenTypes.ANNOTATION_FIELD_DEF:
346                break;
347            default:
348                throw new IllegalStateException("Unexpected token type: " + ast.getType());
349        }
350
351        if (isInterfaceOrAnnotationMember(ast)) {
352            processInterfaceOrAnnotation(ast);
353        }
354
355        if (jdkVersion >= JDK_17) {
356            checkForRedundantModifier(ast, TokenTypes.STRICTFP);
357        }
358    }
359
360    /**
361     * Process lambda parameters.
362     *
363     * @param lambdaAst node of type {@link TokenTypes#LAMBDA}
364     */
365    private void processLambdaParameters(DetailAST lambdaAst) {
366        final DetailAST lambdaParameters = lambdaAst.findFirstToken(TokenTypes.PARAMETERS);
367        if (lambdaParameters != null) {
368            TokenUtil.forEachChild(lambdaParameters, TokenTypes.PARAMETER_DEF,
369                    this::checkUnnamedVariables);
370        }
371    }
372
373    /**
374     * Check if the variable is unnamed and has redundant final modifier.
375     *
376     * @param ast node of type {@link TokenTypes#VARIABLE_DEF}
377     *     or {@link TokenTypes#PATTERN_VARIABLE_DEF}
378     *     or {@link TokenTypes#PARAMETER_DEF}
379     */
380    private void checkUnnamedVariables(DetailAST ast) {
381        if (jdkVersion >= JDK_22 && isUnnamedVariable(ast)) {
382            checkForRedundantModifier(ast, TokenTypes.FINAL);
383        }
384    }
385
386    /**
387     * Check if the variable is unnamed.
388     *
389     * @param ast node of type {@link TokenTypes#VARIABLE_DEF}
390     *     or {@link TokenTypes#PATTERN_VARIABLE_DEF}
391     *     or {@link TokenTypes#PARAMETER_DEF}
392     * @return true if the variable is unnamed
393     */
394    private static boolean isUnnamedVariable(DetailAST ast) {
395        return "_".equals(ast.findFirstToken(TokenTypes.IDENT).getText());
396    }
397
398    /**
399     * Check modifiers of constructor.
400     *
401     * @param ctorDefAst ast node of type {@link TokenTypes#CTOR_DEF}
402     */
403    private void checkConstructorModifiers(DetailAST ctorDefAst) {
404        if (isEnumMember(ctorDefAst)) {
405            checkEnumConstructorModifiers(ctorDefAst);
406        }
407        else {
408            checkClassConstructorModifiers(ctorDefAst);
409        }
410    }
411
412    /**
413     * Checks if interface has proper modifiers.
414     *
415     * @param ast interface to check
416     */
417    private void checkInterfaceModifiers(DetailAST ast) {
418        final DetailAST modifiers =
419            ast.findFirstToken(TokenTypes.MODIFIERS);
420
421        for (final int tokenType : TOKENS_FOR_INTERFACE_MODIFIERS) {
422            final DetailAST modifier =
423                    modifiers.findFirstToken(tokenType);
424            if (modifier != null) {
425                log(modifier, MSG_KEY, modifier.getText());
426            }
427        }
428    }
429
430    /**
431     * Check if enum constructor has proper modifiers.
432     *
433     * @param ast constructor of enum
434     */
435    private void checkEnumConstructorModifiers(DetailAST ast) {
436        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
437        TokenUtil.findFirstTokenByPredicate(
438            modifiers, mod -> mod.getType() != TokenTypes.ANNOTATION
439        ).ifPresent(modifier -> log(modifier, MSG_KEY, modifier.getText()));
440    }
441
442    /**
443     * Do validation of interface of annotation.
444     *
445     * @param ast token AST
446     */
447    private void processInterfaceOrAnnotation(DetailAST ast) {
448        final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
449        DetailAST modifier = modifiers.getFirstChild();
450        while (modifier != null) {
451            // javac does not allow final or static in interface methods
452            // order annotation fields hence no need to check that this
453            // is not a method or annotation field
454
455            final int type = modifier.getType();
456            if (type == TokenTypes.LITERAL_PUBLIC
457                || type == TokenTypes.LITERAL_STATIC
458                        && ast.getType() != TokenTypes.METHOD_DEF
459                || type == TokenTypes.ABSTRACT
460                        && ast.getType() != TokenTypes.CLASS_DEF
461                || type == TokenTypes.FINAL
462                        && ast.getType() != TokenTypes.CLASS_DEF) {
463                log(modifier, MSG_KEY, modifier.getText());
464            }
465
466            modifier = modifier.getNextSibling();
467        }
468    }
469
470    /**
471     * Process validation of Methods.
472     *
473     * @param ast method AST
474     */
475    private void processMethods(DetailAST ast) {
476        final DetailAST modifiers =
477                        ast.findFirstToken(TokenTypes.MODIFIERS);
478        // private method?
479        boolean checkFinal =
480            modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) != null;
481        // declared in a final class?
482        DetailAST parent = ast;
483        while (parent != null && !checkFinal) {
484            if (parent.getType() == TokenTypes.CLASS_DEF) {
485                final DetailAST classModifiers =
486                    parent.findFirstToken(TokenTypes.MODIFIERS);
487                checkFinal = classModifiers.findFirstToken(TokenTypes.FINAL) != null;
488                parent = null;
489            }
490            else if (parent.getType() == TokenTypes.LITERAL_NEW
491                    || parent.getType() == TokenTypes.ENUM_CONSTANT_DEF) {
492                checkFinal = true;
493                parent = null;
494            }
495            else if (parent.getType() == TokenTypes.ENUM_DEF) {
496                checkFinal = modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
497                parent = null;
498            }
499            else {
500                parent = parent.getParent();
501            }
502        }
503        if (checkFinal && !isAnnotatedWithSafeVarargs(ast)) {
504            checkForRedundantModifier(ast, TokenTypes.FINAL);
505        }
506
507        if (ast.findFirstToken(TokenTypes.SLIST) == null) {
508            processAbstractMethodParameters(ast);
509        }
510    }
511
512    /**
513     * Process validation of parameters for Methods with no definition.
514     *
515     * @param ast method AST
516     */
517    private void processAbstractMethodParameters(DetailAST ast) {
518        final DetailAST parameters = ast.findFirstToken(TokenTypes.PARAMETERS);
519        TokenUtil.forEachChild(parameters, TokenTypes.PARAMETER_DEF, paramDef -> {
520            checkForRedundantModifier(paramDef, TokenTypes.FINAL);
521        });
522    }
523
524    /**
525     * Check if class constructor has proper modifiers.
526     *
527     * @param classCtorAst class constructor ast
528     */
529    private void checkClassConstructorModifiers(DetailAST classCtorAst) {
530        final DetailAST classDef = classCtorAst.getParent().getParent();
531        if (!isClassPublic(classDef) && !isClassProtected(classDef)) {
532            checkForRedundantModifier(classCtorAst, TokenTypes.LITERAL_PUBLIC);
533        }
534    }
535
536    /**
537     * Checks if given resource has redundant modifiers.
538     *
539     * @param ast ast
540     */
541    private void processResources(DetailAST ast) {
542        checkForRedundantModifier(ast, TokenTypes.FINAL);
543    }
544
545    /**
546     * Checks if given ast has a redundant modifier.
547     *
548     * @param ast ast
549     * @param modifierTypes The modifiers to check for.
550     */
551    private void checkForRedundantModifier(DetailAST ast, int... modifierTypes) {
552        Optional.ofNullable(ast.findFirstToken(TokenTypes.MODIFIERS))
553            .ifPresent(modifiers -> {
554                for (DetailAST childAst = modifiers.getFirstChild();
555                     childAst != null; childAst = childAst.getNextSibling()) {
556                    if (TokenUtil.isOfType(childAst, modifierTypes)) {
557                        log(childAst, MSG_KEY, childAst.getText());
558                    }
559                }
560            });
561    }
562
563    /**
564     * Checks if given class ast has protected modifier.
565     *
566     * @param classDef class ast
567     * @return true if class is protected, false otherwise
568     */
569    private static boolean isClassProtected(DetailAST classDef) {
570        final DetailAST classModifiers =
571                classDef.findFirstToken(TokenTypes.MODIFIERS);
572        return classModifiers.findFirstToken(TokenTypes.LITERAL_PROTECTED) != null;
573    }
574
575    /**
576     * Checks if given class is accessible from "public" scope.
577     *
578     * @param ast class def to check
579     * @return true if class is accessible from public scope,false otherwise
580     */
581    private static boolean isClassPublic(DetailAST ast) {
582        boolean isAccessibleFromPublic = false;
583        final DetailAST modifiersAst = ast.findFirstToken(TokenTypes.MODIFIERS);
584        final boolean hasPublicModifier =
585                modifiersAst.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null;
586
587        if (TokenUtil.isRootNode(ast.getParent())) {
588            isAccessibleFromPublic = hasPublicModifier;
589        }
590        else {
591            final DetailAST parentClassAst = ast.getParent().getParent();
592
593            if (hasPublicModifier || parentClassAst.getType() == TokenTypes.INTERFACE_DEF) {
594                isAccessibleFromPublic = isClassPublic(parentClassAst);
595            }
596        }
597
598        return isAccessibleFromPublic;
599    }
600
601    /**
602     * Checks if current AST node is member of Enum.
603     *
604     * @param ast AST node
605     * @return true if it is an enum member
606     */
607    private static boolean isEnumMember(DetailAST ast) {
608        final DetailAST parentTypeDef = ast.getParent().getParent();
609        return parentTypeDef.getType() == TokenTypes.ENUM_DEF;
610    }
611
612    /**
613     * Checks if current AST node is member of Interface or Annotation, not of their subnodes.
614     *
615     * @param ast AST node
616     * @return true or false
617     */
618    private static boolean isInterfaceOrAnnotationMember(DetailAST ast) {
619        DetailAST parentTypeDef = ast.getParent();
620        parentTypeDef = parentTypeDef.getParent();
621        return parentTypeDef != null
622                && (parentTypeDef.getType() == TokenTypes.INTERFACE_DEF
623                    || parentTypeDef.getType() == TokenTypes.ANNOTATION_DEF);
624    }
625
626    /**
627     * Checks if method definition is annotated with.
628     * <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/SafeVarargs.html">
629     * SafeVarargs</a> annotation
630     *
631     * @param methodDef method definition node
632     * @return true or false
633     */
634    private static boolean isAnnotatedWithSafeVarargs(DetailAST methodDef) {
635        boolean result = false;
636        final List<DetailAST> methodAnnotationsList = getMethodAnnotationsList(methodDef);
637        for (DetailAST annotationNode : methodAnnotationsList) {
638            if ("SafeVarargs".equals(annotationNode.getLastChild().getText())) {
639                result = true;
640                break;
641            }
642        }
643        return result;
644    }
645
646    /**
647     * Gets the list of annotations on method definition.
648     *
649     * @param methodDef method definition node
650     * @return List of annotations
651     */
652    private static List<DetailAST> getMethodAnnotationsList(DetailAST methodDef) {
653        final List<DetailAST> annotationsList = new ArrayList<>();
654        final DetailAST modifiers = methodDef.findFirstToken(TokenTypes.MODIFIERS);
655        TokenUtil.forEachChild(modifiers, TokenTypes.ANNOTATION, annotationsList::add);
656        return annotationsList;
657    }
658
659}