001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.coding; 020 021import com.puppycrawl.tools.checkstyle.api.Check; 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.FastStack; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025/** 026 * Check for ensuring that for loop control variables are not modified 027 * inside the for block. 028 * 029 * @author Daniel Grenner 030 */ 031public final class ModifiedControlVariableCheck extends Check 032{ 033 /** Current set of parameters. */ 034 private FastStack<String> currentVariables = FastStack.newInstance(); 035 /** Stack of block parameters. */ 036 private final FastStack<FastStack<String>> variableStack = 037 FastStack.newInstance(); 038 039 @Override 040 public int[] getDefaultTokens() 041 { 042 return new int[] { 043 TokenTypes.OBJBLOCK, 044 TokenTypes.LITERAL_FOR, 045 TokenTypes.FOR_ITERATOR, 046 TokenTypes.FOR_EACH_CLAUSE, 047 TokenTypes.ASSIGN, 048 TokenTypes.PLUS_ASSIGN, 049 TokenTypes.MINUS_ASSIGN, 050 TokenTypes.STAR_ASSIGN, 051 TokenTypes.DIV_ASSIGN, 052 TokenTypes.MOD_ASSIGN, 053 TokenTypes.SR_ASSIGN, 054 TokenTypes.BSR_ASSIGN, 055 TokenTypes.SL_ASSIGN, 056 TokenTypes.BAND_ASSIGN, 057 TokenTypes.BXOR_ASSIGN, 058 TokenTypes.BOR_ASSIGN, 059 TokenTypes.INC, 060 TokenTypes.POST_INC, 061 TokenTypes.DEC, 062 TokenTypes.POST_DEC, 063 }; 064 } 065 066 @Override 067 public int[] getRequiredTokens() 068 { 069 return getDefaultTokens(); 070 } 071 072 @Override 073 public void beginTree(DetailAST rootAST) 074 { 075 // clear data 076 currentVariables.clear(); 077 variableStack.clear(); 078 } 079 080 @Override 081 public void visitToken(DetailAST ast) 082 { 083 switch (ast.getType()) { 084 case TokenTypes.OBJBLOCK: 085 enterBlock(); 086 break; 087 case TokenTypes.LITERAL_FOR: 088 case TokenTypes.FOR_ITERATOR: 089 case TokenTypes.FOR_EACH_CLAUSE: 090 break; 091 case TokenTypes.ASSIGN: 092 case TokenTypes.PLUS_ASSIGN: 093 case TokenTypes.MINUS_ASSIGN: 094 case TokenTypes.STAR_ASSIGN: 095 case TokenTypes.DIV_ASSIGN: 096 case TokenTypes.MOD_ASSIGN: 097 case TokenTypes.SR_ASSIGN: 098 case TokenTypes.BSR_ASSIGN: 099 case TokenTypes.SL_ASSIGN: 100 case TokenTypes.BAND_ASSIGN: 101 case TokenTypes.BXOR_ASSIGN: 102 case TokenTypes.BOR_ASSIGN: 103 case TokenTypes.INC: 104 case TokenTypes.POST_INC: 105 case TokenTypes.DEC: 106 case TokenTypes.POST_DEC: 107 checkIdent(ast); 108 break; 109 default: 110 throw new IllegalStateException(ast.toString()); 111 } 112 } 113 114 115 @Override 116 public void leaveToken(DetailAST ast) 117 { 118 switch (ast.getType()) { 119 case TokenTypes.FOR_ITERATOR: 120 leaveForIter(ast.getParent()); 121 break; 122 case TokenTypes.FOR_EACH_CLAUSE: 123 leaveForEach(ast); 124 break; 125 case TokenTypes.LITERAL_FOR: 126 leaveForDef(ast); 127 break; 128 case TokenTypes.OBJBLOCK: 129 exitBlock(); 130 break; 131 case TokenTypes.ASSIGN: 132 case TokenTypes.PLUS_ASSIGN: 133 case TokenTypes.MINUS_ASSIGN: 134 case TokenTypes.STAR_ASSIGN: 135 case TokenTypes.DIV_ASSIGN: 136 case TokenTypes.MOD_ASSIGN: 137 case TokenTypes.SR_ASSIGN: 138 case TokenTypes.BSR_ASSIGN: 139 case TokenTypes.SL_ASSIGN: 140 case TokenTypes.BAND_ASSIGN: 141 case TokenTypes.BXOR_ASSIGN: 142 case TokenTypes.BOR_ASSIGN: 143 case TokenTypes.INC: 144 case TokenTypes.POST_INC: 145 case TokenTypes.DEC: 146 case TokenTypes.POST_DEC: 147 // Do nothing 148 break; 149 default: 150 throw new IllegalStateException(ast.toString()); 151 } 152 } 153 154 /** 155 * Enters an inner class, which requires a new variable set. 156 */ 157 private void enterBlock() 158 { 159 variableStack.push(currentVariables); 160 currentVariables = FastStack.newInstance(); 161 162 } 163 /** 164 * Leave an inner class, so restore variable set. 165 */ 166 private void exitBlock() 167 { 168 currentVariables = variableStack.pop(); 169 } 170 171 /** 172 * Check if ident is parameter. 173 * @param ast ident to check. 174 */ 175 private void checkIdent(DetailAST ast) 176 { 177 if ((currentVariables != null) && !currentVariables.isEmpty()) { 178 final DetailAST identAST = ast.getFirstChild(); 179 180 if ((identAST != null) 181 && (identAST.getType() == TokenTypes.IDENT) 182 && currentVariables.contains(identAST.getText())) 183 { 184 log(ast.getLineNo(), ast.getColumnNo(), 185 "modified.control.variable", identAST.getText()); 186 } 187 } 188 } 189 190 /** 191 * Push current variables to the stack. 192 * @param ast a for definition. 193 */ 194 private void leaveForIter(DetailAST ast) 195 { 196 final DetailAST forInitAST = ast.findFirstToken(TokenTypes.FOR_INIT); 197 DetailAST parameterDefAST = 198 forInitAST.findFirstToken(TokenTypes.VARIABLE_DEF); 199 200 for (; parameterDefAST != null; 201 parameterDefAST = parameterDefAST.getNextSibling()) 202 { 203 if (parameterDefAST.getType() == TokenTypes.VARIABLE_DEF) { 204 final DetailAST param = 205 parameterDefAST.findFirstToken(TokenTypes.IDENT); 206 currentVariables.push(param.getText()); 207 } 208 } 209 } 210 211 /** 212 * Push current variables to the stack. 213 * @param forEach a for-each clause 214 */ 215 private void leaveForEach(DetailAST forEach) 216 { 217 final DetailAST paramDef = 218 forEach.findFirstToken(TokenTypes.VARIABLE_DEF); 219 final DetailAST paramName = paramDef.findFirstToken(TokenTypes.IDENT); 220 currentVariables.push(paramName.getText()); 221 } 222 223 /** 224 * Pops the variables from the stack. 225 * @param ast a for definition. 226 */ 227 private void leaveForDef(DetailAST ast) 228 { 229 final DetailAST forInitAST = ast.findFirstToken(TokenTypes.FOR_INIT); 230 if (forInitAST != null) { 231 DetailAST parameterDefAST = 232 forInitAST.findFirstToken(TokenTypes.VARIABLE_DEF); 233 234 for (; parameterDefAST != null; 235 parameterDefAST = parameterDefAST.getNextSibling()) 236 { 237 if (parameterDefAST.getType() == TokenTypes.VARIABLE_DEF) { 238 currentVariables.pop(); 239 } 240 } 241 } 242 else { 243 // this is for-each loop, just pop veriables 244 currentVariables.pop(); 245 } 246 } 247}