Skip to content

Commit

Permalink
Issue #3006: Fix false positive when variable is assigned multiple times
Browse files Browse the repository at this point in the history
  • Loading branch information
MEZk authored and romani committed Mar 29, 2016
1 parent 1121035 commit 55aa50d
Showing 1 changed file with 168 additions and 13 deletions.
Expand Up @@ -209,6 +209,18 @@ && shouldCheckEnhancedForLoopVariable(ast)) {
final int parentType = ast.getParent().getType(); final int parentType = ast.getParent().getType();
if (isAssignOperator(parentType) if (isAssignOperator(parentType)
&& isFirstChild(ast)) { && isFirstChild(ast)) {
if (isInIfBlock(ast))
{
markFinalVariableCandidateAsAssignInIfBlock(ast);
}
else if (isInElseBlock(ast))
{
markFinalVariableCandidateAsAssignInElseBlock(ast);
}
else
{
markFinalVariableCandidateAsAssignOutsideIfOrElseBlock(ast);
}
removeVariable(ast); removeVariable(ast);
} }
break; break;
Expand All @@ -218,9 +230,84 @@ && isFirstChild(ast)) {
} }
} }


private boolean isInIfBlock(DetailAST node) {
boolean returnValue = false;
for (DetailAST token = node.getParent(); token != null; token = token.getParent()) {
final int type = token.getType();
if (type == TokenTypes.LITERAL_IF) {
returnValue = true;
break;
}
}
return returnValue;
}

private boolean isInElseBlock(DetailAST node) {
boolean returnValue = false;
for (DetailAST token = node.getParent(); token != null; token = token.getParent()) {
final int type = token.getType();
if (type == TokenTypes.LITERAL_ELSE) {
returnValue = true;
break;
}
}
return returnValue;
}

private void markFinalVariableCandidateAsAssignInIfBlock(DetailAST ast) {
final Iterator<ScopeData> iterator = scopeStack.descendingIterator();
while (iterator.hasNext()) {
final ScopeData scopeData = iterator.next();
final Map<String, FinalVariableCandidate> scope = scopeData.scope;
DetailAST storedVariable = null;
final FinalVariableCandidate candidate = scope.get(ast.getText());
if (candidate != null) {
storedVariable = candidate.variableIdent;
}
if (storedVariable != null && isSameVariables(storedVariable, ast)) {
candidate.assignInIfBlock = true;
break;
}
}
}

private void markFinalVariableCandidateAsAssignInElseBlock(DetailAST ast) {
final Iterator<ScopeData> iterator = scopeStack.descendingIterator();
while (iterator.hasNext()) {
final ScopeData scopeData = iterator.next();
final Map<String, FinalVariableCandidate> scope = scopeData.scope;
DetailAST storedVariable = null;
final FinalVariableCandidate candidate = scope.get(ast.getText());
if (candidate != null) {
storedVariable = candidate.variableIdent;
}
if (storedVariable != null && isSameVariables(storedVariable, ast)) {
candidate.assignInElseBlock = true;
break;
}
}
}

private void markFinalVariableCandidateAsAssignOutsideIfOrElseBlock(DetailAST ast) {
final Iterator<ScopeData> iterator = scopeStack.descendingIterator();
while (iterator.hasNext()) {
final ScopeData scopeData = iterator.next();
final Map<String, FinalVariableCandidate> scope = scopeData.scope;
DetailAST storedVariable = null;
final FinalVariableCandidate candidate = scope.get(ast.getText());
if (candidate != null) {
storedVariable = candidate.variableIdent;
}
if (storedVariable != null && isSameVariables(storedVariable, ast)) {
candidate.assignOutsideIfOrElseBlock = true;
break;
}
}
}

