VariableDeclarationUsageDistanceCheck was imported #223
This commit is contained in:
parent
a5031acb33
commit
c4e886312a
|
|
@ -0,0 +1,892 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// checkstyle: Checks Java source code for adherence to a set of rules.
|
||||
// Copyright (C) 2001-2014 Oliver Burn
|
||||
//
|
||||
// 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.AbstractMap.SimpleEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import antlr.collections.ASTEnumeration;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.api.Check;
|
||||
import com.puppycrawl.tools.checkstyle.api.DetailAST;
|
||||
import com.puppycrawl.tools.checkstyle.api.FullIdent;
|
||||
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Checks the distance between declaration of variable and its first usage.
|
||||
* </p>
|
||||
* Example #1:
|
||||
* <pre>
|
||||
* <code>int count;
|
||||
* a = a + b;
|
||||
* b = a + a;
|
||||
* count = b; // DECLARATION OF VARIABLE 'count'
|
||||
* // SHOULD BE HERE (distance = 3)</code>
|
||||
* </pre>
|
||||
* Example #2:
|
||||
* <pre>
|
||||
* <code>int count;
|
||||
* {
|
||||
* a = a + b;
|
||||
* count = b; // DECLARATION OF VARIABLE 'count'
|
||||
* // SHOULD BE HERE (distance = 2)
|
||||
* }</code>
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Check can detect a block of initialization methods. If a variable is used in
|
||||
* such a block and there is no other statements after this variable then distance=1.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Case #1:</b>
|
||||
* <pre>
|
||||
* int <b>minutes</b> = 5;
|
||||
* Calendar cal = Calendar.getInstance();
|
||||
* cal.setTimeInMillis(timeNow);
|
||||
* cal.set(Calendar.SECOND, 0);
|
||||
* cal.set(Calendar.MILLISECOND, 0);
|
||||
* cal.set(Calendar.HOUR_OF_DAY, hh);
|
||||
* cal.set(Calendar.MINUTE, <b>minutes</b>);
|
||||
*
|
||||
* The distance for the variable <b>minutes</b> is 1 even
|
||||
* though this variable is used in the fifth method's call.
|
||||
* </pre>
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Case #2:</b>
|
||||
* <pre>
|
||||
* int <b>minutes</b> = 5;
|
||||
* Calendar cal = Calendar.getInstance();
|
||||
* cal.setTimeInMillis(timeNow);
|
||||
* cal.set(Calendar.SECOND, 0);
|
||||
* cal.set(Calendar.MILLISECOND, 0);
|
||||
* <i>System.out.println(cal);</i>
|
||||
* cal.set(Calendar.HOUR_OF_DAY, hh);
|
||||
* cal.set(Calendar.MINUTE, <b>minutes</b>);
|
||||
*
|
||||
* The distance for the variable <b>minutes</b> is 6 because there is one more expression
|
||||
* (except the initialization block) between the declaration of this variable and its usage.
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* There are several additional options to configure the check:
|
||||
* <pre>
|
||||
* 1. allowedDistance - allows to set a distance
|
||||
* between declaration of variable and its first usage.
|
||||
* 2. ignoreVariablePattern - allows to set a RegEx pattern for
|
||||
* ignoring the distance calculation for variables listed in this pattern.
|
||||
* 3. validateBetweenScopes - allows to calculate the distance between
|
||||
* declaration of variable and its first usage in the different scopes.
|
||||
* 4. ignoreFinal - allows to ignore variables with a 'final' modifier.
|
||||
* </pre>
|
||||
* ATTENTION!! (Not supported cases)
|
||||
* <pre>
|
||||
* Case #1:
|
||||
* <code>{
|
||||
* int c;
|
||||
* int a = 3;
|
||||
* int b = 2;
|
||||
* {
|
||||
* a = a + b;
|
||||
* c = b;
|
||||
* }
|
||||
* }</code>
|
||||
*
|
||||
* Distance for variable 'a' = 1;
|
||||
* Distance for variable 'b' = 1;
|
||||
* Distance for variable 'c' = 2.
|
||||
* </pre>
|
||||
* As distance by default is 1 the Check doesn't raise warning for variables 'a'
|
||||
* and 'b' to move them into the block.
|
||||
* <pre>
|
||||
* Case #2:
|
||||
* <code>int sum = 0;
|
||||
* for (int i = 0; i < 20; i++) {
|
||||
* a++;
|
||||
* b--;
|
||||
* sum++;
|
||||
* if (sum > 10) {
|
||||
* res = true;
|
||||
* }
|
||||
* }</code>
|
||||
* Distance for variable 'sum' = 3.
|
||||
* </pre>
|
||||
* <p>
|
||||
* As the distance is more then the default one, the Check raises warning for variable
|
||||
* 'sum' to move it into the 'for(...)' block. But there is situation when
|
||||
* variable 'sum' hasn't to be 0 within each iteration. So, to avoid such
|
||||
* warnings you can use Suppression Filter, provided by Checkstyle, for the
|
||||
* whole class.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* An example how to configure this Check:
|
||||
* </p>
|
||||
* <pre>
|
||||
* <module name="VariableDeclarationUsageDistance"/>
|
||||
* </pre>
|
||||
* <p>
|
||||
* An example of how to configure this Check:
|
||||
* - to set the allowed distance to 4;
|
||||
* - to ignore variables with prefix '^temp';
|
||||
* - to force the validation between scopes;
|
||||
* - to check the final variables;
|
||||
* </p>
|
||||
* <pre>
|
||||
* <module name="VariableDeclarationUsageDistance">
|
||||
* <property name="allowedDistance" value="4">
|
||||
* <property name="ignoreVariablePattern" value="^temp.*">
|
||||
* <property name="validateBetweenScopes" value="true">
|
||||
* <property name="mIgnoreFinal" value="false">
|
||||
* </module>
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko</a>
|
||||
* @author <a href="mailto:barataliba@gmail.com">Baratali Izmailov</a>
|
||||
*/
|
||||
public class VariableDeclarationUsageDistanceCheck extends Check
|
||||
{
|
||||
/**
|
||||
* Warning message key.
|
||||
*/
|
||||
public static final String MSG_KEY = "variable.declaration.usage.distance";
|
||||
|
||||
/**
|
||||
* Default value of distance between declaration of variable and its first
|
||||
* usage.
|
||||
*/
|
||||
private static final int DEFAULT_DISTANCE = 3;
|
||||
|
||||
/** Allowed distance between declaration of variable and its first usage. */
|
||||
private int mAllowedDistance = DEFAULT_DISTANCE;
|
||||
|
||||
/**
|
||||
* RegExp pattern to ignore distance calculation for variables listed in
|
||||
* this pattern.
|
||||
*/
|
||||
private Pattern mIgnoreVariablePattern = Pattern.compile("");
|
||||
|
||||
/**
|
||||
* Allows to calculate distance between declaration of variable and its
|
||||
* first usage in different scopes.
|
||||
*/
|
||||
private boolean mValidateBetweenScopes;
|
||||
|
||||
/** Allows to ignore variables with 'final' modifier. */
|
||||
private boolean mIgnoreFinal = true;
|
||||
|
||||
/**
|
||||
* Sets an allowed distance between declaration of variable and its first
|
||||
* usage.
|
||||
* @param aAllowedDistance
|
||||
* Allowed distance between declaration of variable and its first
|
||||
* usage.
|
||||
*/
|
||||
public void setAllowedDistance(int aAllowedDistance)
|
||||
{
|
||||
this.mAllowedDistance = aAllowedDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets RegExp pattern to ignore distance calculation for variables listed
|
||||
* in this pattern.
|
||||
* @param aIgnorePattern
|
||||
* Pattern contains ignored variables.
|
||||
*/
|
||||
public void setIgnoreVariablePattern(String aIgnorePattern)
|
||||
{
|
||||
this.mIgnoreVariablePattern = Pattern.compile(aIgnorePattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets option which allows to calculate distance between declaration of
|
||||
* variable and its first usage in different scopes.
|
||||
* @param aValidateBetweenScopes
|
||||
* Defines if allow to calculate distance between declaration of
|
||||
* variable and its first usage in different scopes or not.
|
||||
*/
|
||||
public void setValidateBetweenScopes(boolean aValidateBetweenScopes)
|
||||
{
|
||||
this.mValidateBetweenScopes = aValidateBetweenScopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ignore option for variables with 'final' modifier.
|
||||
* @param aIgnoreFinal
|
||||
* Defines if ignore variables with 'final' modifier or not.
|
||||
*/
|
||||
public void setIgnoreFinal(boolean aIgnoreFinal)
|
||||
{
|
||||
this.mIgnoreFinal = aIgnoreFinal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getDefaultTokens()
|
||||
{
|
||||
return new int[] {TokenTypes.VARIABLE_DEF};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitToken(DetailAST aAST)
|
||||
{
|
||||
final int parentType = aAST.getParent().getType();
|
||||
final DetailAST modifiers = aAST.getFirstChild();
|
||||
|
||||
if ((mIgnoreFinal && modifiers.branchContains(TokenTypes.FINAL))
|
||||
|| parentType == TokenTypes.OBJBLOCK)
|
||||
{
|
||||
;// no code
|
||||
}
|
||||
else {
|
||||
final DetailAST variable = aAST.findFirstToken(TokenTypes.IDENT);
|
||||
|
||||
if (!isVariableMatchesIgnorePattern(variable.getText())) {
|
||||
final DetailAST semicolonAst = aAST.getNextSibling();
|
||||
Entry<DetailAST, Integer> entry = null;
|
||||
if (mValidateBetweenScopes) {
|
||||
entry = calculateDistanceBetweenScopes(semicolonAst, variable);
|
||||
}
|
||||
else {
|
||||
entry = calculateDistanceInSingleScope(semicolonAst, variable);
|
||||
}
|
||||
final DetailAST variableUsageAst = entry.getKey();
|
||||
final int dist = entry.getValue();
|
||||
if (dist > mAllowedDistance
|
||||
&& !isInitializationSequence(variableUsageAst, variable.getText()))
|
||||
{
|
||||
log(variable.getLineNo(),
|
||||
MSG_KEY, variable.getText(), dist, mAllowedDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name of instance whose method is called.
|
||||
* @param aMethodCallAst
|
||||
* DetailAST of METHOD_CALL.
|
||||
* @return name of instance.
|
||||
*/
|
||||
private static String getInstanceName(DetailAST aMethodCallAst)
|
||||
{
|
||||
final String methodCallName =
|
||||
FullIdent.createFullIdentBelow(aMethodCallAst).getText();
|
||||
final int lastDotIndex = methodCallName.lastIndexOf('.');
|
||||
String instanceName = "";
|
||||
if (lastDotIndex != -1) {
|
||||
instanceName = methodCallName.substring(0, lastDotIndex);
|
||||
}
|
||||
return instanceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes statements until usage of variable to detect sequence of
|
||||
* initialization methods.
|
||||
* @param aVariableUsageAst
|
||||
* DetailAST of expression that uses variable named aVariableName.
|
||||
* @param aVariableName
|
||||
* name of considered variable.
|
||||
* @return true if statements between declaration and usage of variable are
|
||||
* initialization methods.
|
||||
*/
|
||||
private static boolean isInitializationSequence(
|
||||
DetailAST aVariableUsageAst, String aVariableName)
|
||||
{
|
||||
boolean result = true;
|
||||
boolean isUsedVariableDeclarationFound = false;
|
||||
DetailAST currentSiblingAst = aVariableUsageAst;
|
||||
String initInstanceName = "";
|
||||
|
||||
while (result
|
||||
&& !isUsedVariableDeclarationFound
|
||||
&& currentSiblingAst != null)
|
||||
{
|
||||
|
||||
switch (currentSiblingAst.getType()) {
|
||||
|
||||
case TokenTypes.EXPR:
|
||||
final DetailAST methodCallAst = currentSiblingAst.getFirstChild();
|
||||
|
||||
if (methodCallAst != null
|
||||
&& methodCallAst.getType() == TokenTypes.METHOD_CALL)
|
||||
{
|
||||
final String instanceName =
|
||||
getInstanceName(methodCallAst);
|
||||
// method is called without instance
|
||||
if (instanceName.isEmpty()) {
|
||||
result = false;
|
||||
}
|
||||
// differs from previous instance
|
||||
else if (!instanceName.equals(initInstanceName)) {
|
||||
if (!initInstanceName.isEmpty()) {
|
||||
result = false;
|
||||
}
|
||||
else {
|
||||
initInstanceName = instanceName;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // is not method call
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case TokenTypes.VARIABLE_DEF:
|
||||
final String currentVariableName = currentSiblingAst.
|
||||
findFirstToken(TokenTypes.IDENT).getText();
|
||||
isUsedVariableDeclarationFound = aVariableName.equals(currentVariableName);
|
||||
break;
|
||||
|
||||
case TokenTypes.SEMI:
|
||||
break;
|
||||
|
||||
default:
|
||||
result = false;
|
||||
}
|
||||
|
||||
currentSiblingAst = currentSiblingAst.getPreviousSibling();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates distance between declaration of variable and its first usage
|
||||
* in single scope.
|
||||
* @param aSemicolonAst
|
||||
* Regular node of Ast which is checked for content of checking
|
||||
* variable.
|
||||
* @param aVariableIdentAst
|
||||
* Variable which distance is calculated for.
|
||||
* @return entry which contains expression with variable usage and distance.
|
||||
*/
|
||||
private Entry<DetailAST, Integer> calculateDistanceInSingleScope(
|
||||
DetailAST aSemicolonAst, DetailAST aVariableIdentAst)
|
||||
{
|
||||
int dist = 0;
|
||||
boolean firstUsageFound = false;
|
||||
DetailAST currentAst = aSemicolonAst;
|
||||
DetailAST variableUsageAst = null;
|
||||
|
||||
while (!firstUsageFound && currentAst != null
|
||||
&& currentAst.getType() != TokenTypes.RCURLY)
|
||||
{
|
||||
if (currentAst.getFirstChild() != null) {
|
||||
|
||||
if (isChild(currentAst, aVariableIdentAst)) {
|
||||
|
||||
switch (currentAst.getType()) {
|
||||
case TokenTypes.VARIABLE_DEF:
|
||||
dist++;
|
||||
break;
|
||||
case TokenTypes.SLIST:
|
||||
dist = 0;
|
||||
break;
|
||||
case TokenTypes.LITERAL_FOR:
|
||||
case TokenTypes.LITERAL_WHILE:
|
||||
case TokenTypes.LITERAL_DO:
|
||||
case TokenTypes.LITERAL_IF:
|
||||
case TokenTypes.LITERAL_SWITCH:
|
||||
if (isVariableInOperatorExpr(currentAst, aVariableIdentAst)) {
|
||||
dist++;
|
||||
}
|
||||
else { // variable usage is in inner scope
|
||||
// reset counters, because we can't determine distance
|
||||
dist = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (currentAst.branchContains(TokenTypes.SLIST)) {
|
||||
dist = 0;
|
||||
}
|
||||
else {
|
||||
dist++;
|
||||
}
|
||||
}
|
||||
variableUsageAst = currentAst;
|
||||
firstUsageFound = true;
|
||||
}
|
||||
else if (currentAst.getType() != TokenTypes.VARIABLE_DEF) {
|
||||
dist++;
|
||||
}
|
||||
}
|
||||
currentAst = currentAst.getNextSibling();
|
||||
}
|
||||
|
||||
// If variable wasn't used after its declaration, distance is 0.
|
||||
if (!firstUsageFound) {
|
||||
dist = 0;
|
||||
}
|
||||
|
||||
return new SimpleEntry<DetailAST, Integer>(variableUsageAst, dist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates distance between declaration of variable and its first usage
|
||||
* in multiple scopes.
|
||||
* @param aAST
|
||||
* Regular node of Ast which is checked for content of checking
|
||||
* variable.
|
||||
* @param aVariable
|
||||
* Variable which distance is calculated for.
|
||||
* @return entry which contains expression with variable usage and distance.
|
||||
*/
|
||||
private Entry<DetailAST, Integer> calculateDistanceBetweenScopes(
|
||||
DetailAST aAST, DetailAST aVariable)
|
||||
{
|
||||
int dist = 0;
|
||||
DetailAST currentScopeAst = aAST;
|
||||
DetailAST variableUsageAst = null;
|
||||
while (currentScopeAst != null) {
|
||||
final List<DetailAST> variableUsageExpressions = new ArrayList<DetailAST>();
|
||||
DetailAST currentStatementAst = currentScopeAst;
|
||||
currentScopeAst = null;
|
||||
while (currentStatementAst != null
|
||||
&& currentStatementAst.getType() != TokenTypes.RCURLY)
|
||||
{
|
||||
if (currentStatementAst.getFirstChild() != null) {
|
||||
if (isChild(currentStatementAst, aVariable)) {
|
||||
variableUsageExpressions.add(currentStatementAst);
|
||||
}
|
||||
// If expression doesn't contain variable and this variable
|
||||
// hasn't been met yet, than distance + 1.
|
||||
else if (variableUsageExpressions.size() == 0
|
||||
&& currentStatementAst.getType() != TokenTypes.VARIABLE_DEF)
|
||||
{
|
||||
dist++;
|
||||
}
|
||||
}
|
||||
currentStatementAst = currentStatementAst.getNextSibling();
|
||||
}
|
||||
// If variable usage exists in a single scope, then look into
|
||||
// this scope and count distance until variable usage.
|
||||
if (variableUsageExpressions.size() == 1) {
|
||||
final DetailAST blockWithVariableUsage = variableUsageExpressions
|
||||
.get(0);
|
||||
DetailAST exprWithVariableUsage = null;
|
||||
switch (blockWithVariableUsage.getType()) {
|
||||
case TokenTypes.VARIABLE_DEF:
|
||||
case TokenTypes.EXPR:
|
||||
dist++;
|
||||
break;
|
||||
case TokenTypes.LITERAL_FOR:
|
||||
case TokenTypes.LITERAL_WHILE:
|
||||
case TokenTypes.LITERAL_DO:
|
||||
exprWithVariableUsage = getFirstNodeInsideForWhileDoWhileBlocks(
|
||||
blockWithVariableUsage, aVariable);
|
||||
break;
|
||||
case TokenTypes.LITERAL_IF:
|
||||
exprWithVariableUsage = getFirstNodeInsideIfBlock(
|
||||
blockWithVariableUsage, aVariable);
|
||||
break;
|
||||
case TokenTypes.LITERAL_SWITCH:
|
||||
exprWithVariableUsage = getFirstNodeInsideSwitchBlock(
|
||||
blockWithVariableUsage, aVariable);
|
||||
break;
|
||||
case TokenTypes.LITERAL_TRY:
|
||||
exprWithVariableUsage =
|
||||
getFirstNodeInsideTryCatchFinallyBlocks(blockWithVariableUsage, aVariable);
|
||||
break;
|
||||
default:
|
||||
exprWithVariableUsage = blockWithVariableUsage.getFirstChild();
|
||||
}
|
||||
currentScopeAst = exprWithVariableUsage;
|
||||
if (exprWithVariableUsage != null) {
|
||||
variableUsageAst = exprWithVariableUsage;
|
||||
}
|
||||
else {
|
||||
variableUsageAst = blockWithVariableUsage;
|
||||
}
|
||||
}
|
||||
// If variable usage exists in different scopes, then distance =
|
||||
// distance until variable first usage.
|
||||
else if (variableUsageExpressions.size() > 1) {
|
||||
dist++;
|
||||
variableUsageAst = variableUsageExpressions.get(0);
|
||||
}
|
||||
// If there's no any variable usage, then distance = 0.
|
||||
else {
|
||||
variableUsageAst = null;
|
||||
}
|
||||
}
|
||||
return new SimpleEntry<DetailAST, Integer>(variableUsageAst, dist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets first Ast node inside FOR, WHILE or DO-WHILE blocks if variable
|
||||
* usage is met only inside the block (not in its declaration!).
|
||||
* @param aBlock
|
||||
* Ast node represents FOR, WHILE or DO-WHILE block.
|
||||
* @param aVariable
|
||||
* Variable which is checked for content in block.
|
||||
* @return If variable usage is met only inside the block
|
||||
* (not in its declaration!) than return the first Ast node
|
||||
* of this block, otherwise - null.
|
||||
*/
|
||||
private DetailAST getFirstNodeInsideForWhileDoWhileBlocks(
|
||||
DetailAST aBlock, DetailAST aVariable)
|
||||
{
|
||||
DetailAST firstNodeInsideBlock = null;
|
||||
|
||||
if (!isVariableInOperatorExpr(aBlock, aVariable)) {
|
||||
DetailAST currentNode = null;
|
||||
|
||||
// Find currentNode for DO-WHILE block.
|
||||
if (aBlock.getType() == TokenTypes.LITERAL_DO) {
|
||||
currentNode = aBlock.getFirstChild();
|
||||
}
|
||||
// Find currentNode for FOR or WHILE block.
|
||||
else {
|
||||
// Looking for RPAREN ( ')' ) token to mark the end of operator
|
||||
// expression.
|
||||
currentNode = aBlock.findFirstToken(TokenTypes.RPAREN);
|
||||
if (currentNode != null) {
|
||||
currentNode = currentNode.getNextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
if (currentNode != null) {
|
||||
final int currentNodeType = currentNode.getType();
|
||||
|
||||
if (currentNodeType == TokenTypes.SLIST) {
|
||||
firstNodeInsideBlock = currentNode.getFirstChild();
|
||||
}
|
||||
else if (currentNodeType == TokenTypes.VARIABLE_DEF
|
||||
|| currentNodeType == TokenTypes.EXPR)
|
||||
{
|
||||
; // no code
|
||||
}
|
||||
else {
|
||||
firstNodeInsideBlock = currentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return firstNodeInsideBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets first Ast node inside IF block if variable usage is met
|
||||
* only inside the block (not in its declaration!).
|
||||
* @param aBlock
|
||||
* Ast node represents IF block.
|
||||
* @param aVariable
|
||||
* Variable which is checked for content in block.
|
||||
* @return If variable usage is met only inside the block
|
||||
* (not in its declaration!) than return the first Ast node
|
||||
* of this block, otherwise - null.
|
||||
*/
|
||||
private DetailAST getFirstNodeInsideIfBlock(
|
||||
DetailAST aBlock, DetailAST aVariable)
|
||||
{
|
||||
DetailAST firstNodeInsideBlock = null;
|
||||
|
||||
if (!isVariableInOperatorExpr(aBlock, aVariable)) {
|
||||
DetailAST currentNode = aBlock.getLastChild();
|
||||
final List<DetailAST> variableUsageExpressions =
|
||||
new ArrayList<DetailAST>();
|
||||
|
||||
while (currentNode != null
|
||||
&& currentNode.getType() == TokenTypes.LITERAL_ELSE)
|
||||
{
|
||||
final DetailAST previousNode =
|
||||
currentNode.getPreviousSibling();
|
||||
|
||||
// Checking variable usage inside IF block.
|
||||
if (isChild(previousNode, aVariable)) {
|
||||
variableUsageExpressions.add(previousNode);
|
||||
}
|
||||
|
||||
// Looking into ELSE block, get its first child and analyze it.
|
||||
currentNode = currentNode.getFirstChild();
|
||||
|
||||
if (currentNode.getType() == TokenTypes.LITERAL_IF) {
|
||||
currentNode = currentNode.getLastChild();
|
||||
}
|
||||
else if (isChild(currentNode, aVariable)) {
|
||||
variableUsageExpressions.add(currentNode);
|
||||
currentNode = null;
|
||||
}
|
||||
}
|
||||
|
||||
// If IF block doesn't include ELSE than analyze variable usage
|
||||
// only inside IF block.
|
||||
if (currentNode != null
|
||||
&& isChild(currentNode, aVariable))
|
||||
{
|
||||
variableUsageExpressions.add(currentNode);
|
||||
}
|
||||
|
||||
// If variable usage exists in several related blocks, then
|
||||
// firstNodeInsideBlock = null, otherwise if variable usage exists
|
||||
// only inside one block, then get node from
|
||||
// variableUsageExpressions.
|
||||
if (variableUsageExpressions.size() == 1) {
|
||||
firstNodeInsideBlock = variableUsageExpressions.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
return firstNodeInsideBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets first Ast node inside SWITCH block if variable usage is met
|
||||
* only inside the block (not in its declaration!).
|
||||
* @param aBlock
|
||||
* Ast node represents SWITCH block.
|
||||
* @param aVariable
|
||||
* Variable which is checked for content in block.
|
||||
* @return If variable usage is met only inside the block
|
||||
* (not in its declaration!) than return the first Ast node
|
||||
* of this block, otherwise - null.
|
||||
*/
|
||||
private DetailAST getFirstNodeInsideSwitchBlock(
|
||||
DetailAST aBlock, DetailAST aVariable)
|
||||
{
|
||||
DetailAST firstNodeInsideBlock = null;
|
||||
|
||||
if (!isVariableInOperatorExpr(aBlock, aVariable)) {
|
||||
DetailAST currentNode = aBlock
|
||||
.findFirstToken(TokenTypes.CASE_GROUP);
|
||||
final List<DetailAST> variableUsageExpressions =
|
||||
new ArrayList<DetailAST>();
|
||||
|
||||
// Checking variable usage inside all CASE blocks.
|
||||
while (currentNode != null
|
||||
&& currentNode.getType() == TokenTypes.CASE_GROUP)
|
||||
{
|
||||
final DetailAST lastNodeInCaseGroup =
|
||||
currentNode.getLastChild();
|
||||
|
||||
if (isChild(lastNodeInCaseGroup, aVariable)) {
|
||||
variableUsageExpressions.add(lastNodeInCaseGroup);
|
||||
}
|
||||
currentNode = currentNode.getNextSibling();
|
||||
}
|
||||
|
||||
// If variable usage exists in several related blocks, then
|
||||
// firstNodeInsideBlock = null, otherwise if variable usage exists
|
||||
// only inside one block, then get node from
|
||||
// variableUsageExpressions.
|
||||
if (variableUsageExpressions.size() == 1) {
|
||||
firstNodeInsideBlock = variableUsageExpressions.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
return firstNodeInsideBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets first Ast node inside TRY-CATCH-FINALLY blocks if variable usage is
|
||||
* met only inside the block (not in its declaration!).
|
||||
* @param aBlock
|
||||
* Ast node represents TRY-CATCH-FINALLY block.
|
||||
* @param aVariable
|
||||
* Variable which is checked for content in block.
|
||||
* @return If variable usage is met only inside the block
|
||||
* (not in its declaration!) than return the first Ast node
|
||||
* of this block, otherwise - null.
|
||||
*/
|
||||
private static DetailAST getFirstNodeInsideTryCatchFinallyBlocks(
|
||||
DetailAST aBlock, DetailAST aVariable)
|
||||
{
|
||||
DetailAST currentNode = aBlock.getFirstChild();
|
||||
final List<DetailAST> variableUsageExpressions =
|
||||
new ArrayList<DetailAST>();
|
||||
|
||||
// Checking variable usage inside TRY block.
|
||||
if (isChild(currentNode, aVariable)) {
|
||||
variableUsageExpressions.add(currentNode);
|
||||
}
|
||||
|
||||
// Switch on CATCH block.
|
||||
currentNode = currentNode.getNextSibling();
|
||||
|
||||
// Checking variable usage inside all CATCH blocks.
|
||||
while (currentNode != null
|
||||
&& currentNode.getType() == TokenTypes.LITERAL_CATCH)
|
||||
{
|
||||
final DetailAST catchBlock = currentNode.getLastChild();
|
||||
|
||||
if (isChild(catchBlock, aVariable)) {
|
||||
variableUsageExpressions.add(catchBlock);
|
||||
}
|
||||
currentNode = currentNode.getNextSibling();
|
||||
}
|
||||
|
||||
// Checking variable usage inside FINALLY block.
|
||||
if (currentNode != null) {
|
||||
final DetailAST finalBlock = currentNode.getLastChild();
|
||||
|
||||
if (isChild(finalBlock, aVariable)) {
|
||||
variableUsageExpressions.add(finalBlock);
|
||||
}
|
||||
}
|
||||
|
||||
DetailAST variableUsageNode = null;
|
||||
|
||||
// If variable usage exists in several related blocks, then
|
||||
// firstNodeInsideBlock = null, otherwise if variable usage exists
|
||||
// only inside one block, then get node from
|
||||
// variableUsageExpressions.
|
||||
if (variableUsageExpressions.size() == 1) {
|
||||
variableUsageNode = variableUsageExpressions.get(0).getFirstChild();
|
||||
}
|
||||
|
||||
return variableUsageNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if variable is in operator declaration. For instance:
|
||||
* <pre>
|
||||
* boolean b = true;
|
||||
* if (b) {...}
|
||||
* </pre>
|
||||
* Variable 'b' is in declaration of operator IF.
|
||||
* @param aOperator
|
||||
* Ast node which represents operator.
|
||||
* @param aVariable
|
||||
* Variable which is checked for content in operator.
|
||||
* @return true if operator contains variable in its declaration, otherwise
|
||||
* - false.
|
||||
*/
|
||||
private boolean isVariableInOperatorExpr(
|
||||
DetailAST aOperator, DetailAST aVariable)
|
||||
{
|
||||
boolean isVarInOperatorDeclr = false;
|
||||
final DetailAST openingBracket =
|
||||
aOperator.findFirstToken(TokenTypes.LPAREN);
|
||||
|
||||
if (openingBracket != null) {
|
||||
// Get EXPR between brackets
|
||||
DetailAST exprBetweenBrackets = openingBracket
|
||||
.getNextSibling();
|
||||
|
||||
// Look if variable is in operator expression
|
||||
while (exprBetweenBrackets.getType() != TokenTypes.RPAREN) {
|
||||
|
||||
if (isChild(exprBetweenBrackets, aVariable)) {
|
||||
isVarInOperatorDeclr = true;
|
||||
break;
|
||||
}
|
||||
exprBetweenBrackets = exprBetweenBrackets.getNextSibling();
|
||||
}
|
||||
|
||||
// Variable may be met in ELSE declaration or in CASE declaration.
|
||||
// So, check variable usage in these declarations.
|
||||
if (!isVarInOperatorDeclr) {
|
||||
switch (aOperator.getType()) {
|
||||
case TokenTypes.LITERAL_IF:
|
||||
final DetailAST elseBlock = aOperator.getLastChild();
|
||||
|
||||
if (elseBlock.getType() == TokenTypes.LITERAL_ELSE) {
|
||||
// Get IF followed by ELSE
|
||||
final DetailAST firstNodeInsideElseBlock = elseBlock
|
||||
.getFirstChild();
|
||||
|
||||
if (firstNodeInsideElseBlock.getType()
|
||||
== TokenTypes.LITERAL_IF)
|
||||
{
|
||||
isVarInOperatorDeclr |=
|
||||
isVariableInOperatorExpr(
|
||||
firstNodeInsideElseBlock,
|
||||
aVariable);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TokenTypes.LITERAL_SWITCH:
|
||||
DetailAST currentCaseBlock = aOperator
|
||||
.findFirstToken(TokenTypes.CASE_GROUP);
|
||||
|
||||
while (currentCaseBlock != null
|
||||
&& currentCaseBlock.getType()
|
||||
== TokenTypes.CASE_GROUP)
|
||||
{
|
||||
final DetailAST firstNodeInsideCaseBlock =
|
||||
currentCaseBlock.getFirstChild();
|
||||
|
||||
if (isChild(firstNodeInsideCaseBlock,
|
||||
aVariable))
|
||||
{
|
||||
isVarInOperatorDeclr = true;
|
||||
break;
|
||||
}
|
||||
currentCaseBlock = currentCaseBlock.getNextSibling();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
;// no code
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isVarInOperatorDeclr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if Ast node contains given element.
|
||||
* @param aParent
|
||||
* Node of AST.
|
||||
* @param aAST
|
||||
* Ast element which is checked for content in Ast node.
|
||||
* @return true if Ast element was found in Ast node, otherwise - false.
|
||||
*/
|
||||
private static boolean isChild(DetailAST aParent, DetailAST aAST)
|
||||
{
|
||||
boolean isChild = false;
|
||||
final ASTEnumeration astList = aParent.findAllPartial(aAST);
|
||||
|
||||
while (astList.hasMoreNodes()) {
|
||||
final DetailAST ast = (DetailAST) astList.nextNode();
|
||||
DetailAST astParent = ast.getParent();
|
||||
|
||||
while (astParent != null) {
|
||||
|
||||
if (astParent.equals(aParent)
|
||||
&& astParent.getLineNo() == aParent.getLineNo())
|
||||
{
|
||||
isChild = true;
|
||||
break;
|
||||
}
|
||||
astParent = astParent.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
return isChild;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entrance variable is contained in ignored pattern.
|
||||
* @param aVariable
|
||||
* Variable which is checked for content in ignored pattern.
|
||||
* @return true if variable was found, otherwise - false.
|
||||
*/
|
||||
private boolean isVariableMatchesIgnorePattern(String aVariable)
|
||||
{
|
||||
final Matcher matcher = mIgnoreVariablePattern.matcher(aVariable);
|
||||
return matcher.matches();
|
||||
}
|
||||
}
|
||||
|
|
@ -64,3 +64,4 @@ unnecessary.paren.literal=Unnecessary parentheses around literal ''{0}''.
|
|||
unnecessary.paren.return=Unnecessary parentheses around return value.
|
||||
unnecessary.paren.string=Unnecessary parentheses around string {0}.
|
||||
package.dir.mismatch=Package declaration does not match directory ''{0}''.
|
||||
variable.declaration.usage.distance=Distance between variable ''{0}'' declaration and its first usage is {1}, but allowed {2}.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,215 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// checkstyle: Checks Java source code for adherence to a set of rules.
|
||||
// Copyright (C) 2001-2014 Oliver Burn
|
||||
//
|
||||
// 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 static com.puppycrawl.tools.checkstyle.checks.coding.VariableDeclarationUsageDistanceCheck.MSG_KEY;
|
||||
import static java.text.MessageFormat.format;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.puppycrawl.tools.checkstyle.BaseCheckTestSupport;
|
||||
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
|
||||
|
||||
public class VariableDeclarationUsageDistanceCheckTest extends
|
||||
BaseCheckTestSupport
|
||||
{
|
||||
@Test
|
||||
public void testGeneralLogic() throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig = createCheckConfig(VariableDeclarationUsageDistanceCheck.class);
|
||||
checkConfig.addAttribute("allowedDistance", "1");
|
||||
checkConfig.addAttribute("ignoreVariablePattern", "");
|
||||
checkConfig.addAttribute("validateBetweenScopes", "true");
|
||||
checkConfig.addAttribute("ignoreFinal", "false");
|
||||
final String[] expected = {
|
||||
"30: " + getCheckMessage(MSG_KEY, "a", 2, 1),
|
||||
"38: " + getCheckMessage(MSG_KEY, "temp", 2, 1),
|
||||
"44: " + getCheckMessage(MSG_KEY, "temp", 2, 1),
|
||||
"57: " + getCheckMessage(MSG_KEY, "count", 2, 1),
|
||||
"71: " + getCheckMessage(MSG_KEY, "count", 4, 1),
|
||||
"96: " + getCheckMessage(MSG_KEY, "arg", 2, 1),
|
||||
"144: " + getCheckMessage(MSG_KEY, "m", 3, 1),
|
||||
"145: " + getCheckMessage(MSG_KEY, "n", 2, 1),
|
||||
"184: " + getCheckMessage(MSG_KEY, "result", 2, 1),
|
||||
"219: " + getCheckMessage(MSG_KEY, "t", 5, 1),
|
||||
"222: " + getCheckMessage(MSG_KEY, "c", 3, 1),
|
||||
"223: " + getCheckMessage(MSG_KEY, "d2", 3, 1),
|
||||
"260: " + getCheckMessage(MSG_KEY, "selected", 2, 1),
|
||||
"261: " + getCheckMessage(MSG_KEY, "model", 2, 1),
|
||||
"287: " + getCheckMessage(MSG_KEY, "sw", 2, 1),
|
||||
"300: " + getCheckMessage(MSG_KEY, "wh", 2, 1),
|
||||
"343: " + getCheckMessage(MSG_KEY, "green", 2, 1),
|
||||
"344: " + getCheckMessage(MSG_KEY, "blue", 3, 1),
|
||||
"367: " + getCheckMessage(MSG_KEY, "intervalMs", 2, 1),
|
||||
"454: " + getCheckMessage(MSG_KEY, "aOpt", 3, 1),
|
||||
"455: " + getCheckMessage(MSG_KEY, "bOpt", 2, 1),
|
||||
"471: " + getCheckMessage(MSG_KEY, "l1", 3, 1),
|
||||
"471: " + getCheckMessage(MSG_KEY, "l2", 2, 1),
|
||||
"479: " + getCheckMessage(MSG_KEY, "myOption", 7, 1),
|
||||
"491: " + getCheckMessage(MSG_KEY, "myOption", 6, 1),
|
||||
"504: " + getCheckMessage(MSG_KEY, "count", 4, 1),
|
||||
"505: " + getCheckMessage(MSG_KEY, "files", 2, 1),
|
||||
"540: " + getCheckMessage(MSG_KEY, "id", 2, 1),
|
||||
"542: " + getCheckMessage(MSG_KEY, "parentId", 3, 1),
|
||||
};
|
||||
verify(checkConfig, getPath("coding/InputVariableDeclarationUsageDistanceCheck.java"), expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDistance() throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig = createCheckConfig(VariableDeclarationUsageDistanceCheck.class);
|
||||
checkConfig.addAttribute("allowedDistance", "3");
|
||||
checkConfig.addAttribute("ignoreVariablePattern", "");
|
||||
checkConfig.addAttribute("validateBetweenScopes", "true");
|
||||
checkConfig.addAttribute("ignoreFinal", "false");
|
||||
final String[] expected = {
|
||||
"71: " + getCheckMessage(MSG_KEY, "count", 4, 3),
|
||||
"219: " + getCheckMessage(MSG_KEY, "t", 5, 3),
|
||||
"479: " + getCheckMessage(MSG_KEY, "myOption", 7, 3),
|
||||
"491: " + getCheckMessage(MSG_KEY, "myOption", 6, 3),
|
||||
"504: " + getCheckMessage(MSG_KEY, "count", 4, 3),
|
||||
};
|
||||
verify(checkConfig, getPath("coding/InputVariableDeclarationUsageDistanceCheck.java"), expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVariableRegExp() throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig = createCheckConfig(VariableDeclarationUsageDistanceCheck.class);
|
||||
checkConfig.addAttribute("allowedDistance", "1");
|
||||
checkConfig.addAttribute("ignoreVariablePattern",
|
||||
"a|b|c|d|block|dist|t|m");
|
||||
checkConfig.addAttribute("validateBetweenScopes", "true");
|
||||
checkConfig.addAttribute("ignoreFinal", "false");
|
||||
final String[] expected = {
|
||||
"38: " + getCheckMessage(MSG_KEY, "temp", 2, 1),
|
||||
"44: " + getCheckMessage(MSG_KEY, "temp", 2, 1),
|
||||
"57: " + getCheckMessage(MSG_KEY, "count", 2, 1),
|
||||
"71: " + getCheckMessage(MSG_KEY, "count", 4, 1),
|
||||
"96: " + getCheckMessage(MSG_KEY, "arg", 2, 1),
|
||||
"145: " + getCheckMessage(MSG_KEY, "n", 2, 1),
|
||||
"184: " + getCheckMessage(MSG_KEY, "result", 2, 1),
|
||||
"223: " + getCheckMessage(MSG_KEY, "d2", 3, 1),
|
||||
"260: " + getCheckMessage(MSG_KEY, "selected", 2, 1),
|
||||
"261: " + getCheckMessage(MSG_KEY, "model", 2, 1),
|
||||
"287: " + getCheckMessage(MSG_KEY, "sw", 2, 1),
|
||||
"300: " + getCheckMessage(MSG_KEY, "wh", 2, 1),
|
||||
"343: " + getCheckMessage(MSG_KEY, "green", 2, 1),
|
||||
"344: " + getCheckMessage(MSG_KEY, "blue", 3, 1),
|
||||
"367: " + getCheckMessage(MSG_KEY, "intervalMs", 2, 1),
|
||||
"454: " + getCheckMessage(MSG_KEY, "aOpt", 3, 1),
|
||||
"455: " + getCheckMessage(MSG_KEY, "bOpt", 2, 1),
|
||||
"471: " + getCheckMessage(MSG_KEY, "l1", 3, 1),
|
||||
"471: " + getCheckMessage(MSG_KEY, "l2", 2, 1),
|
||||
"479: " + getCheckMessage(MSG_KEY, "myOption", 7, 1),
|
||||
"491: " + getCheckMessage(MSG_KEY, "myOption", 6, 1),
|
||||
"504: " + getCheckMessage(MSG_KEY, "count", 4, 1),
|
||||
"505: " + getCheckMessage(MSG_KEY, "files", 2, 1),
|
||||
"540: " + getCheckMessage(MSG_KEY, "id", 2, 1),
|
||||
"542: " + getCheckMessage(MSG_KEY, "parentId", 3, 1),
|
||||
};
|
||||
verify(checkConfig, getPath("coding/InputVariableDeclarationUsageDistanceCheck.java"), expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateBetweenScopesOption() throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig = createCheckConfig(VariableDeclarationUsageDistanceCheck.class);
|
||||
checkConfig.addAttribute("allowedDistance", "1");
|
||||
checkConfig.addAttribute("ignoreVariablePattern", "");
|
||||
checkConfig.addAttribute("validateBetweenScopes", "false");
|
||||
checkConfig.addAttribute("ignoreFinal", "false");
|
||||
final String[] expected = {
|
||||
"30: " + getCheckMessage(MSG_KEY, "a", 2, 1),
|
||||
"38: " + getCheckMessage(MSG_KEY, "temp", 2, 1),
|
||||
"44: " + getCheckMessage(MSG_KEY, "temp", 2, 1),
|
||||
"71: " + getCheckMessage(MSG_KEY, "count", 4, 1),
|
||||
"96: " + getCheckMessage(MSG_KEY, "arg", 2, 1),
|
||||
"219: " + getCheckMessage(MSG_KEY, "t", 5, 1),
|
||||
"222: " + getCheckMessage(MSG_KEY, "c", 3, 1),
|
||||
"223: " + getCheckMessage(MSG_KEY, "d2", 3, 1),
|
||||
"300: " + getCheckMessage(MSG_KEY, "wh", 2, 1),
|
||||
"343: " + getCheckMessage(MSG_KEY, "green", 2, 1),
|
||||
"344: " + getCheckMessage(MSG_KEY, "blue", 3, 1),
|
||||
"367: " + getCheckMessage(MSG_KEY, "intervalMs", 2, 1),
|
||||
"454: " + getCheckMessage(MSG_KEY, "aOpt", 3, 1),
|
||||
"455: " + getCheckMessage(MSG_KEY, "bOpt", 2, 1),
|
||||
"471: " + getCheckMessage(MSG_KEY, "l1", 3, 1),
|
||||
"471: " + getCheckMessage(MSG_KEY, "l2", 2, 1),
|
||||
"479: " + getCheckMessage(MSG_KEY, "myOption", 7, 1),
|
||||
"491: Distance between variable 'myOption' declaration and its first usage is 6, but allowed 1.",
|
||||
"505: Distance between variable 'files' declaration and its first usage is 2, but allowed 1.",
|
||||
"540: Distance between variable 'id' declaration and its first usage is 2, but allowed 1.",
|
||||
"542: Distance between variable 'parentId' declaration and its first usage is 4, but allowed 1.",
|
||||
};
|
||||
verify(checkConfig, getPath("coding/InputVariableDeclarationUsageDistanceCheck.java"), expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoreFinalOption() throws Exception
|
||||
{
|
||||
final DefaultConfiguration checkConfig = createCheckConfig(VariableDeclarationUsageDistanceCheck.class);
|
||||
checkConfig.addAttribute("allowedDistance", "1");
|
||||
checkConfig.addAttribute("ignoreVariablePattern", "");
|
||||
checkConfig.addAttribute("validateBetweenScopes", "true");
|
||||
checkConfig.addAttribute("ignoreFinal", "true");
|
||||
final String[] expected = {
|
||||
"30: " + getCheckMessage(MSG_KEY, "a", 2, 1),
|
||||
"38: " + getCheckMessage(MSG_KEY, "temp", 2, 1),
|
||||
"44: " + getCheckMessage(MSG_KEY, "temp", 2, 1),
|
||||
"57: " + getCheckMessage(MSG_KEY, "count", 2, 1),
|
||||
"71: " + getCheckMessage(MSG_KEY, "count", 4, 1),
|
||||
"96: " + getCheckMessage(MSG_KEY, "arg", 2, 1),
|
||||
"144: " + getCheckMessage(MSG_KEY, "m", 3, 1),
|
||||
"145: " + getCheckMessage(MSG_KEY, "n", 2, 1),
|
||||
"184: " + getCheckMessage(MSG_KEY, "result", 2, 1),
|
||||
"219: " + getCheckMessage(MSG_KEY, "t", 5, 1),
|
||||
"222: " + getCheckMessage(MSG_KEY, "c", 3, 1),
|
||||
"223: " + getCheckMessage(MSG_KEY, "d2", 3, 1),
|
||||
"260: " + getCheckMessage(MSG_KEY, "selected", 2, 1),
|
||||
"261: " + getCheckMessage(MSG_KEY, "model", 2, 1),
|
||||
"287: " + getCheckMessage(MSG_KEY, "sw", 2, 1),
|
||||
"300: " + getCheckMessage(MSG_KEY, "wh", 2, 1),
|
||||
"343: " + getCheckMessage(MSG_KEY, "green", 2, 1),
|
||||
"344: " + getCheckMessage(MSG_KEY, "blue", 3, 1),
|
||||
"454: " + getCheckMessage(MSG_KEY, "aOpt", 3, 1),
|
||||
"455: " + getCheckMessage(MSG_KEY, "bOpt", 2, 1),
|
||||
"471: " + getCheckMessage(MSG_KEY, "l1", 3, 1),
|
||||
"471: " + getCheckMessage(MSG_KEY, "l2", 2, 1),
|
||||
"479: " + getCheckMessage(MSG_KEY, "myOption", 7, 1),
|
||||
"491: " + getCheckMessage(MSG_KEY, "myOption", 6, 1),
|
||||
"504: " + getCheckMessage(MSG_KEY, "count", 4, 1),
|
||||
"505: " + getCheckMessage(MSG_KEY, "files", 2, 1),
|
||||
"540: " + getCheckMessage(MSG_KEY, "id", 2, 1),
|
||||
"542: " + getCheckMessage(MSG_KEY, "parentId", 3, 1),
|
||||
};
|
||||
verify(checkConfig, getPath("coding/InputVariableDeclarationUsageDistanceCheck.java"), expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the check message 'as is' from appropriate 'messages.properties' file.
|
||||
* @param messageKey the key of message in 'messages.properties' file.
|
||||
* @param arguments the arguments of message in 'messages.properties' file.
|
||||
*/
|
||||
public String getCheckMessage(String messageKey, Object ... arguments)
|
||||
{
|
||||
return format(getCheckMessage(messageKey), arguments);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,567 @@
|
|||
package com.puppycrawl.tools.checkstyle.design;
|
||||
|
||||
public class InputVariableDeclarationUsageDistanceCheck {
|
||||
|
||||
private static final int test1;
|
||||
|
||||
static {
|
||||
int b;
|
||||
int d;
|
||||
{
|
||||
d = ++b;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
int c;
|
||||
int a = 3;
|
||||
int b = 2;
|
||||
{
|
||||
a = a + b;
|
||||
c = b;
|
||||
}
|
||||
{
|
||||
c--;
|
||||
}
|
||||
a = 7;
|
||||
}
|
||||
|
||||
static {
|
||||
int a = -1;
|
||||
int b = 2;
|
||||
b++;
|
||||
int c = --b;
|
||||
a = b; // DECLARATION OF VARIABLE 'a' SHOULD BE HERE (distance = 2)
|
||||
}
|
||||
|
||||
public InputVariableDeclarationUsageDistanceCheck(int test1) {
|
||||
int temp = -1;
|
||||
this.test1 = test1;
|
||||
temp = test1; // DECLARATION OF VARIABLE 'temp' SHOULD BE HERE (distance = 2)
|
||||
}
|
||||
|
||||
public boolean testMethod() {
|
||||
int temp = 7;
|
||||
new InputVariableDeclarationUsageDistanceCheck(2);
|
||||
InputVariableDeclarationUsageDistanceCheck(temp); // DECLARATION OF VARIABLE 'temp' SHOULD BE HERE (distance = 2)
|
||||
boolean result = false;
|
||||
String str = "";
|
||||
if (test1 > 1) {
|
||||
str = "123";
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void testMethod2() {
|
||||
int count;
|
||||
int a = 3;
|
||||
int b = 2;
|
||||
{
|
||||
a = a
|
||||
+ b
|
||||
- 5
|
||||
+ 2
|
||||
* a;
|
||||
count = b; // DECLARATION OF VARIABLE 'count' SHOULD BE HERE (distance = 2)
|
||||
}
|
||||
}
|
||||
|
||||
public void testMethod3() {
|
||||
int count;
|
||||
int a = 3;
|
||||
int b = 3;
|
||||
a = a + b;
|
||||
b = a + a;
|
||||
testMethod2();
|
||||
count = b; // DECLARATION OF VARIABLE 'count' SHOULD BE HERE (distance = 4)
|
||||
}
|
||||
|
||||
public void testMethod4(int arg) {
|
||||
int d;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
d++;
|
||||
if (i > 5) {
|
||||
d += arg;
|
||||
}
|
||||
}
|
||||
|
||||
String ar[] = { "1", "2" };
|
||||
for (String st : ar) {
|
||||
System.out.println(st);
|
||||
}
|
||||
}
|
||||
|
||||
public void testMethod5() {
|
||||
int arg = 7;
|
||||
boolean b = true;
|
||||
boolean bb = false;
|
||||
if (b)
|
||||
if (!bb)
|
||||
b = false;
|
||||
testMethod4(arg); // DECLARATION OF VARIABLE 'arg' SHOULD BE HERE (distance = 2)
|
||||
}
|
||||
|
||||
public void testMethod6() {
|
||||
int blockNumWithSimilarVar = 3;
|
||||
int dist = 0;
|
||||
int index = 0;
|
||||
int block = 0;
|
||||
|
||||
if (blockNumWithSimilarVar <= 1) {
|
||||
do {
|
||||
dist++;
|
||||
if (block > 4) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
block++;
|
||||
} while (index < 7);
|
||||
} else {
|
||||
while (index < 8) {
|
||||
dist += block;
|
||||
index++;
|
||||
block++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean testMethod7(int a) {
|
||||
boolean res;
|
||||
switch (a) {
|
||||
case 1:
|
||||
res = true;
|
||||
break;
|
||||
default:
|
||||
res = false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void testMethod8() {
|
||||
int b;
|
||||
int c;
|
||||
int m;
|
||||
int n;
|
||||
{
|
||||
c++;
|
||||
b++;
|
||||
}
|
||||
{
|
||||
n++; // DECLARATION OF VARIABLE 'n' SHOULD BE HERE (distance = 2)
|
||||
m++; // DECLARATION OF VARIABLE 'm' SHOULD BE HERE (distance = 3)
|
||||
b++;
|
||||
}
|
||||
}
|
||||
|
||||
public void testMethod9() {
|
||||
boolean result = false;
|
||||
boolean b1 = true;
|
||||
boolean b2 = false;
|
||||
if (b1) {
|
||||
if (!b2) {
|
||||
result = true;
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean testMethod10() {
|
||||
boolean result;
|
||||
try {
|
||||
result = true;
|
||||
} catch (IOException e) {
|
||||
result = false;
|
||||
} finally {
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void testMethod11() {
|
||||
int a = 0;
|
||||
int b = 10;
|
||||
boolean result;
|
||||
try {
|
||||
b--;
|
||||
} catch (IOException e) {
|
||||
b++;
|
||||
result = false; // DECLARATION OF VARIABLE 'result' SHOULD BE HERE (distance = 2)
|
||||
} finally {
|
||||
a++;
|
||||
}
|
||||
}
|
||||
|
||||
public void testMethod12() {
|
||||
boolean result = false;
|
||||
boolean b3 = true;
|
||||
boolean b1 = true;
|
||||
boolean b2 = false;
|
||||
if (b1) {
|
||||
if (b3) {
|
||||
if (!b2) {
|
||||
result = true;
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testMethod13() {
|
||||
int i = 9;
|
||||
int j = 6;
|
||||
int g = i + 8;
|
||||
int k = j + 10;
|
||||
}
|
||||
|
||||
public void testMethod14() {
|
||||
Session s = openSession();
|
||||
Transaction t = s.beginTransaction();
|
||||
A a = new A();
|
||||
E d1 = new E();
|
||||
C1 c = new C1();
|
||||
E d2 = new E();
|
||||
a.setForward(d1);
|
||||
d1.setReverse(a);
|
||||
c.setForward(d2); // DECLARATION OF VARIABLE 'c' SHOULD BE HERE (distance = 3)
|
||||
// DECLARATION OF VARIABLE 'd2' SHOULD BE HERE (distance = 3)
|
||||
d2.setReverse(c);
|
||||
Serializable aid = s.save(a);
|
||||
Serializable d2id = s.save(d2);
|
||||
t.commit(); // DECLARATION OF VARIABLE 't' SHOULD BE HERE (distance = 5)
|
||||
s.close();
|
||||
}
|
||||
|
||||
public boolean isCheckBoxEnabled(TreePath path) {
|
||||
DataLabelModel model = (DataLabelModel) getModel();
|
||||
if (recursiveState) {
|
||||
for (int index = 0; index < path.getPathCount(); ++index) {
|
||||
int nodeIndex = model.getNodeIndex(path.getPathComponent(index));
|
||||
if (disabled.contains(nodeIndex)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int nodeIndex = model.getNodeIndex(path.getLastPathComponent());
|
||||
if (disabled.contains(nodeIndex)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object readObject(IObjectInputStream in) throws IOException {
|
||||
SimpleDay startDay = new SimpleDay(in.readInt());
|
||||
SimpleDay endDay = new SimpleDay(in.readInt());
|
||||
return new SimplePeriod(startDay, endDay);
|
||||
}
|
||||
|
||||
public int[] getSelectedIndices() {
|
||||
int[] selected = new int[paths.length];
|
||||
DataLabelModel model = (DataLabelModel) getModel();
|
||||
int a = 0;
|
||||
a++;
|
||||
for (int index = 0; index < paths.length; ++index) {
|
||||
selected[index] = model.getNodeIndex(paths[index].getLastPathComponent()); // DECLARATION OF VARIABLE 'selected' SHOULD BE HERE (distance = 2)
|
||||
// DECLARATION OF VARIABLE 'model' SHOULD BE HERE (distance = 2)
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void testMethod15() {
|
||||
String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
|
||||
if (!confDebug.equals("") && !confDebug.equals("null")) {
|
||||
LogLog.warn("The \"" + CONFIG_DEBUG_ATTR + "\" attribute is deprecated.");
|
||||
LogLog.warn("Use the \"" + INTERNAL_DEBUG_ATTR + "\" attribute instead.");
|
||||
LogLog.setInternalDebugging(OptionConverter.toBoolean(confDebug, true));
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
int k = 7;
|
||||
boolean b = false;
|
||||
for (; i < k; i++) {
|
||||
b = true;
|
||||
k++;
|
||||
}
|
||||
|
||||
int sw;
|
||||
switch (i) {
|
||||
case 0:
|
||||
k++;
|
||||
sw = 0; // DECLARATION OF VARIABLE 'sw' SHOULD BE HERE (distance = 2)
|
||||
break;
|
||||
case 1:
|
||||
b = false;
|
||||
break;
|
||||
default:
|
||||
b = true;
|
||||
}
|
||||
|
||||
int wh;
|
||||
b = true;
|
||||
do {
|
||||
k--;
|
||||
i++;
|
||||
} while (wh > 0); // DECLARATION OF VARIABLE 'wh' SHOULD BE HERE (distance = 2)
|
||||
|
||||
if (wh > 0) {
|
||||
k++;
|
||||
} else if (!b) {
|
||||
i++;
|
||||
} else {
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
public void testMethod16() {
|
||||
int wh = 1;
|
||||
if (i > 0) {
|
||||
k++;
|
||||
} else if (wh > 0) {
|
||||
i++;
|
||||
} else {
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
protected JMenuItem createSubMenuItem(LogLevel level) {
|
||||
final JMenuItem result = new JMenuItem(level.toString());
|
||||
final LogLevel logLevel = level;
|
||||
result.setMnemonic(level.toString().charAt(0));
|
||||
result.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
showLogLevelColorChangeDialog(result, logLevel); // DECLARATION OF VARIABLE 'logLevel' SHOULD BE HERE (distance = 2)
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public static Color darker(Color color, double fraction) {
|
||||
int red = (int) Math.round(color.getRed() * (1.0 - fraction));
|
||||
int green = (int) Math.round(color.getGreen() * (1.0 - fraction));
|
||||
int blue = (int) Math.round(color.getBlue() * (1.0 - fraction));
|
||||
|
||||
if (red < 0) {
|
||||
red = 0;
|
||||
} else if (red > 255) {
|
||||
red = 255;
|
||||
}
|
||||
if (green < 0) { // DECLARATION OF VARIABLE 'green' SHOULD BE HERE (distance = 2)
|
||||
green = 0;
|
||||
} else if (green > 255) {
|
||||
green = 255;
|
||||
}
|
||||
if (blue < 0) { // DECLARATION OF VARIABLE 'blue' SHOULD BE HERE (distance = 3)
|
||||
// blue = 0;
|
||||
}
|
||||
|
||||
int alpha = color.getAlpha();
|
||||
|
||||
return new Color(red, green, blue, alpha);
|
||||
}
|
||||
|
||||
public void testFinal() {
|
||||
AuthUpdateTask authUpdateTask = null;
|
||||
final long intervalMs = 30 * 60000L; // 30 min
|
||||
|
||||
authUpdateTask = new AuthUpdateTask(authCheckUrl, authInfo, new IAuthListener() {
|
||||
@Override
|
||||
public void authTokenChanged(String cookie, String token) {
|
||||
fireAuthTokenChanged(cookie, token);
|
||||
}
|
||||
});
|
||||
|
||||
Timer authUpdateTimer = new Timer("Auth Guard", true);
|
||||
authUpdateTimer.schedule(authUpdateTask, intervalMs / 2, intervalMs); // DECLARATION OF VARIABLE 'intervalMs' SHOULD BE HERE (distance = 2)
|
||||
}
|
||||
|
||||
public void testForCycle() {
|
||||
int filterCount = 0;
|
||||
for (int i = 0; i < 10; i++, filterCount++) {
|
||||
int abc = 0;
|
||||
System.out.println(abc);
|
||||
|
||||
for (int j = 0; j < 10; j++) {
|
||||
abc = filterCount;
|
||||
System.out.println(abc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testIssue32_1()
|
||||
{
|
||||
Option srcDdlFile = OptionBuilder.create("f");
|
||||
Option logDdlFile = OptionBuilder.create("o");
|
||||
Option help = OptionBuilder.create("h");
|
||||
|
||||
Options options = new Options();
|
||||
options.something();
|
||||
options.something();
|
||||
options.something();
|
||||
options.something();
|
||||
options.addOption(srcDdlFile, logDdlFile, help); // distance=1
|
||||
}
|
||||
|
||||
public void testIssue32_2()
|
||||
{
|
||||
int mm = Integer.parseInt(time.substring(div + 1).trim());
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(timeNow);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
cal.set(Calendar.HOUR_OF_DAY, hh);
|
||||
cal.set(Calendar.MINUTE, mm); // distance=1
|
||||
}
|
||||
|
||||
public void testIssue32_3(MyObject[] objects) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
for(int i=0; i<objects.length; i++) {
|
||||
objects[i].setEnabled(true);
|
||||
objects[i].setColor(0x121212);
|
||||
objects[i].setUrl("http://google.com");
|
||||
objects[i].setSize(789);
|
||||
objects[i].setCalendar(cal); // distance=1
|
||||
}
|
||||
}
|
||||
|
||||
public void testIssue32_4(boolean flag) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("flag is ");
|
||||
builder.append(flag);
|
||||
final String line = ast.getLineNo();
|
||||
if(flag) {
|
||||
builder.append("line of AST is:");
|
||||
builder.append("\n");
|
||||
builder.append(String.valueOf(line)); //distance=1
|
||||
builder.append("\n");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public void testIssue32_5() {
|
||||
Option a;
|
||||
Option b;
|
||||
Option c;
|
||||
boolean isCNull = isNull(c); // distance=1
|
||||
boolean isBNull = isNull(b); // distance=1
|
||||
boolean isANull = isNull(a); // distance=1
|
||||
}
|
||||
|
||||
public void testIssue32_6() {
|
||||
Option aOpt;
|
||||
Option bOpt;
|
||||
Option cOpt;
|
||||
isNull(cOpt); // distance = 1
|
||||
isNull(bOpt); // distance = 2
|
||||
isNull(aOpt); // distance = 3
|
||||
}
|
||||
|
||||
public void testIssue32_7() {
|
||||
String line = "abc";
|
||||
writer.write(line);
|
||||
line.charAt(1);
|
||||
builder.append(line);
|
||||
test(line, line, line);
|
||||
}
|
||||
|
||||
public void testIssue32_8(Writer w1, Writer w2, Writer w3) {
|
||||
String l1="1", l2="2", l3="3";
|
||||
w1.write(l3); //distance=1
|
||||
w2.write(l2); //distance=2
|
||||
w3.write(l1); //distance=3
|
||||
}
|
||||
|
||||
public void testIssue32_9() {
|
||||
Options options = new Options();
|
||||
Option myOption = null;
|
||||
options.addBindFile(null);
|
||||
options.addBindFile(null);
|
||||
options.addBindFile(null);
|
||||
options.addBindFile(null);
|
||||
options.addBindFile(null);
|
||||
System.out.println("message");
|
||||
myOption.setArgName("abc"); // distance=7
|
||||
}
|
||||
|
||||
public void testIssue32_10() {
|
||||
Options options = new Options();
|
||||
Option myOption = null;
|
||||
options.addBindFile(null);
|
||||
options.addBindFile(null);
|
||||
options.addBindFile(null);
|
||||
options.addBindFile(null);
|
||||
options.addBindFile(null);
|
||||
myOption.setArgName("q"); // distance=6
|
||||
}
|
||||
|
||||
public int testIssue32_11(File toDir)
|
||||
throws IOException, FTPException,
|
||||
ParseException, InterruptedException
|
||||
{
|
||||
int count = 0;
|
||||
FTPFile[] files = client.dirDetails(".");
|
||||
|
||||
log.info("Data archivation started");
|
||||
archiveOldData(archDir, files);
|
||||
log.info("Data archivation finished");
|
||||
|
||||
if (files == null || files.length == 0) {
|
||||
warn("No files on a remote site");
|
||||
}
|
||||
else {
|
||||
log.debug("Files on remote site: " + files.length);
|
||||
|
||||
for (FTPFile ftpFile : files) {
|
||||
if (!file.exists()) {
|
||||
getFile(client, ftpFile, file);
|
||||
file.setLastModified(ftpFile.lastModified().getTime());
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.quit();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// False positive. Will be fixed in future.
|
||||
//////////////////////////////////////////////////
|
||||
private TreeMapNode buildTree(List<Object[]> tree)
|
||||
{
|
||||
state.clear();
|
||||
revState.clear();
|
||||
TreeMapNode root = null;
|
||||
for (Object[] s : tree) {
|
||||
Integer id = (Integer) s[0];
|
||||
String label = (String) s[1];
|
||||
Integer parentId = (Integer) s[2]; ///!!!!!!!!
|
||||
Number weight = (Number) s[3];
|
||||
Number value = (Number) s[4];
|
||||
Integer childCount = (Integer) s[5];
|
||||
TreeMapNode node;
|
||||
if (childCount == 0) {
|
||||
node = new TreeMapNode(label,
|
||||
weight != null ? weight.doubleValue() : 0.0,
|
||||
new DefaultValue(value != null ? value.doubleValue()
|
||||
: 0.0));
|
||||
}
|
||||
else {
|
||||
node = new TreeMapNode(label);
|
||||
}
|
||||
state.put(id, node);
|
||||
revState.put(node, id);
|
||||
if (parentId == null || parentId == -1) { ///!!!!!!!
|
||||
root = node;
|
||||
}
|
||||
else {
|
||||
state.get(parentId).add(node);
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
|
@ -713,6 +713,10 @@
|
|||
<td><a href="config_misc.html#UpperEll">UpperEll</a></td>
|
||||
<td>Checks that long constants are defined with an upper ell.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="config_coding.html#VariableDeclarationUsageDistance">VariableDeclarationUsageDistance</a></td>
|
||||
<td>Checks the distance between declaration of variable and its first usage.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="config_design.html#VisibilityModifier">VisibilityModifier</a></td>
|
||||
<td>Checks visibility of class members.</td>
|
||||
|
|
|
|||
|
|
@ -2509,5 +2509,196 @@ case 3:
|
|||
</subsection>
|
||||
</section>
|
||||
|
||||
<section name="VariableDeclarationUsageDistance">
|
||||
<subsection name="Description">
|
||||
<p>
|
||||
Checks the distance between declaration of variable and its first usage.
|
||||
</p>
|
||||
</subsection>
|
||||
|
||||
<subsection name="Properties">
|
||||
<table>
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th>description</th>
|
||||
<th>type</th>
|
||||
<th>default value</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>allowedDistance</td>
|
||||
<td>A distance between declaration of variable and its first usage</td>
|
||||
<td><a href="property_types.html#integer">integer</a></td>
|
||||
<td>3</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>ignoreVariablePattern</td>
|
||||
<td>pattern for ignoring the distance calculation</td>
|
||||
<td><a href="property_types.html#regexp">regular expression</a></td>
|
||||
<td>(not applied)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>validateBetweenScopes</td>
|
||||
<td>Allows to calculate the distance between declaration of variable and its first usage in the different scopes.</td>
|
||||
<td><a href="property_types.html#boolean">Boolean</a></td>
|
||||
<td><code>false</code></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>ignoreFinal</td>
|
||||
<td>Allows to ignore variables with a 'final' modifier.</td>
|
||||
<td><a href="property_types.html#boolean">Boolean</a></td>
|
||||
<td><code>true</code></td>
|
||||
</tr>
|
||||
</table>
|
||||
</subsection>
|
||||
|
||||
<subsection name="Examples">
|
||||
<p>
|
||||
Example #1:
|
||||
</p>
|
||||
<source>
|
||||
int count;
|
||||
a = a + b;
|
||||
b = a + a;
|
||||
count = b; // DECLARATION OF VARIABLE 'count'
|
||||
// SHOULD BE HERE (distance = 3)
|
||||
</source>
|
||||
<p>
|
||||
Example #2:
|
||||
</p>
|
||||
<source>
|
||||
int count;
|
||||
{
|
||||
a = a + b;
|
||||
count = b; // DECLARATION OF VARIABLE 'count'
|
||||
// SHOULD BE HERE (distance = 2)
|
||||
}
|
||||
</source>
|
||||
<p>
|
||||
Check can detect a block of initialization methods. If a variable is used in
|
||||
such a block and there is no other statements after this variable then distance=1.
|
||||
</p>
|
||||
<p>
|
||||
Case #1:
|
||||
</p>
|
||||
<source>
|
||||
int minutes = 5;
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(timeNow);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
cal.set(Calendar.HOUR_OF_DAY, hh);
|
||||
cal.set(Calendar.MINUTE, minutes);
|
||||
</source>
|
||||
<p>
|
||||
The distance for the variable minutes is 1 even
|
||||
though this variable is used in the fifth method's call.
|
||||
</p>
|
||||
<p>
|
||||
Case #2:
|
||||
</p>
|
||||
<source>
|
||||
int minutes = 5;
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(timeNow);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
System.out.println(cal);
|
||||
cal.set(Calendar.HOUR_OF_DAY, hh);
|
||||
cal.set(Calendar.MINUTE, minutes);
|
||||
</source>
|
||||
<p>
|
||||
The distance for the variable minutes is 6 because there is one more expression
|
||||
(except the initialization block) between the declaration of this variable and its usage.
|
||||
</p>
|
||||
<p>
|
||||
An example how to configure this Check:
|
||||
</p>
|
||||
<source>
|
||||
<module name="VariableDeclarationUsageDistance"/>
|
||||
</source>
|
||||
<p>
|
||||
An example of how to configure this Check:
|
||||
- to set the allowed distance to 4;
|
||||
- to ignore variables with prefix '^temp';
|
||||
- to force the validation between scopes;
|
||||
- to check the final variables;
|
||||
</p>
|
||||
<source>
|
||||
<module name="VariableDeclarationUsageDistance">
|
||||
<property name="allowedDistance" value="4">
|
||||
<property name="ignoreVariablePattern" value="^temp.*">
|
||||
<property name="validateBetweenScopes" value="true">
|
||||
<property name="mIgnoreFinal" value="false">
|
||||
</module>
|
||||
</source>
|
||||
</subsection>
|
||||
<subsection name="Note">
|
||||
<p>
|
||||
ATTENTION!! (Not supported cases)
|
||||
</p>
|
||||
<source>
|
||||
Case #1:
|
||||
{
|
||||
int c;
|
||||
int a = 3;
|
||||
int b = 2;
|
||||
{
|
||||
a = a + b;
|
||||
c = b;
|
||||
}
|
||||
}
|
||||
</source>
|
||||
<p>
|
||||
Distance for variable 'a' = 1;
|
||||
Distance for variable 'b' = 1;
|
||||
Distance for variable 'c' = 2.
|
||||
</p>
|
||||
<p>
|
||||
As distance by default is 1 the Check doesn't raise warning for
|
||||
variables 'a' and 'b' to move them into the block.
|
||||
</p>
|
||||
<p>
|
||||
Case #2:
|
||||
</p>
|
||||
<source>
|
||||
int sum = 0;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
a++;
|
||||
b--;
|
||||
sum++;
|
||||
if (sum > 10) {
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
</source>
|
||||
<p>
|
||||
Distance for variable 'sum' = 3.
|
||||
</p>
|
||||
<p>
|
||||
As the distance is more then the default one, the Check
|
||||
raises warning for variable 'sum' to move it into the 'for(...)' block.
|
||||
But there is situation when variable 'sum' hasn't to be 0 within each iteration.
|
||||
So, to avoid such warnings you can use Suppression Filter, provided by
|
||||
Checkstyle, for the whole class.
|
||||
</p>
|
||||
</subsection>
|
||||
|
||||
<subsection name="Package">
|
||||
<p>
|
||||
com.puppycrawl.tools.checkstyle.checks.coding
|
||||
</p>
|
||||
</subsection>
|
||||
|
||||
<subsection name="Parent Module">
|
||||
<p>
|
||||
<a href="config.html#TreeWalker">TreeWalker</a>
|
||||
</p>
|
||||
</subsection>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</document>
|
||||
|
|
|
|||
Loading…
Reference in New Issue