blob: 2025bb443f3fff358ad2d1ad1943e62dce090c78 [file] [log] [blame]
/*
* Copyright 2003-2012 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ig.psiutils;
import com.intellij.psi.*;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class UninitializedReadCollector {
private final Set<PsiExpression> uninitializedReads;
private int counter = 0;
public UninitializedReadCollector() {
uninitializedReads = new HashSet<PsiExpression>();
}
public PsiExpression[] getUninitializedReads() {
return uninitializedReads.toArray(new PsiExpression[uninitializedReads.size()]);
}
public boolean blockAssignsVariable(@Nullable PsiCodeBlock block, @NotNull PsiVariable variable) {
return blockAssignsVariable(block, variable, counter, new HashSet<MethodSignature>());
}
private boolean blockAssignsVariable(@Nullable PsiCodeBlock block, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
if (counter != stamp) {
return true;
}
if (block == null) {
return false;
}
final PsiStatement[] statements = block.getStatements();
for (final PsiStatement statement : statements) {
if (statementAssignsVariable(statement, variable, stamp, checkedMethods)) {
return true;
}
if (counter != stamp) {
return true;
}
}
return false;
}
private boolean statementAssignsVariable(@Nullable PsiStatement statement, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
if (statement == null) {
return false;
}
if (ExceptionUtils.statementThrowsException(statement)) {
return true;
}
if (statement instanceof PsiBreakStatement ||
statement instanceof PsiContinueStatement ||
statement instanceof PsiAssertStatement ||
statement instanceof PsiEmptyStatement) {
return false;
}
else if (statement instanceof PsiReturnStatement) {
final PsiReturnStatement returnStatement = (PsiReturnStatement)statement;
final PsiExpression returnValue = returnStatement.getReturnValue();
return expressionAssignsVariable(returnValue, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiThrowStatement) {
final PsiThrowStatement throwStatement = (PsiThrowStatement)statement;
final PsiExpression exception = throwStatement.getException();
return expressionAssignsVariable(exception, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiExpressionListStatement) {
final PsiExpressionListStatement list = (PsiExpressionListStatement)statement;
final PsiExpressionList expressionList = list.getExpressionList();
final PsiExpression[] expressions = expressionList.getExpressions();
for (final PsiExpression expression : expressions) {
if (expressionAssignsVariable(expression, variable, stamp, checkedMethods)) {
return true;
}
}
return false;
}
else if (statement instanceof PsiExpressionStatement) {
final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)statement;
final PsiExpression expression = expressionStatement.getExpression();
return expressionAssignsVariable(expression, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiDeclarationStatement) {
final PsiDeclarationStatement declarationStatement = (PsiDeclarationStatement)statement;
return declarationStatementAssignsVariable(declarationStatement, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiForStatement) {
final PsiForStatement forStatement = (PsiForStatement)statement;
return forStatementAssignsVariable(forStatement, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiForeachStatement) {
final PsiForeachStatement foreachStatement = (PsiForeachStatement)statement;
return foreachStatementAssignsVariable(foreachStatement, variable);
}
else if (statement instanceof PsiWhileStatement) {
final PsiWhileStatement whileStatement = (PsiWhileStatement)statement;
return whileStatementAssignsVariable(whileStatement, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiDoWhileStatement) {
final PsiDoWhileStatement doWhileStatement = (PsiDoWhileStatement)statement;
return doWhileAssignsVariable(doWhileStatement, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiSynchronizedStatement) {
final PsiSynchronizedStatement synchronizedStatement = (PsiSynchronizedStatement)statement;
final PsiCodeBlock body = synchronizedStatement.getBody();
return blockAssignsVariable(body, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiBlockStatement) {
final PsiBlockStatement blockStatement = (PsiBlockStatement)statement;
final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
return blockAssignsVariable(codeBlock, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiLabeledStatement) {
final PsiLabeledStatement labeledStatement = (PsiLabeledStatement)statement;
final PsiStatement statementLabeled = labeledStatement.getStatement();
return statementAssignsVariable(statementLabeled, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiIfStatement) {
final PsiIfStatement ifStatement = (PsiIfStatement)statement;
return ifStatementAssignsVariable(ifStatement, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiTryStatement) {
final PsiTryStatement tryStatement = (PsiTryStatement)statement;
return tryStatementAssignsVariable(tryStatement, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiSwitchStatement) {
final PsiSwitchStatement switchStatement = (PsiSwitchStatement)statement;
return switchStatementAssignsVariable(switchStatement, variable, stamp, checkedMethods);
}
else if (statement instanceof PsiSwitchLabelStatement) {
return false;
}
else {
assert false : "unknown statement: " + statement;
return false;
}
}
private boolean switchStatementAssignsVariable(@NotNull PsiSwitchStatement switchStatement, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
final PsiExpression expression = switchStatement.getExpression();
if (expressionAssignsVariable(expression, variable, stamp, checkedMethods)) {
return true;
}
final PsiCodeBlock body = switchStatement.getBody();
if (body == null) {
return false;
}
final PsiStatement[] statements = body.getStatements();
boolean containsDefault = false;
boolean assigns = false;
for (int i = 0; i < statements.length; i++) {
final PsiStatement statement = statements[i];
if (statement instanceof PsiSwitchLabelStatement) {
final PsiSwitchLabelStatement labelStatement = (PsiSwitchLabelStatement)statement;
if (i == statements.length - 1) {
return false;
}
if (labelStatement.isDefaultCase()) {
containsDefault = true;
}
assigns = false;
}
else if (statement instanceof PsiBreakStatement) {
final PsiBreakStatement breakStatement = (PsiBreakStatement)statement;
if (breakStatement.getLabelIdentifier() != null) {
return false;
}
if (!assigns) {
return false;
}
assigns = false;
}
else {
assigns |= statementAssignsVariable(statement, variable, stamp, checkedMethods);
if (i == statements.length - 1 && !assigns) {
return false;
}
}
}
return containsDefault;
}
private boolean declarationStatementAssignsVariable(@NotNull PsiDeclarationStatement declarationStatement, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
final PsiElement[] elements = declarationStatement.getDeclaredElements();
for (PsiElement element : elements) {
if (element instanceof PsiVariable) {
final PsiVariable variableElement = (PsiVariable)element;
final PsiExpression initializer = variableElement.getInitializer();
if (expressionAssignsVariable(initializer, variable, stamp, checkedMethods)) {
return true;
}
}
}
return false;
}
private boolean tryStatementAssignsVariable(@NotNull PsiTryStatement tryStatement, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
final PsiResourceList resourceList = tryStatement.getResourceList();
if (resourceList != null) {
final List<PsiResourceVariable> resourceVariables = resourceList.getResourceVariables();
for (PsiResourceVariable resourceVariable : resourceVariables) {
final PsiExpression initializer = resourceVariable.getInitializer();
if (expressionAssignsVariable(initializer, variable, stamp, checkedMethods)) {
return true;
}
}
}
final PsiCodeBlock tryBlock = tryStatement.getTryBlock();
boolean initializedInTryOrCatch = blockAssignsVariable(tryBlock, variable, stamp, checkedMethods);
final PsiCodeBlock[] catchBlocks = tryStatement.getCatchBlocks();
for (final PsiCodeBlock catchBlock : catchBlocks) {
initializedInTryOrCatch &= blockAssignsVariable(catchBlock, variable, stamp, checkedMethods);
}
if (initializedInTryOrCatch) {
return true;
}
final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
return blockAssignsVariable(finallyBlock, variable, stamp, checkedMethods);
}
private boolean ifStatementAssignsVariable(@NotNull PsiIfStatement ifStatement, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
final PsiExpression condition = ifStatement.getCondition();
if (expressionAssignsVariable(condition, variable, stamp, checkedMethods)) {
return true;
}
final PsiStatement thenBranch = ifStatement.getThenBranch();
final PsiStatement elseBranch = ifStatement.getElseBranch();
return statementAssignsVariable(thenBranch, variable, stamp, checkedMethods) &&
statementAssignsVariable(elseBranch, variable, stamp, checkedMethods);
}
private boolean doWhileAssignsVariable(@NotNull PsiDoWhileStatement doWhileStatement, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
final PsiExpression condition = doWhileStatement.getCondition();
final PsiStatement body = doWhileStatement.getBody();
return statementAssignsVariable(body, variable, stamp, checkedMethods) ||
expressionAssignsVariable(condition, variable, stamp, checkedMethods);
}
private boolean whileStatementAssignsVariable(@NotNull PsiWhileStatement whileStatement, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
final PsiExpression condition = whileStatement.getCondition();
if (expressionAssignsVariable(condition, variable, stamp, checkedMethods)) {
return true;
}
if (BoolUtils.isTrue(condition)) {
final PsiStatement body = whileStatement.getBody();
if (statementAssignsVariable(body, variable, stamp, checkedMethods)) {
return true;
}
}
return false;
}
private boolean forStatementAssignsVariable(@NotNull PsiForStatement forStatement, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
final PsiStatement initialization = forStatement.getInitialization();
if (statementAssignsVariable(initialization, variable, stamp, checkedMethods)) {
return true;
}
final PsiExpression condition = forStatement.getCondition();
if (expressionAssignsVariable(condition, variable, stamp, checkedMethods)) {
return true;
}
if (BoolUtils.isTrue(condition)) {
final PsiStatement body = forStatement.getBody();
if (statementAssignsVariable(body, variable, stamp, checkedMethods)) {
return true;
}
final PsiStatement update = forStatement.getUpdate();
if (statementAssignsVariable(update, variable, stamp, checkedMethods)) {
return true;
}
}
return false;
}
private boolean foreachStatementAssignsVariable(
PsiForeachStatement forStatement, PsiVariable variable) {
return false;
}
private boolean expressionAssignsVariable(@Nullable PsiExpression expression, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
if (counter != stamp) {
return true;
}
if (expression == null) {
return false;
}
if (expression instanceof PsiThisExpression ||
expression instanceof PsiLiteralExpression ||
expression instanceof PsiSuperExpression ||
expression instanceof PsiClassObjectAccessExpression) {
return false;
}
else if (expression instanceof PsiReferenceExpression) {
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression;
return referenceExpressionAssignsVariable(referenceExpression, variable, stamp, checkedMethods);
}
else if (expression instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression callExpression = (PsiMethodCallExpression)expression;
return methodCallAssignsVariable(callExpression, variable, stamp, checkedMethods);
}
else if (expression instanceof PsiNewExpression) {
final PsiNewExpression newExpression = (PsiNewExpression)expression;
return newExpressionAssignsVariable(newExpression, variable, stamp, checkedMethods);
}
else if (expression instanceof PsiArrayInitializerExpression) {
final PsiArrayInitializerExpression array = (PsiArrayInitializerExpression)expression;
final PsiExpression[] initializers = array.getInitializers();
for (final PsiExpression initializer : initializers) {
if (expressionAssignsVariable(initializer, variable, stamp, checkedMethods)) {
return true;
}
}
return false;
}
else if (expression instanceof PsiTypeCastExpression) {
final PsiTypeCastExpression typeCast = (PsiTypeCastExpression)expression;
final PsiExpression operand = typeCast.getOperand();
return expressionAssignsVariable(operand, variable, stamp, checkedMethods);
}
else if (expression instanceof PsiArrayAccessExpression) {
final PsiArrayAccessExpression accessExpression = (PsiArrayAccessExpression)expression;
final PsiExpression arrayExpression = accessExpression.getArrayExpression();
final PsiExpression indexExpression = accessExpression.getIndexExpression();
return expressionAssignsVariable(arrayExpression, variable, stamp, checkedMethods) ||
expressionAssignsVariable(indexExpression, variable, stamp, checkedMethods);
}
else if (expression instanceof PsiPrefixExpression) {
final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)expression;
final PsiExpression operand = prefixExpression.getOperand();
return expressionAssignsVariable(operand, variable, stamp, checkedMethods);
}
else if (expression instanceof PsiPostfixExpression) {
final PsiPostfixExpression postfixExpression = (PsiPostfixExpression)expression;
final PsiExpression operand = postfixExpression.getOperand();
return expressionAssignsVariable(operand, variable, stamp, checkedMethods);
}
else if (expression instanceof PsiPolyadicExpression) {
final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression;
final PsiExpression[] operands = polyadicExpression.getOperands();
for (PsiExpression operand : operands) {
if (expressionAssignsVariable(operand, variable, stamp, checkedMethods)) {
return true;
}
}
return false;
}
else if (expression instanceof PsiConditionalExpression) {
final PsiConditionalExpression conditional = (PsiConditionalExpression)expression;
final PsiExpression condition = conditional.getCondition();
if (expressionAssignsVariable(condition, variable, stamp, checkedMethods)) {
return true;
}
final PsiExpression thenExpression = conditional.getThenExpression();
final PsiExpression elseExpression = conditional.getElseExpression();
return expressionAssignsVariable(thenExpression, variable, stamp, checkedMethods)
&& expressionAssignsVariable(elseExpression, variable, stamp, checkedMethods);
}
else if (expression instanceof PsiAssignmentExpression) {
final PsiAssignmentExpression assignment = (PsiAssignmentExpression)expression;
return assignmentExpressionAssignsVariable(assignment, variable, stamp, checkedMethods);
}
else if (expression instanceof PsiParenthesizedExpression) {
final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression;
final PsiExpression innerExpression = parenthesizedExpression.getExpression();
return expressionAssignsVariable(innerExpression, variable, stamp, checkedMethods);
}
else if (expression instanceof PsiInstanceOfExpression) {
final PsiInstanceOfExpression instanceOfExpression = (PsiInstanceOfExpression)expression;
final PsiExpression operand = instanceOfExpression.getOperand();
return expressionAssignsVariable(operand, variable, stamp, checkedMethods);
}
else {
return false;
}
}
private boolean assignmentExpressionAssignsVariable(@NotNull PsiAssignmentExpression assignment, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
final PsiExpression lhs = assignment.getLExpression();
if (expressionAssignsVariable(lhs, variable, stamp, checkedMethods)) {
return true;
}
final PsiExpression rhs = assignment.getRExpression();
if (expressionAssignsVariable(rhs, variable, stamp, checkedMethods)) {
return true;
}
if (lhs instanceof PsiReferenceExpression) {
final PsiElement element = ((PsiReference)lhs).resolve();
if (element != null && element.equals(variable)) {
return true;
}
}
return false;
}
private boolean referenceExpressionAssignsVariable(@NotNull PsiReferenceExpression referenceExpression, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
final PsiExpression qualifierExpression = referenceExpression.getQualifierExpression();
if (expressionAssignsVariable(qualifierExpression, variable, stamp, checkedMethods)) {
return true;
}
if (variable.equals(referenceExpression.resolve())) {
final PsiElement parent = referenceExpression.getParent();
if (parent instanceof PsiAssignmentExpression) {
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent;
final PsiExpression rhs = assignmentExpression.getRExpression();
if (rhs != null && rhs.equals(referenceExpression)) {
checkReferenceExpression(referenceExpression, variable, qualifierExpression);
}
}
else {
checkReferenceExpression(referenceExpression, variable, qualifierExpression);
}
}
return false;
}
private void checkReferenceExpression(PsiReferenceExpression referenceExpression, PsiVariable variable,
PsiExpression qualifierExpression) {
if (!referenceExpression.isQualified() || qualifierExpression instanceof PsiThisExpression) {
uninitializedReads.add(referenceExpression);
counter++;
}
else if (variable.hasModifierProperty(PsiModifier.STATIC) && qualifierExpression instanceof PsiReferenceExpression) {
final PsiReferenceExpression reference = (PsiReferenceExpression)qualifierExpression;
final PsiElement target = reference.resolve();
if (target instanceof PsiClass) {
if (target.equals(PsiTreeUtil.getParentOfType(variable, PsiClass.class))) {
uninitializedReads.add(referenceExpression);
counter++;
}
}
}
}
private boolean newExpressionAssignsVariable(@NotNull PsiNewExpression newExpression, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
final PsiExpressionList argumentList = newExpression.getArgumentList();
if (argumentList != null) {
final PsiExpression[] args = argumentList.getExpressions();
for (final PsiExpression arg : args) {
if (expressionAssignsVariable(arg, variable, stamp, checkedMethods)) {
return true;
}
}
}
final PsiArrayInitializerExpression arrayInitializer = newExpression.getArrayInitializer();
if (expressionAssignsVariable(arrayInitializer, variable, stamp, checkedMethods)) {
return true;
}
final PsiExpression[] arrayDimensions = newExpression.getArrayDimensions();
for (final PsiExpression dim : arrayDimensions) {
if (expressionAssignsVariable(dim, variable, stamp, checkedMethods)) {
return true;
}
}
return false;
}
private boolean methodCallAssignsVariable(@NotNull PsiMethodCallExpression callExpression, @NotNull PsiVariable variable,
int stamp, @NotNull Set<MethodSignature> checkedMethods) {
final PsiReferenceExpression methodExpression = callExpression.getMethodExpression();
if (expressionAssignsVariable(methodExpression, variable, stamp, checkedMethods)) {
return true;
}
final PsiExpressionList argumentList = callExpression.getArgumentList();
final PsiExpression[] arguments = argumentList.getExpressions();
for (final PsiExpression argument : arguments) {
if (expressionAssignsVariable(argument, variable, stamp, checkedMethods)) {
return true;
}
}
final PsiMethod method = callExpression.resolveMethod();
if (method == null) {
return false;
}
final MethodSignature methodSignature = method.getSignature(PsiSubstitutor.EMPTY);
if (!checkedMethods.add(methodSignature)) {
return false;
}
final PsiClass containingClass = ClassUtils.getContainingClass(callExpression);
final PsiClass calledClass = method.getContainingClass();
// Can remark out this block to continue chase outside of of
// current class
if (calledClass == null || !calledClass.equals(containingClass)) {
return false;
}
if (method.hasModifierProperty(PsiModifier.STATIC)
|| method.isConstructor()
|| method.hasModifierProperty(PsiModifier.PRIVATE)
|| method.hasModifierProperty(PsiModifier.FINAL)
|| calledClass.hasModifierProperty(PsiModifier.FINAL)) {
final PsiCodeBlock body = method.getBody();
return blockAssignsVariable(body, variable, stamp, checkedMethods);
}
return false;
}
}