@Override @Override
public void leaveToken(DetailAST ast) { public void leaveToken(DetailAST ast) {
Map<String, DetailAST> scope = null; Map<String, FinalVariableCandidate> scope = null;
switch (ast.getType()) { switch (ast.getType()) {
case TokenTypes.OBJBLOCK: case TokenTypes.OBJBLOCK:
case TokenTypes.CTOR_DEF: case TokenTypes.CTOR_DEF:
Expand All @@ -245,8 +332,9 @@ public void leaveToken(DetailAST ast) {
// do nothing // do nothing
} }
if (scope != null) { if (scope != null) {
for (DetailAST node : scope.values()) { for (FinalVariableCandidate candidate : scope.values()) {
log(node.getLineNo(), node.getColumnNo(), MSG_KEY, node.getText()); DetailAST ident = candidate.variableIdent;
log(ident.getLineNo(), ident.getColumnNo(), MSG_KEY, ident.getText());
} }
} }
} }
Expand Down Expand Up @@ -274,7 +362,11 @@ private void updateUninitializedVariables(Deque<DetailAST>
// Check for only previous scope // Check for only previous scope
for (DetailAST variable : prevScopeUnitializedVariableData) { for (DetailAST variable : prevScopeUnitializedVariableData) {
for (ScopeData scopeData : scopeStack) { for (ScopeData scopeData : scopeStack) {
final DetailAST storedVariable = scopeData.scope.get(variable.getText()); final FinalVariableCandidate candidate = scopeData.scope.get(variable.getText());
DetailAST storedVariable = null;
if (candidate != null) {
storedVariable = candidate.variableIdent;
}
if (storedVariable != null && isSameVariables(storedVariable, variable) if (storedVariable != null && isSameVariables(storedVariable, variable)
&& !scopeData.uninitializedVariables.contains(storedVariable)) { && !scopeData.uninitializedVariables.contains(storedVariable)) {
scopeData.uninitializedVariables.push(variable); scopeData.uninitializedVariables.push(variable);
Expand All @@ -285,7 +377,11 @@ private void updateUninitializedVariables(Deque<DetailAST>
for (Deque<DetailAST> unitializedVariableData : prevScopeUninitializedVariables) { for (Deque<DetailAST> unitializedVariableData : prevScopeUninitializedVariables) {
for (DetailAST variable : unitializedVariableData) { for (DetailAST variable : unitializedVariableData) {
for (ScopeData scopeData : scopeStack) { for (ScopeData scopeData : scopeStack) {
final DetailAST storedVariable = scopeData.scope.get(variable.getText()); final FinalVariableCandidate candidate = scopeData.scope.get(variable.getText());
DetailAST storedVariable = null;
if (candidate != null) {
storedVariable = candidate.variableIdent;
}
if (storedVariable != null if (storedVariable != null
&& isSameVariables(storedVariable, variable) && isSameVariables(storedVariable, variable)
&& !scopeData.uninitializedVariables.contains(storedVariable)) { && !scopeData.uninitializedVariables.contains(storedVariable)) {
Expand Down Expand Up @@ -344,19 +440,19 @@ private boolean shouldCheckEnhancedForLoopVariable(DetailAST ast) {
* @param ast the variable to insert. * @param ast the variable to insert.
*/ */
private void insertParameter(DetailAST ast) { private void insertParameter(DetailAST ast) {
final Map<String, DetailAST> scope = scopeStack.peek().scope; final Map<String, FinalVariableCandidate> scope = scopeStack.peek().scope;
final DetailAST astNode = ast.findFirstToken(TokenTypes.IDENT); final DetailAST astNode = ast.findFirstToken(TokenTypes.IDENT);
scope.put(astNode.getText(), astNode); scope.put(astNode.getText(), new FinalVariableCandidate(astNode));
} }


/** /**
* Insert a variable at the topmost scope stack. * Insert a variable at the topmost scope stack.
* @param ast the variable to insert. * @param ast the variable to insert.
*/ */
private void insertVariable(DetailAST ast) { private void insertVariable(DetailAST ast) {
final Map<String, DetailAST> scope = scopeStack.peek().scope; final Map<String, FinalVariableCandidate> scope = scopeStack.peek().scope;
final DetailAST astNode = ast.findFirstToken(TokenTypes.IDENT); final DetailAST astNode = ast.findFirstToken(TokenTypes.IDENT);
scope.put(astNode.getText(), astNode); scope.put(astNode.getText(), new FinalVariableCandidate(astNode));
if (!isInitialized(astNode)) { if (!isInitialized(astNode)) {
scopeStack.peek().uninitializedVariables.add(astNode); scopeStack.peek().uninitializedVariables.add(astNode);
} }
Expand Down Expand Up @@ -388,8 +484,12 @@ private void removeVariable(DetailAST ast) {
final Iterator<ScopeData> iterator = scopeStack.descendingIterator(); final Iterator<ScopeData> iterator = scopeStack.descendingIterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final ScopeData scopeData = iterator.next(); final ScopeData scopeData = iterator.next();
final Map<String, DetailAST> scope = scopeData.scope; final Map<String, FinalVariableCandidate> scope = scopeData.scope;
final DetailAST storedVariable = scope.get(ast.getText()); final FinalVariableCandidate candidate = scope.get(ast.getText());
DetailAST storedVariable = null;
if (candidate != null) {
storedVariable = candidate.variableIdent;
}
if (storedVariable != null && isSameVariables(storedVariable, ast)) { if (storedVariable != null && isSameVariables(storedVariable, ast)) {
if (shouldRemoveVariable(scopeData, ast)) { if (shouldRemoveVariable(scopeData, ast)) {
scope.remove(ast.getText()); scope.remove(ast.getText());
Expand All @@ -416,15 +516,55 @@ private static boolean shouldRemoveVariable(ScopeData scopeData, DetailAST ast)
// more than once in this case // more than once in this case
if (isInTheSameLoop(variable, ast) if (isInTheSameLoop(variable, ast)
|| !isUseOfExternalVariableInsideLoop(ast)) { || !isUseOfExternalVariableInsideLoop(ast)) {
shouldRemove = false; if (isAssignInIfBlock(scopeData, ast) && isAssignInElseBlock(scopeData, ast)) {
shouldRemove = true;
}
else if (isAssignInIfBlock(scopeData, ast)
&& isAssignOutsideIfOrElseBlock(scopeData, ast)) {
shouldRemove = true;
}
else {
shouldRemove = false;
}
} }

scopeData.uninitializedVariables.remove(variable); scopeData.uninitializedVariables.remove(variable);
break; break;
} }
} }
return shouldRemove; return shouldRemove;
} }


private static boolean isAssignInIfBlock(ScopeData scopeData, DetailAST ast) {
boolean assignInIfElseBlock = false;
FinalVariableCandidate candidate = scopeData.scope.get(ast.getText());
if (candidate != null)
{
assignInIfElseBlock = candidate.assignInIfBlock;
}
return assignInIfElseBlock;
}

private static boolean isAssignInElseBlock(ScopeData scopeData, DetailAST ast) {
boolean assignInIfElseBlock = false;
FinalVariableCandidate candidate = scopeData.scope.get(ast.getText());
if (candidate != null)
{
assignInIfElseBlock = candidate.assignInElseBlock;
}
return assignInIfElseBlock;
}

private static boolean isAssignOutsideIfOrElseBlock(ScopeData scopeData, DetailAST ast) {
boolean assignInIfElseBlock = false;
FinalVariableCandidate candidate = scopeData.scope.get(ast.getText());
if (candidate != null)
{
assignInIfElseBlock = candidate.assignOutsideIfOrElseBlock;
}
return assignInIfElseBlock;
}

/** /**
* Checks whether a variable which is declared ouside loop is used inside loop. * Checks whether a variable which is declared ouside loop is used inside loop.
* For example: * For example:
Expand Down Expand Up @@ -565,9 +705,24 @@ private static boolean isLoopAst(int ast) {
*/ */
private static class ScopeData { private static class ScopeData {
/** Contains variable definitions. */ /** Contains variable definitions. */
private final Map<String, DetailAST> scope = new HashMap<>(); private final Map<String, FinalVariableCandidate> scope = new HashMap<>();


/** Contains definitions of uninitialized variables. */ /** Contains definitions of uninitialized variables. */
private final Deque<DetailAST> uninitializedVariables = new ArrayDeque<>(); private final Deque<DetailAST> uninitializedVariables = new ArrayDeque<>();
} }

private static class FinalVariableCandidate {

private DetailAST variableIdent;

private boolean assignInIfBlock;

private boolean assignInElseBlock;

private boolean assignOutsideIfOrElseBlock;

public FinalVariableCandidate(DetailAST variableIdent) {
this.variableIdent = variableIdent;
}
}
} }

0 comments on commit 55aa50d

Please sign in to comment.