001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2023 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.naming; 021 022import java.util.Arrays; 023import java.util.Optional; 024 025import com.puppycrawl.tools.checkstyle.api.DetailAST; 026import com.puppycrawl.tools.checkstyle.api.TokenTypes; 027import com.puppycrawl.tools.checkstyle.utils.CheckUtil; 028 029/** 030 * <p> 031 * Checks that method parameter names conform to a specified pattern. 032 * By using {@code accessModifiers} property it is possible 033 * to specify different formats for methods at different visibility levels. 034 * </p> 035 * <p> 036 * To validate {@code catch} parameters please use 037 * <a href="https://checkstyle.org/config_naming.html#CatchParameterName">CatchParameterName</a>. 038 * </p> 039 * <p> 040 * To validate lambda parameters please use 041 * <a href="https://checkstyle.org/config_naming.html#LambdaParameterName">LambdaParameterName</a>. 042 * </p> 043 * <ul> 044 * <li> 045 * Property {@code format} - Specifies valid identifiers. 046 * Type is {@code java.util.regex.Pattern}. 047 * Default value is {@code "^[a-z][a-zA-Z0-9]*$"}. 048 * </li> 049 * <li> 050 * Property {@code ignoreOverridden} - Allows to skip methods with Override annotation from 051 * validation. 052 * Type is {@code boolean}. 053 * Default value is {@code false}. 054 * </li> 055 * <li> 056 * Property {@code accessModifiers} - Access modifiers of methods where parameters are 057 * checked. 058 * Type is {@code com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption[]}. 059 * Default value is {@code public, protected, package, private}. 060 * </li> 061 * </ul> 062 * <p> 063 * To configure the check: 064 * </p> 065 * <pre> 066 * <module name="ParameterName"/> 067 * </pre> 068 * <p>Code Example:</p> 069 * <pre> 070 * class MyClass { 071 * void method1(int v1) {} // OK 072 * void method2(int V2) {} // violation, name 'V2' must match pattern '^[a-z][a-zA-Z0-9]*$' 073 * } 074 * </pre> 075 * <p> 076 * An example of how to configure the check for names that begin with 077 * a lower case letter, followed by letters, digits, and underscores: 078 * </p> 079 * <pre> 080 * <module name="ParameterName"> 081 * <property name="format" value="^[a-z][_a-zA-Z0-9]+$"/> 082 * </module> 083 * </pre> 084 * <p>Code Example:</p> 085 * <pre> 086 * class MyClass { 087 * void method1(int v1) {} // OK 088 * void method2(int v_2) {} // OK 089 * void method3(int V3) {} // violation, name 'V3' must match pattern '^[a-z][_a-zA-Z0-9]+$' 090 * } 091 * </pre> 092 * <p> 093 * An example of how to configure the check to skip methods with Override annotation from 094 * validation: 095 * </p> 096 * <pre> 097 * <module name="ParameterName"> 098 * <property name="ignoreOverridden" value="true"/> 099 * </module> 100 * </pre> 101 * <p>Code Example:</p> 102 * <pre> 103 * class MyClass { 104 * void method1(int v1) {} // OK 105 * void method2(int V2) {} // violation, name 'V2' must match pattern '^[a-z][a-zA-Z0-9]*$' 106 * @Override 107 * public boolean equals(Object V3) { // OK 108 * return true; 109 * } 110 * } 111 * </pre> 112 * <p> 113 * An example of how to configure the check for names that begin with a lower case letter, followed 114 * by letters and digits is: 115 * </p> 116 * <pre> 117 * <module name="ParameterName"> 118 * <property name="format" value="^[a-z][a-zA-Z0-9]+$"/> 119 * </module> 120 * </pre> 121 * <p>Code Example:</p> 122 * <pre> 123 * class MyClass { 124 * void method1(int v1) {} // OK 125 * void method2(int v_2) {} // violation, name 'v_2' must match pattern '^[a-z][a-zA-Z0-9]+$' 126 * void method3(int V3) {} // violation, name 'V3' must match pattern '^[a-z][a-zA-Z0-9]+$' 127 * } 128 * </pre> 129 * <p> 130 * The following configuration checks that the parameters always start with two lowercase 131 * characters and, in addition, that public method parameters cannot be one character long: 132 * </p> 133 * <pre> 134 * <module name="ParameterName"> 135 * <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/> 136 * <property name="accessModifiers" 137 * value="protected, package, private"/> 138 * <message key="name.invalidPattern" 139 * value="Parameter name ''{0}'' must match pattern ''{1}''"/> 140 * </module> 141 * <module name="ParameterName"> 142 * <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/> 143 * <property name="accessModifiers" value="public"/> 144 * <message key="name.invalidPattern" 145 * value="Parameter name ''{0}'' must match pattern ''{1}''"/> 146 * </module> 147 * </pre> 148 * <p>Code Example:</p> 149 * <pre> 150 * class MyClass { 151 * void method1(int v1) {} // OK 152 * protected method2(int V2) {} // violation, Parameter name 'V2' 153 * // must match pattern '^[a-z]([a-z0-9][a-zA-Z0-9]*)?$' 154 * private method3(int a) {} // OK 155 * public method4(int b) {} // violation, Parameter name 'b' 156 * // must match pattern '^[a-z][a-z0-9][a-zA-Z0-9]*$' 157 * } 158 * </pre> 159 * <p> 160 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 161 * </p> 162 * <p> 163 * Violation Message Keys: 164 * </p> 165 * <ul> 166 * <li> 167 * {@code name.invalidPattern} 168 * </li> 169 * </ul> 170 * 171 * @since 3.0 172 */ 173public class ParameterNameCheck extends AbstractNameCheck { 174 175 /** 176 * Allows to skip methods with Override annotation from validation. 177 */ 178 private boolean ignoreOverridden; 179 180 /** Access modifiers of methods where parameters are checked. */ 181 private AccessModifierOption[] accessModifiers = { 182 AccessModifierOption.PUBLIC, 183 AccessModifierOption.PROTECTED, 184 AccessModifierOption.PACKAGE, 185 AccessModifierOption.PRIVATE, 186 }; 187 188 /** 189 * Creates a new {@code ParameterNameCheck} instance. 190 */ 191 public ParameterNameCheck() { 192 super("^[a-z][a-zA-Z0-9]*$"); 193 } 194 195 /** 196 * Setter to allows to skip methods with Override annotation from validation. 197 * 198 * @param ignoreOverridden Flag for skipping methods with Override annotation. 199 */ 200 public void setIgnoreOverridden(boolean ignoreOverridden) { 201 this.ignoreOverridden = ignoreOverridden; 202 } 203 204 /** 205 * Setter to access modifiers of methods where parameters are checked. 206 * 207 * @param accessModifiers access modifiers of methods which should be checked. 208 */ 209 public void setAccessModifiers(AccessModifierOption... accessModifiers) { 210 this.accessModifiers = 211 Arrays.copyOf(accessModifiers, accessModifiers.length); 212 } 213 214 @Override 215 public int[] getDefaultTokens() { 216 return getRequiredTokens(); 217 } 218 219 @Override 220 public int[] getAcceptableTokens() { 221 return getRequiredTokens(); 222 } 223 224 @Override 225 public int[] getRequiredTokens() { 226 return new int[] {TokenTypes.PARAMETER_DEF}; 227 } 228 229 @Override 230 protected boolean mustCheckName(DetailAST ast) { 231 boolean checkName = true; 232 final DetailAST parent = ast.getParent(); 233 if (ignoreOverridden && isOverriddenMethod(ast) 234 || parent.getType() == TokenTypes.LITERAL_CATCH 235 || parent.getParent().getType() == TokenTypes.LAMBDA 236 || CheckUtil.isReceiverParameter(ast) 237 || !matchAccessModifiers( 238 CheckUtil.getAccessModifierFromModifiersToken(parent.getParent()))) { 239 checkName = false; 240 } 241 return checkName; 242 } 243 244 /** 245 * Checks whether a method has the correct access modifier to be checked. 246 * 247 * @param accessModifier the access modifier of the method. 248 * @return whether the method matches the expected access modifier. 249 */ 250 private boolean matchAccessModifiers(final AccessModifierOption accessModifier) { 251 return Arrays.stream(accessModifiers) 252 .anyMatch(modifier -> modifier == accessModifier); 253 } 254 255 /** 256 * Checks whether a method is annotated with Override annotation. 257 * 258 * @param ast method parameter definition token. 259 * @return true if a method is annotated with Override annotation. 260 */ 261 private static boolean isOverriddenMethod(DetailAST ast) { 262 boolean overridden = false; 263 264 final DetailAST parent = ast.getParent().getParent(); 265 final Optional<DetailAST> annotation = 266 Optional.ofNullable(parent.getFirstChild().getFirstChild()); 267 268 if (annotation.isPresent()) { 269 final Optional<DetailAST> overrideToken = 270 Optional.ofNullable(annotation.get().findFirstToken(TokenTypes.IDENT)); 271 if (overrideToken.isPresent() && "Override".equals(overrideToken.get().getText())) { 272 overridden = true; 273 } 274 } 275 return overridden; 276 } 277 278}