View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.naming;
21  
22  import java.util.Arrays;
23  import java.util.Optional;
24  
25  import com.puppycrawl.tools.checkstyle.api.DetailAST;
26  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
27  import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
28  
29  /**
30   * <div>
31   * Checks that method parameter names conform to a specified pattern.
32   * By using {@code accessModifiers} property it is possible
33   * to specify different formats for methods at different visibility levels.
34   * </div>
35   *
36   * <p>
37   * To validate {@code catch} parameters please use
38   * <a href="https://checkstyle.org/checks/naming/catchparametername.html">
39   * CatchParameterName</a>.
40   * </p>
41   *
42   * <p>
43   * To validate lambda parameters please use
44   * <a href="https://checkstyle.org/checks/naming/lambdaparametername.html">
45   * LambdaParameterName</a>.
46   * </p>
47   *
48   * @since 3.0
49   */
50  public class ParameterNameCheck extends AbstractNameCheck {
51  
52      /**
53       * A key is pointing to the warning message text in "messages.properties"
54       * file.
55       */
56      public static final String MSG_INVALID_PATTERN = "name.invalidPattern";
57  
58      /**
59       * Allows to skip methods with Override annotation from validation.
60       */
61      private boolean ignoreOverridden;
62  
63      /** Access modifiers of methods where parameters are checked. */
64      private AccessModifierOption[] accessModifiers = {
65          AccessModifierOption.PUBLIC,
66          AccessModifierOption.PROTECTED,
67          AccessModifierOption.PACKAGE,
68          AccessModifierOption.PRIVATE,
69      };
70  
71      /**
72       * Creates a new {@code ParameterNameCheck} instance.
73       */
74      public ParameterNameCheck() {
75          super("^[a-z][a-zA-Z0-9]*$", MSG_INVALID_PATTERN);
76      }
77  
78      /**
79       * Setter to allows to skip methods with Override annotation from validation.
80       *
81       * @param ignoreOverridden Flag for skipping methods with Override annotation.
82       * @since 6.12.1
83       */
84      public void setIgnoreOverridden(boolean ignoreOverridden) {
85          this.ignoreOverridden = ignoreOverridden;
86      }
87  
88      /**
89       * Setter to access modifiers of methods where parameters are checked.
90       *
91       * @param accessModifiers access modifiers of methods which should be checked.
92       * @since 7.5
93       */
94      public void setAccessModifiers(AccessModifierOption... accessModifiers) {
95          this.accessModifiers =
96              Arrays.copyOf(accessModifiers, accessModifiers.length);
97      }
98  
99      @Override
100     public int[] getDefaultTokens() {
101         return getRequiredTokens();
102     }
103 
104     @Override
105     public int[] getAcceptableTokens() {
106         return getRequiredTokens();
107     }
108 
109     @Override
110     public int[] getRequiredTokens() {
111         return new int[] {TokenTypes.PARAMETER_DEF};
112     }
113 
114     @Override
115     protected boolean mustCheckName(DetailAST ast) {
116         boolean checkName = true;
117         final DetailAST parent = ast.getParent();
118         if (ignoreOverridden && isOverriddenMethod(ast)
119                 || parent.getType() == TokenTypes.LITERAL_CATCH
120                 || parent.getParent().getType() == TokenTypes.LAMBDA
121                 || CheckUtil.isReceiverParameter(ast)
122                 || !matchAccessModifiers(
123                         CheckUtil.getAccessModifierFromModifiersToken(parent.getParent()))) {
124             checkName = false;
125         }
126         return checkName;
127     }
128 
129     /**
130      * Checks whether a method has the correct access modifier to be checked.
131      *
132      * @param accessModifier the access modifier of the method.
133      * @return whether the method matches the expected access modifier.
134      */
135     private boolean matchAccessModifiers(final AccessModifierOption accessModifier) {
136         return Arrays.stream(accessModifiers)
137                 .anyMatch(modifier -> modifier == accessModifier);
138     }
139 
140     /**
141      * Checks whether a method is annotated with Override annotation.
142      *
143      * @param ast method parameter definition token.
144      * @return true if a method is annotated with Override annotation.
145      */
146     private static boolean isOverriddenMethod(DetailAST ast) {
147         boolean overridden = false;
148 
149         final DetailAST parent = ast.getParent().getParent();
150         final Optional<DetailAST> annotation =
151             Optional.ofNullable(parent.getFirstChild().getFirstChild());
152 
153         if (annotation.isPresent()) {
154             final Optional<DetailAST> overrideToken =
155                 Optional.ofNullable(annotation.orElseThrow().findFirstToken(TokenTypes.IDENT));
156             if (overrideToken.isPresent()
157                 && "Override".equals(overrideToken.orElseThrow().getText())) {
158                 overridden = true;
159             }
160         }
161         return overridden;
162     }
163 
164 }