blob: 67c6a32a60f180b93daa2106a06a7abbda84631c [file] [log] [blame]
/*
* Copyright 2003-2013 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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Set;
public class InitializationUtils {
private InitializationUtils() {}
public static boolean methodAssignsVariableOrFails(@Nullable PsiMethod method, @NotNull PsiVariable variable) {
return methodAssignsVariableOrFails(method, variable, false);
}
public static boolean expressionAssignsVariableOrFails(@Nullable PsiExpression expression, @NotNull PsiVariable variable) {
return expressionAssignsVariableOrFails(expression, variable, new HashSet(), true);
}
public static boolean methodAssignsVariableOrFails(@Nullable PsiMethod method, @NotNull PsiVariable variable, boolean strict) {
if (method == null) {
return false;
}
return blockAssignsVariableOrFails(method.getBody(), variable, strict);
}
public static boolean blockAssignsVariableOrFails(@Nullable PsiCodeBlock block, @NotNull PsiVariable variable) {
return blockAssignsVariableOrFails(block, variable, false);
}
public static boolean blockAssignsVariableOrFails(@Nullable PsiCodeBlock block, @NotNull PsiVariable variable, boolean strict) {
return blockAssignsVariableOrFails(block, variable, new HashSet<MethodSignature>(), strict);
}
private static boolean blockAssignsVariableOrFails(@Nullable PsiCodeBlock block, @NotNull PsiVariable variable,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
if (block == null) {
return false;
}
int assignmentCount = 0;
for (final PsiStatement statement : block.getStatements()) {
if (statementAssignsVariableOrFails(statement, variable, checkedMethods, strict)) {
if (strict) {
assignmentCount++;
}
else {
return true;
}
}
}
return assignmentCount == 1;
}
private static boolean statementAssignsVariableOrFails(@Nullable PsiStatement statement, PsiVariable variable,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
if (statement == null) {
return false;
}
if (ExceptionUtils.statementThrowsException(statement)) {
return true;
}
if (statement instanceof PsiBreakStatement ||
statement instanceof PsiContinueStatement ||
statement instanceof PsiAssertStatement ||
statement instanceof PsiEmptyStatement ||
statement instanceof PsiSwitchLabelStatement) {
return false;
}
else if (statement instanceof PsiReturnStatement) {
final PsiReturnStatement returnStatement = (PsiReturnStatement)statement;
return expressionAssignsVariableOrFails(returnStatement.getReturnValue(), variable, checkedMethods, strict);
}
else if (statement instanceof PsiThrowStatement) {
final PsiThrowStatement throwStatement = (PsiThrowStatement)statement;
return expressionAssignsVariableOrFails(throwStatement.getException(), variable, checkedMethods, strict);
}
else if (statement instanceof PsiExpressionListStatement) {
final PsiExpressionListStatement list = (PsiExpressionListStatement)statement;
final PsiExpressionList expressionList = list.getExpressionList();
for (final PsiExpression expression : expressionList.getExpressions()) {
if (expressionAssignsVariableOrFails(expression, variable, checkedMethods, strict)) {
return true;
}
}
return false;
}
else if (statement instanceof PsiExpressionStatement) {
final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)statement;
return expressionAssignsVariableOrFails(expressionStatement.getExpression(), variable, checkedMethods, strict);
}
else if (statement instanceof PsiDeclarationStatement) {
final PsiDeclarationStatement declarationStatement = (PsiDeclarationStatement)statement;
return declarationStatementAssignsVariableOrFails(declarationStatement, variable, checkedMethods, strict);
}
else if (statement instanceof PsiForStatement) {
final PsiForStatement forStatement = (PsiForStatement)statement;
return forStatementAssignsVariableOrFails(forStatement, variable, checkedMethods, strict);
}
else if (statement instanceof PsiForeachStatement) {
final PsiForeachStatement foreachStatement = (PsiForeachStatement)statement;
return foreachStatementAssignsVariableOrFails(foreachStatement, variable, checkedMethods, strict);
}
else if (statement instanceof PsiWhileStatement) {
final PsiWhileStatement whileStatement = (PsiWhileStatement)statement;
return whileStatementAssignsVariableOrFails(whileStatement, variable, checkedMethods, strict);
}
else if (statement instanceof PsiDoWhileStatement) {
final PsiDoWhileStatement doWhileStatement = (PsiDoWhileStatement)statement;
return doWhileAssignsVariableOrFails(doWhileStatement, variable, checkedMethods, strict);
}
else if (statement instanceof PsiSynchronizedStatement) {
final PsiSynchronizedStatement synchronizedStatement = (PsiSynchronizedStatement)statement;
return blockAssignsVariableOrFails(synchronizedStatement.getBody(), variable, checkedMethods, strict);
}
else if (statement instanceof PsiBlockStatement) {
final PsiBlockStatement blockStatement = (PsiBlockStatement)statement;
return blockAssignsVariableOrFails(blockStatement.getCodeBlock(), variable, checkedMethods, strict);
}
else if (statement instanceof PsiLabeledStatement) {
final PsiLabeledStatement labeledStatement = (PsiLabeledStatement)statement;
return statementAssignsVariableOrFails(labeledStatement.getStatement(), variable, checkedMethods, strict);
}
else if (statement instanceof PsiIfStatement) {
final PsiIfStatement ifStatement = (PsiIfStatement)statement;
return ifStatementAssignsVariableOrFails(ifStatement, variable, checkedMethods, strict);
}
else if (statement instanceof PsiTryStatement) {
final PsiTryStatement tryStatement = (PsiTryStatement)statement;
return tryStatementAssignsVariableOrFails(tryStatement, variable, checkedMethods, strict);
}
else if (statement instanceof PsiSwitchStatement) {
final PsiSwitchStatement switchStatement = (PsiSwitchStatement)statement;
return switchStatementAssignsVariableOrFails(switchStatement, variable, checkedMethods, strict);
}
else {
// unknown statement type
return false;
}
}
public static boolean switchStatementAssignsVariableOrFails(@NotNull PsiSwitchStatement switchStatement, @NotNull PsiVariable variable,
boolean strict) {
return switchStatementAssignsVariableOrFails(switchStatement, variable, new HashSet(), strict);
}
private static boolean switchStatementAssignsVariableOrFails(@NotNull PsiSwitchStatement switchStatement, @NotNull PsiVariable variable,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
final PsiExpression expression = switchStatement.getExpression();
if (expressionAssignsVariableOrFails(expression, variable, checkedMethods, strict)) {
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 |= statementAssignsVariableOrFails(statement, variable, checkedMethods, strict);
if (i == statements.length - 1 && !assigns) {
return false;
}
}
}
return containsDefault;
}
private static boolean declarationStatementAssignsVariableOrFails(PsiDeclarationStatement declarationStatement, PsiVariable variable,
Set<MethodSignature> checkedMethods, boolean strict) {
final PsiElement[] elements = declarationStatement.getDeclaredElements();
for (PsiElement element : elements) {
if (element instanceof PsiVariable) {
final PsiVariable declaredVariable = (PsiVariable)element;
if (expressionAssignsVariableOrFails(declaredVariable.getInitializer(), variable, checkedMethods, strict)) {
return true;
}
}
}
return false;
}
private static boolean tryStatementAssignsVariableOrFails(@NotNull PsiTryStatement tryStatement, PsiVariable variable,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
final PsiResourceList resourceList = tryStatement.getResourceList();
if (resourceList != null) {
for (PsiResourceVariable resourceVariable : resourceList.getResourceVariables()) {
final PsiExpression initializer = resourceVariable.getInitializer();
if (expressionAssignsVariableOrFails(initializer, variable, checkedMethods, strict)) {
return true;
}
}
}
boolean initializedInTryAndCatch = blockAssignsVariableOrFails(tryStatement.getTryBlock(), variable, checkedMethods, strict);
final PsiCodeBlock[] catchBlocks = tryStatement.getCatchBlocks();
for (final PsiCodeBlock catchBlock : catchBlocks) {
if (strict) {
initializedInTryAndCatch &= ExceptionUtils.blockThrowsException(catchBlock);
}
else {
initializedInTryAndCatch &= blockAssignsVariableOrFails(catchBlock, variable, checkedMethods, strict);
}
}
return initializedInTryAndCatch || blockAssignsVariableOrFails(tryStatement.getFinallyBlock(), variable, checkedMethods, strict);
}
private static boolean ifStatementAssignsVariableOrFails(@NotNull PsiIfStatement ifStatement, PsiVariable variable,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
final PsiExpression condition = ifStatement.getCondition();
if (expressionAssignsVariableOrFails(condition, variable, checkedMethods, strict)) {
return true;
}
final PsiStatement thenBranch = ifStatement.getThenBranch();
if (BoolUtils.isTrue(condition)) {
return statementAssignsVariableOrFails(thenBranch, variable, checkedMethods, strict);
}
final PsiStatement elseBranch = ifStatement.getElseBranch();
if (BoolUtils.isFalse(condition)) {
return statementAssignsVariableOrFails(elseBranch, variable, checkedMethods, strict);
}
return statementAssignsVariableOrFails(thenBranch, variable, checkedMethods, strict) &&
statementAssignsVariableOrFails(elseBranch, variable, checkedMethods, strict);
}
private static boolean doWhileAssignsVariableOrFails(@NotNull PsiDoWhileStatement doWhileStatement, PsiVariable variable,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
return statementAssignsVariableOrFails(doWhileStatement.getBody(), variable, checkedMethods, strict) ||
expressionAssignsVariableOrFails(doWhileStatement.getCondition(), variable, checkedMethods, strict);
}
private static boolean whileStatementAssignsVariableOrFails(@NotNull PsiWhileStatement whileStatement, PsiVariable variable,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
final PsiExpression condition = whileStatement.getCondition();
if (expressionAssignsVariableOrFails(condition, variable, checkedMethods, strict)) {
return true;
}
if (BoolUtils.isTrue(condition)) {
final PsiStatement body = whileStatement.getBody();
if (statementAssignsVariableOrFails(body, variable, checkedMethods, strict)) {
return true;
}
}
return false;
}
private static boolean forStatementAssignsVariableOrFails(@NotNull PsiForStatement forStatement, PsiVariable variable,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
if (statementAssignsVariableOrFails(forStatement.getInitialization(), variable, checkedMethods, strict)) {
return true;
}
final PsiExpression condition = forStatement.getCondition();
if (expressionAssignsVariableOrFails(condition, variable, checkedMethods, strict)) {
return true;
}
if (BoolUtils.isTrue(condition)) {
if (statementAssignsVariableOrFails(forStatement.getBody(), variable, checkedMethods, strict)) {
return true;
}
if (statementAssignsVariableOrFails(forStatement.getUpdate(), variable, checkedMethods, strict)) {
return true;
}
}
return false;
}
private static boolean foreachStatementAssignsVariableOrFails(@NotNull PsiForeachStatement foreachStatement, PsiVariable field,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
return expressionAssignsVariableOrFails(foreachStatement.getIteratedValue(), field, checkedMethods, strict);
}
private static boolean expressionAssignsVariableOrFails(@Nullable PsiExpression expression, PsiVariable variable,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
if (expression == null) {
return false;
}
if (expression instanceof PsiThisExpression ||
expression instanceof PsiLiteralExpression ||
expression instanceof PsiSuperExpression ||
expression instanceof PsiClassObjectAccessExpression ||
expression instanceof PsiReferenceExpression) {
return false;
}
else if (expression instanceof PsiParenthesizedExpression) {
final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression;
return expressionAssignsVariableOrFails(parenthesizedExpression.getExpression(), variable, checkedMethods, strict);
}
else if (expression instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
return methodCallAssignsVariableOrFails(methodCallExpression, variable, checkedMethods, strict);
}
else if (expression instanceof PsiNewExpression) {
final PsiNewExpression newExpression = (PsiNewExpression)expression;
return newExpressionAssignsVariableOrFails(newExpression, variable, checkedMethods, strict);
}
else if (expression instanceof PsiArrayInitializerExpression) {
final PsiArrayInitializerExpression array = (PsiArrayInitializerExpression)expression;
for (final PsiExpression initializer : array.getInitializers()) {
if (expressionAssignsVariableOrFails(initializer, variable, checkedMethods, strict)) {
return true;
}
}
return false;
}
else if (expression instanceof PsiTypeCastExpression) {
final PsiTypeCastExpression typeCast = (PsiTypeCastExpression)expression;
return expressionAssignsVariableOrFails(typeCast.getOperand(), variable, checkedMethods, strict);
}
else if (expression instanceof PsiArrayAccessExpression) {
final PsiArrayAccessExpression accessExpression = (PsiArrayAccessExpression)expression;
return expressionAssignsVariableOrFails(accessExpression.getArrayExpression(), variable, checkedMethods, strict) ||
expressionAssignsVariableOrFails(accessExpression.getIndexExpression(), variable, checkedMethods, strict);
}
else if (expression instanceof PsiPrefixExpression) {
final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)expression;
return expressionAssignsVariableOrFails(prefixExpression.getOperand(), variable, checkedMethods, strict);
}
else if (expression instanceof PsiPostfixExpression) {
final PsiPostfixExpression postfixExpression = (PsiPostfixExpression)expression;
return expressionAssignsVariableOrFails(postfixExpression.getOperand(), variable, checkedMethods, strict);
}
else if (expression instanceof PsiPolyadicExpression) {
final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression;
for (PsiExpression operand : polyadicExpression.getOperands()) {
if (expressionAssignsVariableOrFails(operand, variable, checkedMethods, strict)) {
return true;
}
}
return false;
}
else if (expression instanceof PsiConditionalExpression) {
final PsiConditionalExpression conditional = (PsiConditionalExpression)expression;
if (expressionAssignsVariableOrFails(conditional.getCondition(), variable, checkedMethods, strict)) {
return true;
}
return expressionAssignsVariableOrFails(conditional.getThenExpression(), variable, checkedMethods, strict) &&
expressionAssignsVariableOrFails(conditional.getElseExpression(), variable, checkedMethods, strict);
}
else if (expression instanceof PsiAssignmentExpression) {
final PsiAssignmentExpression assignment = (PsiAssignmentExpression)expression;
final PsiExpression lhs = assignment.getLExpression();
if (expressionAssignsVariableOrFails(lhs, variable, checkedMethods, strict)) {
return true;
}
if (expressionAssignsVariableOrFails(assignment.getRExpression(), variable, checkedMethods, strict)) {
return true;
}
if (lhs instanceof PsiReferenceExpression) {
final PsiElement element = ((PsiReference)lhs).resolve();
if (variable.equals(element)) {
return true;
}
}
return false;
}
else if (expression instanceof PsiInstanceOfExpression) {
final PsiInstanceOfExpression instanceOfExpression = (PsiInstanceOfExpression)expression;
return expressionAssignsVariableOrFails(instanceOfExpression.getOperand(), variable, checkedMethods, strict);
}
else {
return false;
}
}
private static boolean newExpressionAssignsVariableOrFails(@NotNull PsiNewExpression newExpression, PsiVariable variable,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
final PsiExpressionList argumentList = newExpression.getArgumentList();
if (argumentList != null) {
for (final PsiExpression argument : argumentList.getExpressions()) {
if (expressionAssignsVariableOrFails(argument, variable, checkedMethods, strict)) {
return true;
}
}
}
if (expressionAssignsVariableOrFails(newExpression.getArrayInitializer(), variable, checkedMethods, strict)) {
return true;
}
for (final PsiExpression dimension : newExpression.getArrayDimensions()) {
if (expressionAssignsVariableOrFails(dimension, variable, checkedMethods, strict)) {
return true;
}
}
return false;
}
private static boolean methodCallAssignsVariableOrFails(@NotNull PsiMethodCallExpression callExpression, PsiVariable variable,
@NotNull Set<MethodSignature> checkedMethods, boolean strict) {
final PsiExpressionList argumentList = callExpression.getArgumentList();
for (final PsiExpression argument : argumentList.getExpressions()) {
if (expressionAssignsVariableOrFails(argument, variable, checkedMethods, strict)) {
return true;
}
}
if (expressionAssignsVariableOrFails(callExpression.getMethodExpression(), variable, checkedMethods, strict)) {
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();
if (calledClass == null || !calledClass.equals(containingClass)) {
return false;
}
if (method.hasModifierProperty(PsiModifier.STATIC)
|| method.hasModifierProperty(PsiModifier.PRIVATE)
|| method.hasModifierProperty(PsiModifier.FINAL)
|| method.isConstructor()
|| calledClass.hasModifierProperty(PsiModifier.FINAL)) {
return blockAssignsVariableOrFails(method.getBody(), variable, checkedMethods, strict);
}
return false;
}
}