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