1 //////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code for adherence to a set of rules. 3 // Copyright (C) 2001-2019 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.regex.Pattern; 23 24 import com.puppycrawl.tools.checkstyle.api.DetailAST; 25 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 26 import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 27 28 /** 29 * <p> 30 * Checks that local, non-{@code final} variable names conform to a format specified 31 * by the format property. A catch parameter is considered to be 32 * a local variable. 33 * </p> 34 * <ul> 35 * <li> 36 * Property {@code format} - Specifies valid identifiers. Default value is 37 * {@code "^[a-z][a-zA-Z0-9]*$"}. 38 * </li> 39 * <li> 40 * Property {@code allowOneCharVarInForLoop} - Allow one character variable name in 41 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">initialization expressions</a> 42 * in FOR loop. For example: 43 * <pre> 44 * for (int i = 1; i < 10; i++) {} 45 * </pre> 46 * Default value is {@code false}. 47 * </li> 48 * </ul> 49 * <p> 50 * An example of how to configure the check is: 51 * </p> 52 * <pre> 53 * <module name="LocalVariableName"/> 54 * </pre> 55 * <p>Code Example:</p> 56 * <pre> 57 * class MyClass { 58 * void MyMethod() { 59 * for (int var = 1; var < 10; var++) {} // OK 60 * for (int VAR = 1; VAR < 10; VAR++) {} // violation, name 'VAR' must match 61 * // pattern '^[a-z][a-zA-Z0-9]*$' 62 * for (int i = 1; i < 10; i++) {} // OK 63 * for (int var_1 = 0; var_1 < 10; var_1++) {} // violation, name 'var_1' must match 64 * // pattern '^[a-z][a-zA-Z0-9]*$' 65 * } 66 * } 67 * </pre> 68 * <p> 69 * An example of how to configure the check for names that begin with a lower 70 * case letter, followed by letters, digits, and underscores is: 71 * </p> 72 * <pre> 73 * <module name="LocalVariableName"> 74 * <property name="format" value="^[a-z](_?[a-zA-Z0-9]+)*$"/> 75 * </module> 76 * </pre> 77 * <p>Code Example:</p> 78 * <pre> 79 * class MyClass { 80 * void MyMethod() { 81 * for (int var = 1; var < 10; var++) {} // OK 82 * for (int VAR = 1; VAR < 10; VAR++) {} // violation, name 'VAR' must match 83 * // pattern '^[a-z](_?[a-zA-Z0-9]+)*$' 84 * for (int i = 1; i < 10; i++) {} // OK 85 * for (int var_1 = 0; var_1 < 10; var_1++) {} // OK 86 * } 87 * } 88 * </pre> 89 * <p> 90 * An example of one character variable name in 91 * initialization expression(like "i") in FOR loop: 92 * </p> 93 * <pre> 94 * for(int i = 1; i < 10; i++) {} 95 * </pre> 96 * <p> 97 * An example of how to configure the check to allow one character variable name in 98 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html"> 99 * initialization expressions</a> in FOR loop: 100 * </p> 101 * <pre> 102 * <module name="LocalVariableName"> 103 * <property name="allowOneCharVarInForLoop" value="true"/> 104 * </module> 105 * </pre> 106 * 107 * @since 3.0 108 */ 109 public class LocalVariableNameCheck 110 extends AbstractNameCheck { 111 112 /** Regexp for one-char loop variables. */ 113 private static final Pattern SINGLE_CHAR = Pattern.compile("^[a-z]$"); 114 115 /** 116 * Allow one character variable name in 117 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">initialization expressions</a> 118 * in FOR loop. For example: 119 * <pre> 120 * for (int i = 1; i < 10; i++) {} 121 * </pre> 122 */ 123 private boolean allowOneCharVarInForLoop; 124 125 /** Creates a new {@code LocalVariableNameCheck} instance. */ 126 public LocalVariableNameCheck() { 127 super("^[a-z][a-zA-Z0-9]*$"); 128 } 129 130 /** 131 * Setter to allow one character variable name in 132 * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html">initialization expressions</a> 133 * in FOR loop. For example: 134 * <pre> 135 * for (int i = 1; i < 10; i++) {} 136 * </pre> 137 * 138 * @param allow Flag for allowing or not one character name in FOR loop. 139 */ 140 public final void setAllowOneCharVarInForLoop(boolean allow) { 141 allowOneCharVarInForLoop = allow; 142 } 143 144 @Override 145 public int[] getDefaultTokens() { 146 return getRequiredTokens(); 147 } 148 149 @Override 150 public int[] getAcceptableTokens() { 151 return getRequiredTokens(); 152 } 153 154 @Override 155 public int[] getRequiredTokens() { 156 return new int[] { 157 TokenTypes.VARIABLE_DEF, 158 }; 159 } 160 161 @Override 162 protected final boolean mustCheckName(DetailAST ast) { 163 final boolean result; 164 if (allowOneCharVarInForLoop && isForLoopVariable(ast)) { 165 final String variableName = ast.findFirstToken(TokenTypes.IDENT).getText(); 166 result = !SINGLE_CHAR.matcher(variableName).find(); 167 } 168 else { 169 final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS); 170 final boolean isFinal = modifiersAST.findFirstToken(TokenTypes.FINAL) != null; 171 result = !isFinal && ScopeUtil.isLocalVariableDef(ast); 172 } 173 return result; 174 } 175 176 /** 177 * Checks if a variable is the loop's one. 178 * @param variableDef variable definition. 179 * @return true if a variable is the loop's one. 180 */ 181 private static boolean isForLoopVariable(DetailAST variableDef) { 182 final int parentType = variableDef.getParent().getType(); 183 return parentType == TokenTypes.FOR_INIT 184 || parentType == TokenTypes.FOR_EACH_CLAUSE; 185 } 186 187 }