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 com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.TokenTypes; 024import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 025 026/** 027 * <p> 028 * Checks that local, non-{@code final} variable names conform to a specified pattern. 029 * A catch parameter is considered to be 030 * a local variable. 031 * </p> 032 * <ul> 033 * <li> 034 * Property {@code format} - Specifies valid identifiers. 035 * Type is {@code java.util.regex.Pattern}. 036 * Default value is {@code "^[a-z][a-zA-Z0-9]*$"}. 037 * </li> 038 * <li> 039 * Property {@code allowOneCharVarInForLoop} - Allow one character variable name in 040 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html"> 041 * initialization expressions</a> 042 * in FOR loop if one char variable name is prohibited by {@code format} regexp. For example: 043 * <pre> 044 * for (int i = 1; i < 10; i++) { // OK 045 * int j = 1; // violation 046 * } 047 * for (int K = 1; K < 10; K++) { // OK 048 * int j = 1; // violation 049 * } 050 * List list = new ArrayList(); 051 * for (Object o : list) { // OK 052 * int j = 1; // violation 053 * } 054 * for (Object O : list) { // OK 055 * int j = 1; // violation 056 * } 057 * </pre> 058 * Type is {@code boolean}. 059 * Default value is {@code false}. 060 * </li> 061 * </ul> 062 * <p> 063 * An example of how to configure the check is: 064 * </p> 065 * <pre> 066 * <module name="LocalVariableName"/> 067 * </pre> 068 * <p>Code Example:</p> 069 * <pre> 070 * class MyClass { 071 * void MyMethod() { 072 * for (int var = 1; var < 10; var++) {} // OK 073 * for (int VAR = 1; VAR < 10; VAR++) {} // violation, name 'VAR' must match 074 * // pattern '^[a-z][a-zA-Z0-9]*$' 075 * for (int i = 1; i < 10; i++) {} // OK 076 * for (int var_1 = 0; var_1 < 10; var_1++) {} // violation, name 'var_1' must match 077 * // pattern '^[a-z][a-zA-Z0-9]*$' 078 * } 079 * } 080 * </pre> 081 * <p> 082 * An example of how to configure the check for names that begin with a lower 083 * case letter, followed by letters, digits, and underscores is: 084 * </p> 085 * <pre> 086 * <module name="LocalVariableName"> 087 * <property name="format" value="^[a-z](_?[a-zA-Z0-9]+)*$"/> 088 * </module> 089 * </pre> 090 * <p>Code Example:</p> 091 * <pre> 092 * class MyClass { 093 * void MyMethod() { 094 * for (int var = 1; var < 10; var++) {} // OK 095 * for (int VAR = 1; VAR < 10; VAR++) {} // violation, name 'VAR' must match 096 * // pattern '^[a-z](_?[a-zA-Z0-9]+)*$' 097 * for (int i = 1; i < 10; i++) {} // OK 098 * for (int var_1 = 0; var_1 < 10; var_1++) {} // OK 099 * } 100 * } 101 * </pre> 102 * <p> 103 * An example of one character variable name in 104 * initialization expression(like "i") in FOR loop: 105 * </p> 106 * <pre> 107 * for(int i = 1; i < 10; i++) {} 108 * for(int K = 1; K < 10; K++) {} 109 * List list = new ArrayList(); 110 * for (Object o : list) {} 111 * for (Object O : list) {} 112 * </pre> 113 * <p> 114 * An example of how to configure the check to allow one character variable name in 115 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html"> 116 * initialization expressions</a> in FOR loop, where regexp allows 2 or more chars: 117 * </p> 118 * <pre> 119 * <module name="LocalVariableName"> 120 * <property name="format" value="^[a-z][_a-zA-Z0-9]+$"/> 121 * <property name="allowOneCharVarInForLoop" value="true"/> 122 * </module> 123 * </pre> 124 * <p>Code Example:</p> 125 * <pre> 126 * class MyClass { 127 * void MyMethod() { 128 * int good = 1; 129 * int g = 0; // violation 130 * for (int v = 1; v < 10; v++) { // OK 131 * int a = 1; // violation 132 * } 133 * for (int V = 1; V < 10; V++) { // OK 134 * int I = 1; // violation 135 * } 136 * List list = new ArrayList(); 137 * for (Object o : list) { // OK 138 * String a = ""; // violation 139 * } 140 * for (Object O : list) { // OK 141 * String A = ""; // violation 142 * } 143 * } 144 * } 145 * </pre> 146 * <p> 147 * An example of how to configure the check to that all variables have 3 or more chars in name: 148 * </p> 149 * <pre> 150 * <module name="LocalVariableName"> 151 * <property name="format" value="^[a-z][_a-zA-Z0-9]{2,}$"/> 152 * </module> 153 * </pre> 154 * <p>Code Example:</p> 155 * <pre> 156 * class MyClass { 157 * void MyMethod() { 158 * int goodName = 0; 159 * int i = 1; // violation 160 * for (int var = 1; var < 10; var++) { //OK 161 * int j = 1; // violation 162 * } 163 * } 164 * } 165 * </pre> 166 * <p> 167 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 168 * </p> 169 * <p> 170 * Violation Message Keys: 171 * </p> 172 * <ul> 173 * <li> 174 * {@code name.invalidPattern} 175 * </li> 176 * </ul> 177 * 178 * @since 3.0 179 */ 180public class LocalVariableNameCheck 181 extends AbstractNameCheck { 182 183 /** 184 * Allow one character variable name in 185 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html"> 186 * initialization expressions</a> 187 * in FOR loop if one char variable name is prohibited by {@code format} regexp. For example: 188 * <pre> 189 * for (int i = 1; i < 10; i++) { // OK 190 * int j = 1; // violation 191 * } 192 * for (int K = 1; K < 10; K++) { // OK 193 * int j = 1; // violation 194 * } 195 * List list = new ArrayList(); 196 * for (Object o : list) { // OK 197 * int j = 1; // violation 198 * } 199 * for (Object O : list) { // OK 200 * int j = 1; // violation 201 * } 202 * </pre> 203 */ 204 private boolean allowOneCharVarInForLoop; 205 206 /** Creates a new {@code LocalVariableNameCheck} instance. */ 207 public LocalVariableNameCheck() { 208 super("^[a-z][a-zA-Z0-9]*$"); 209 } 210 211 /** 212 * Setter to allow one character variable name in 213 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html"> 214 * initialization expressions</a> 215 * in FOR loop if one char variable name is prohibited by {@code format} regexp. For example: 216 * <pre> 217 * for (int i = 1; i < 10; i++) { // OK 218 * int j = 1; // violation 219 * } 220 * for (int K = 1; K < 10; K++) { // OK 221 * int j = 1; // violation 222 * } 223 * List list = new ArrayList(); 224 * for (Object o : list) { // OK 225 * int j = 1; // violation 226 * } 227 * for (Object O : list) { // OK 228 * int j = 1; // violation 229 * } 230 * </pre> 231 * 232 * @param allow Flag for allowing or not one character name in FOR loop. 233 */ 234 public final void setAllowOneCharVarInForLoop(boolean allow) { 235 allowOneCharVarInForLoop = allow; 236 } 237 238 @Override 239 public int[] getDefaultTokens() { 240 return getRequiredTokens(); 241 } 242 243 @Override 244 public int[] getAcceptableTokens() { 245 return getRequiredTokens(); 246 } 247 248 @Override 249 public int[] getRequiredTokens() { 250 return new int[] { 251 TokenTypes.VARIABLE_DEF, 252 }; 253 } 254 255 @Override 256 protected final boolean mustCheckName(DetailAST ast) { 257 final boolean result; 258 if (allowOneCharVarInForLoop && isForLoopVariable(ast)) { 259 final String variableName = ast.findFirstToken(TokenTypes.IDENT).getText(); 260 result = variableName.length() != 1; 261 } 262 else { 263 final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS); 264 final boolean isFinal = modifiersAST.findFirstToken(TokenTypes.FINAL) != null; 265 result = !isFinal && ScopeUtil.isLocalVariableDef(ast); 266 } 267 return result; 268 } 269 270 /** 271 * Checks if a variable is the loop's one. 272 * 273 * @param variableDef variable definition. 274 * @return true if a variable is the loop's one. 275 */ 276 private static boolean isForLoopVariable(DetailAST variableDef) { 277 final int parentType = variableDef.getParent().getType(); 278 return parentType == TokenTypes.FOR_INIT 279 || parentType == TokenTypes.FOR_EACH_CLAUSE; 280 } 281 282}