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 }