All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.puppycrawl.tools.checkstyle.checks.coding.ModifiedControlVariableCheck Maven / Gradle / Ivy

Go to download

Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard

There is a newer version: 10.18.1
Show newest version
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2016 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
////////////////////////////////////////////////////////////////////////////////

package com.puppycrawl.tools.checkstyle.checks.coding;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import com.google.common.collect.Sets;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;

/**
 * Check for ensuring that for loop control variables are not modified
 * inside the for block. An example is:
 *
 * 
 * {@code
 * for (int i = 0; i < 1; i++) {
 *     i++;//violation
 * }
 * }
 * 
* Rationale: If the control variable is modified inside the loop * body, the program flow becomes more difficult to follow.
* See * FOR statement specification for more details. *

Examples:

* *
 * <module name="ModifiedControlVariable">
 * </module>
 * 
* *

Such loop would be suppressed: * *

 * {@code
 * for(int i=0; i < 10;) {
 *     i++;
 * }
 * }
 * 
* *

* By default, This Check validates * * Enhanced For-Loop. *

*

* Option 'skipEnhancedForLoopVariable' could be used to skip check of variable * from Enhanced For Loop. *

*

* An example of how to configure the check so that it skips enhanced For Loop Variable is: *

*
 * <module name="ModifiedControlVariable">
 *     <property name="skipEnhancedForLoopVariable" value="true"/>
 * </module>
 * 
*

Example:

* *
 * {@code
 * for (String line: lines) {
 *     line = line.trim();   // it will skip this violation
 * }
 * }
 * 
* * * @author Daniel Grenner * @author liscju */ public final class ModifiedControlVariableCheck extends AbstractCheck { /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_KEY = "modified.control.variable"; /** * Message thrown with IllegalStateException. */ private static final String ILLEGAL_TYPE_OF_TOKEN = "Illegal type of token: "; /** Operations which can change control variable in update part of the loop. */ private static final Set MUTATION_OPERATIONS = Sets.newHashSet(TokenTypes.POST_INC, TokenTypes.POST_DEC, TokenTypes.DEC, TokenTypes.INC, TokenTypes.ASSIGN); /** Stack of block parameters. */ private final Deque> variableStack = new ArrayDeque<>(); /** Controls whether to skip enhanced for-loop variable. */ private boolean skipEnhancedForLoopVariable; /** * Whether to skip enhanced for-loop variable or not. * @param skipEnhancedForLoopVariable whether to skip enhanced for-loop variable */ public void setSkipEnhancedForLoopVariable(boolean skipEnhancedForLoopVariable) { this.skipEnhancedForLoopVariable = skipEnhancedForLoopVariable; } @Override public int[] getDefaultTokens() { return getAcceptableTokens(); } @Override public int[] getRequiredTokens() { return getAcceptableTokens(); } @Override public int[] getAcceptableTokens() { return new int[] { TokenTypes.OBJBLOCK, TokenTypes.LITERAL_FOR, TokenTypes.FOR_ITERATOR, TokenTypes.FOR_EACH_CLAUSE, TokenTypes.ASSIGN, TokenTypes.PLUS_ASSIGN, TokenTypes.MINUS_ASSIGN, TokenTypes.STAR_ASSIGN, TokenTypes.DIV_ASSIGN, TokenTypes.MOD_ASSIGN, TokenTypes.SR_ASSIGN, TokenTypes.BSR_ASSIGN, TokenTypes.SL_ASSIGN, TokenTypes.BAND_ASSIGN, TokenTypes.BXOR_ASSIGN, TokenTypes.BOR_ASSIGN, TokenTypes.INC, TokenTypes.POST_INC, TokenTypes.DEC, TokenTypes.POST_DEC, }; } @Override public void beginTree(DetailAST rootAST) { // clear data variableStack.clear(); variableStack.push(new ArrayDeque()); } @Override public void visitToken(DetailAST ast) { switch (ast.getType()) { case TokenTypes.OBJBLOCK: enterBlock(); break; case TokenTypes.LITERAL_FOR: case TokenTypes.FOR_ITERATOR: case TokenTypes.FOR_EACH_CLAUSE: //we need that Tokens only at leaveToken() break; case TokenTypes.ASSIGN: case TokenTypes.PLUS_ASSIGN: case TokenTypes.MINUS_ASSIGN: case TokenTypes.STAR_ASSIGN: case TokenTypes.DIV_ASSIGN: case TokenTypes.MOD_ASSIGN: case TokenTypes.SR_ASSIGN: case TokenTypes.BSR_ASSIGN: case TokenTypes.SL_ASSIGN: case TokenTypes.BAND_ASSIGN: case TokenTypes.BXOR_ASSIGN: case TokenTypes.BOR_ASSIGN: case TokenTypes.INC: case TokenTypes.POST_INC: case TokenTypes.DEC: case TokenTypes.POST_DEC: checkIdent(ast); break; default: throw new IllegalStateException(ILLEGAL_TYPE_OF_TOKEN + ast); } } @Override public void leaveToken(DetailAST ast) { switch (ast.getType()) { case TokenTypes.FOR_ITERATOR: leaveForIter(ast.getParent()); break; case TokenTypes.FOR_EACH_CLAUSE: if (!skipEnhancedForLoopVariable) { final DetailAST paramDef = ast.findFirstToken(TokenTypes.VARIABLE_DEF); leaveForEach(paramDef); } break; case TokenTypes.LITERAL_FOR: if (!getCurrentVariables().isEmpty()) { leaveForDef(ast); } break; case TokenTypes.OBJBLOCK: exitBlock(); break; case TokenTypes.ASSIGN: case TokenTypes.PLUS_ASSIGN: case TokenTypes.MINUS_ASSIGN: case TokenTypes.STAR_ASSIGN: case TokenTypes.DIV_ASSIGN: case TokenTypes.MOD_ASSIGN: case TokenTypes.SR_ASSIGN: case TokenTypes.BSR_ASSIGN: case TokenTypes.SL_ASSIGN: case TokenTypes.BAND_ASSIGN: case TokenTypes.BXOR_ASSIGN: case TokenTypes.BOR_ASSIGN: case TokenTypes.INC: case TokenTypes.POST_INC: case TokenTypes.DEC: case TokenTypes.POST_DEC: //we need that Tokens only at visitToken() break; default: throw new IllegalStateException(ILLEGAL_TYPE_OF_TOKEN + ast); } } /** * Enters an inner class, which requires a new variable set. */ private void enterBlock() { variableStack.push(new ArrayDeque()); } /** * Leave an inner class, so restore variable set. */ private void exitBlock() { variableStack.pop(); } /** * Get current variable stack. * @return current variable stack */ private Deque getCurrentVariables() { return variableStack.peek(); } /** * Check if ident is parameter. * @param ast ident to check. */ private void checkIdent(DetailAST ast) { if (!getCurrentVariables().isEmpty()) { final DetailAST identAST = ast.getFirstChild(); if (identAST != null && identAST.getType() == TokenTypes.IDENT && getCurrentVariables().contains(identAST.getText())) { log(ast.getLineNo(), ast.getColumnNo(), MSG_KEY, identAST.getText()); } } } /** * Push current variables to the stack. * @param ast a for definition. */ private void leaveForIter(DetailAST ast) { final Set variablesToPutInScope = getVariablesManagedByForLoop(ast); for (String variableName : variablesToPutInScope) { getCurrentVariables().push(variableName); } } /** * Determines which variable are specific to for loop and should not be * change by inner loop body. * @param ast For Loop * @return Set of Variable Name which are managed by for */ private static Set getVariablesManagedByForLoop(DetailAST ast) { final Set initializedVariables = getForInitVariables(ast); final Set iteratingVariables = getForIteratorVariables(ast); return Sets.intersection(initializedVariables, iteratingVariables); } /** * Push current variables to the stack. * @param paramDef a for-each clause variable */ private void leaveForEach(DetailAST paramDef) { final DetailAST paramName = paramDef.findFirstToken(TokenTypes.IDENT); getCurrentVariables().push(paramName.getText()); } /** * Pops the variables from the stack. * @param ast a for definition. */ private void leaveForDef(DetailAST ast) { final DetailAST forInitAST = ast.findFirstToken(TokenTypes.FOR_INIT); if (forInitAST == null) { // this is for-each loop, just pop variables getCurrentVariables().pop(); } else { final Set variablesManagedByForLoop = getVariablesManagedByForLoop(ast); popCurrentVariables(variablesManagedByForLoop.size()); } } /** * Pops given number of variables from currentVariables. * @param count Count of variables to be popped from currentVariables */ private void popCurrentVariables(int count) { for (int i = 0; i < count; i++) { getCurrentVariables().pop(); } } /** * Get all variables initialized In init part of for loop. * @param ast for loop token * @return set of variables initialized in for loop */ private static Set getForInitVariables(DetailAST ast) { final Set initializedVariables = new HashSet<>(); final DetailAST forInitAST = ast.findFirstToken(TokenTypes.FOR_INIT); for (DetailAST parameterDefAST = forInitAST.findFirstToken(TokenTypes.VARIABLE_DEF); parameterDefAST != null; parameterDefAST = parameterDefAST.getNextSibling()) { if (parameterDefAST.getType() == TokenTypes.VARIABLE_DEF) { final DetailAST param = parameterDefAST.findFirstToken(TokenTypes.IDENT); initializedVariables.add(param.getText()); } } return initializedVariables; } /** * Get all variables which for loop iterating part change in every loop. * @param ast for loop literal(TokenTypes.LITERAL_FOR) * @return names of variables change in iterating part of for */ private static Set getForIteratorVariables(DetailAST ast) { final Set iteratorVariables = new HashSet<>(); final DetailAST forIteratorAST = ast.findFirstToken(TokenTypes.FOR_ITERATOR); final DetailAST forUpdateListAST = forIteratorAST.findFirstToken(TokenTypes.ELIST); for (DetailAST iteratingExpressionAST : findChildrenOfExpressionType(forUpdateListAST)) { if (MUTATION_OPERATIONS.contains(iteratingExpressionAST.getType())) { final DetailAST oneVariableOperatorChild = iteratingExpressionAST.getFirstChild(); if (oneVariableOperatorChild.getType() == TokenTypes.IDENT) { iteratorVariables.add(oneVariableOperatorChild.getText()); } } } return iteratorVariables; } /** * Find all child of given AST of type TokenType.EXPR * @param ast parent of expressions to find * @return all child of given ast */ private static List findChildrenOfExpressionType(DetailAST ast) { final List foundExpressions = new LinkedList<>(); if (ast != null) { for (DetailAST iteratingExpressionAST = ast.findFirstToken(TokenTypes.EXPR); iteratingExpressionAST != null; iteratingExpressionAST = iteratingExpressionAST.getNextSibling()) { if (iteratingExpressionAST.getType() == TokenTypes.EXPR) { foundExpressions.add(iteratingExpressionAST.getFirstChild()); } } } return foundExpressions; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy