blob: 58720c6ffa1815e4c0ed94d7992e16bdd0d95a97 [file] [log] [blame]
/*
* Copyright 2003-2010 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.tree.IElementType;
import org.jetbrains.annotations.NotNull;
public class RecursionUtils {
private RecursionUtils() {
super();
}
public static boolean statementMayReturnBeforeRecursing(
PsiStatement statement, PsiMethod method) {
if (statement == null) {
return true;
}
if (statement instanceof PsiBreakStatement ||
statement instanceof PsiContinueStatement ||
statement instanceof PsiThrowStatement ||
statement instanceof PsiExpressionListStatement ||
statement instanceof PsiExpressionStatement ||
statement instanceof PsiEmptyStatement ||
statement instanceof PsiAssertStatement ||
statement instanceof PsiDeclarationStatement) {
return false;
}
else if (statement instanceof PsiReturnStatement) {
final PsiReturnStatement returnStatement =
(PsiReturnStatement)statement;
final PsiExpression returnValue = returnStatement.getReturnValue();
if (returnValue != null) {
if (expressionDefinitelyRecurses(returnValue, method)) {
return false;
}
}
return true;
}
else if (statement instanceof PsiForStatement) {
return forStatementMayReturnBeforeRecursing(
(PsiForStatement)statement, method);
}
else if (statement instanceof PsiForeachStatement) {
return foreachStatementMayReturnBeforeRecursing(
(PsiForeachStatement)statement, method);
}
else if (statement instanceof PsiWhileStatement) {
return whileStatementMayReturnBeforeRecursing(
(PsiWhileStatement)statement, method);
}
else if (statement instanceof PsiDoWhileStatement) {
return doWhileStatementMayReturnBeforeRecursing(
(PsiDoWhileStatement)statement, method);
}
else if (statement instanceof PsiSynchronizedStatement) {
final PsiCodeBlock body = ((PsiSynchronizedStatement)statement)
.getBody();
return codeBlockMayReturnBeforeRecursing(body, method, false);
}
else if (statement instanceof PsiBlockStatement) {
final PsiBlockStatement blockStatement =
(PsiBlockStatement)statement;
final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
return codeBlockMayReturnBeforeRecursing(codeBlock, method, false);
}
else if (statement instanceof PsiLabeledStatement) {
return labeledStatementMayReturnBeforeRecursing(
(PsiLabeledStatement)statement, method);
}
else if (statement instanceof PsiIfStatement) {
return ifStatementMayReturnBeforeRecursing(
(PsiIfStatement)statement, method);
}
else if (statement instanceof PsiTryStatement) {
return tryStatementMayReturnBeforeRecursing(
(PsiTryStatement)statement, method);
}
else if (statement instanceof PsiSwitchStatement) {
return switchStatementMayReturnBeforeRecursing(
(PsiSwitchStatement)statement, method);
}
else {
// unknown statement type
return true;
}
}
private static boolean doWhileStatementMayReturnBeforeRecursing(
PsiDoWhileStatement loopStatement, PsiMethod method) {
final PsiStatement body = loopStatement.getBody();
return statementMayReturnBeforeRecursing(body, method);
}
private static boolean whileStatementMayReturnBeforeRecursing(
PsiWhileStatement loopStatement, PsiMethod method) {
final PsiExpression test = loopStatement.getCondition();
if (expressionDefinitelyRecurses(test, method)) {
return false;
}
final PsiStatement body = loopStatement.getBody();
return statementMayReturnBeforeRecursing(body, method);
}
private static boolean forStatementMayReturnBeforeRecursing(
PsiForStatement loopStatement, PsiMethod method) {
final PsiStatement initialization = loopStatement.getInitialization();
if (statementMayReturnBeforeRecursing(initialization, method)) {
return true;
}
final PsiExpression test = loopStatement.getCondition();
if (expressionDefinitelyRecurses(test, method)) {
return false;
}
final PsiStatement body = loopStatement.getBody();
return statementMayReturnBeforeRecursing(body, method);
}
private static boolean foreachStatementMayReturnBeforeRecursing(
PsiForeachStatement loopStatement, PsiMethod method) {
final PsiExpression test = loopStatement.getIteratedValue();
if (expressionDefinitelyRecurses(test, method)) {
return false;
}
final PsiStatement body = loopStatement.getBody();
return statementMayReturnBeforeRecursing(body, method);
}
private static boolean switchStatementMayReturnBeforeRecursing(
PsiSwitchStatement switchStatement, PsiMethod method) {
final PsiCodeBlock body = switchStatement.getBody();
if (body == null) {
return true;
}
final PsiStatement[] statements = body.getStatements();
for (final PsiStatement statement : statements) {
if (statementMayReturnBeforeRecursing(statement, method)) {
return true;
}
}
return false;
}
private static boolean tryStatementMayReturnBeforeRecursing(
PsiTryStatement tryStatement, PsiMethod method) {
final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
if (finallyBlock != null) {
if (codeBlockMayReturnBeforeRecursing(finallyBlock, method,
false)) {
return true;
}
if (codeBlockDefinitelyRecurses(finallyBlock, method)) {
return false;
}
}
final PsiCodeBlock tryBlock = tryStatement.getTryBlock();
if (codeBlockMayReturnBeforeRecursing(tryBlock, method, false)) {
return true;
}
final PsiCodeBlock[] catchBlocks = tryStatement.getCatchBlocks();
for (final PsiCodeBlock catchBlock : catchBlocks) {
if (codeBlockMayReturnBeforeRecursing(catchBlock, method, false)) {
return true;
}
}
return false;
}
private static boolean ifStatementMayReturnBeforeRecursing(
PsiIfStatement ifStatement, PsiMethod method) {
final PsiExpression test = ifStatement.getCondition();
if (expressionDefinitelyRecurses(test, method)) {
return false;
}
final PsiStatement thenBranch = ifStatement.getThenBranch();
if (statementMayReturnBeforeRecursing(thenBranch, method)) {
return true;
}
final PsiStatement elseBranch = ifStatement.getElseBranch();
return elseBranch != null &&
statementMayReturnBeforeRecursing(elseBranch, method);
}
private static boolean labeledStatementMayReturnBeforeRecursing(
PsiLabeledStatement labeledStatement, PsiMethod method) {
final PsiStatement statement = labeledStatement.getStatement();
return statementMayReturnBeforeRecursing(statement, method);
}
private static boolean codeBlockMayReturnBeforeRecursing(
PsiCodeBlock block, PsiMethod method, boolean endsInImplicitReturn) {
if (block == null) {
return true;
}
final PsiStatement[] statements = block.getStatements();
for (final PsiStatement statement : statements) {
if (statementMayReturnBeforeRecursing(statement, method)) {
return true;
}
if (statementDefinitelyRecurses(statement, method)) {
return false;
}
}
return endsInImplicitReturn;
}
public static boolean methodMayRecurse(@NotNull PsiMethod method) {
final RecursionVisitor recursionVisitor = new RecursionVisitor(method);
method.accept(recursionVisitor);
return recursionVisitor.isRecursive();
}
private static boolean expressionDefinitelyRecurses(PsiExpression exp,
PsiMethod method) {
if (exp == null) {
return false;
}
if (exp instanceof PsiMethodCallExpression) {
return methodCallExpressionDefinitelyRecurses(
(PsiMethodCallExpression)exp, method);
}
if (exp instanceof PsiNewExpression) {
return newExpressionDefinitelyRecurses(
(PsiNewExpression)exp, method);
}
if (exp instanceof PsiAssignmentExpression) {
return assignmentExpressionDefinitelyRecurses(
(PsiAssignmentExpression)exp, method);
}
if (exp instanceof PsiArrayInitializerExpression) {
return arrayInitializerExpressionDefinitelyRecurses(
(PsiArrayInitializerExpression)exp, method);
}
if (exp instanceof PsiTypeCastExpression) {
return typeCastExpressionDefinitelyRecurses(
(PsiTypeCastExpression)exp, method);
}
if (exp instanceof PsiArrayAccessExpression) {
return arrayAccessExpressionDefinitelyRecurses(
(PsiArrayAccessExpression)exp, method);
}
if (exp instanceof PsiPrefixExpression) {
return prefixExpressionDefinitelyRecurses(
(PsiPrefixExpression)exp, method);
}
if (exp instanceof PsiPostfixExpression) {
return postfixExpressionDefinitelyRecurses(
(PsiPostfixExpression)exp, method);
}
if (exp instanceof PsiBinaryExpression) {
return binaryExpressionDefinitelyRecurses(
(PsiBinaryExpression)exp, method);
}
if (exp instanceof PsiInstanceOfExpression) {
return instanceOfExpressionDefinitelyRecurses(
(PsiInstanceOfExpression)exp, method);
}
if (exp instanceof PsiConditionalExpression) {
return conditionalExpressionDefinitelyRecurses(
(PsiConditionalExpression)exp, method);
}
if (exp instanceof PsiParenthesizedExpression) {
return parenthesizedExpressionDefinitelyRecurses(
(PsiParenthesizedExpression)exp, method);
}
if (exp instanceof PsiReferenceExpression) {
return referenceExpressionDefinitelyRecurses(
(PsiReferenceExpression)exp, method);
}
if (exp instanceof PsiLiteralExpression ||
exp instanceof PsiClassObjectAccessExpression ||
exp instanceof PsiThisExpression ||
exp instanceof PsiSuperExpression) {
return false;
}
return false;
}
private static boolean conditionalExpressionDefinitelyRecurses(
PsiConditionalExpression expression, PsiMethod method) {
final PsiExpression condExpression = expression.getCondition();
if (expressionDefinitelyRecurses(condExpression, method)) {
return true;
}
final PsiExpression thenExpression = expression.getThenExpression();
final PsiExpression elseExpression = expression.getElseExpression();
return expressionDefinitelyRecurses(thenExpression, method)
&& expressionDefinitelyRecurses(elseExpression, method);
}
private static boolean binaryExpressionDefinitelyRecurses(
PsiBinaryExpression expression, PsiMethod method) {
final PsiExpression lhs = expression.getLOperand();
if (expressionDefinitelyRecurses(lhs, method)) {
return true;
}
final IElementType tokenType = expression.getOperationTokenType();
if (tokenType.equals(JavaTokenType.ANDAND) ||
tokenType.equals(JavaTokenType.OROR)) {
return false;
}
final PsiExpression rhs = expression.getROperand();
return expressionDefinitelyRecurses(rhs, method);
}
private static boolean arrayAccessExpressionDefinitelyRecurses(
PsiArrayAccessExpression expression, PsiMethod method) {
final PsiExpression arrayExp = expression.getArrayExpression();
final PsiExpression indexExp = expression.getIndexExpression();
return expressionDefinitelyRecurses(arrayExp, method) ||
expressionDefinitelyRecurses(indexExp, method);
}
private static boolean arrayInitializerExpressionDefinitelyRecurses(
PsiArrayInitializerExpression expression, PsiMethod method) {
final PsiExpression[] initializers = expression.getInitializers();
for (final PsiExpression initializer : initializers) {
if (expressionDefinitelyRecurses(initializer, method)) {
return true;
}
}
return false;
}
private static boolean prefixExpressionDefinitelyRecurses(
PsiPrefixExpression expression, PsiMethod method) {
final PsiExpression operand = expression.getOperand();
return expressionDefinitelyRecurses(operand, method);
}
private static boolean postfixExpressionDefinitelyRecurses(
PsiPostfixExpression expression, PsiMethod method) {
final PsiExpression operand = expression.getOperand();
return expressionDefinitelyRecurses(operand, method);
}
private static boolean instanceOfExpressionDefinitelyRecurses(
PsiInstanceOfExpression expression, PsiMethod method) {
final PsiExpression operand = expression.getOperand();
return expressionDefinitelyRecurses(operand, method);
}
private static boolean parenthesizedExpressionDefinitelyRecurses(
PsiParenthesizedExpression expression, PsiMethod method) {
final PsiExpression innerExpression = expression.getExpression();
return expressionDefinitelyRecurses(innerExpression, method);
}
private static boolean referenceExpressionDefinitelyRecurses(
PsiReferenceExpression expression, PsiMethod method) {
final PsiExpression qualifierExpression =
expression.getQualifierExpression();
return qualifierExpression != null &&
expressionDefinitelyRecurses(qualifierExpression, method);
}
private static boolean typeCastExpressionDefinitelyRecurses(
PsiTypeCastExpression expression, PsiMethod method) {
final PsiExpression operand = expression.getOperand();
return expressionDefinitelyRecurses(operand, method);
}
private static boolean assignmentExpressionDefinitelyRecurses(
PsiAssignmentExpression assignmentExpression, PsiMethod method) {
final PsiExpression rhs = assignmentExpression.getRExpression();
final PsiExpression lhs = assignmentExpression.getLExpression();
return expressionDefinitelyRecurses(rhs, method) ||
expressionDefinitelyRecurses(lhs, method);
}
private static boolean newExpressionDefinitelyRecurses(PsiNewExpression exp,
PsiMethod method) {
final PsiExpression[] arrayDimensions = exp.getArrayDimensions();
for (final PsiExpression arrayDimension : arrayDimensions) {
if (expressionDefinitelyRecurses(arrayDimension, method)) {
return true;
}
}
final PsiArrayInitializerExpression arrayInitializer = exp
.getArrayInitializer();
if (expressionDefinitelyRecurses(arrayInitializer, method)) {
return true;
}
final PsiExpression qualifier = exp.getQualifier();
if (expressionDefinitelyRecurses(qualifier, method)) {
return true;
}
final PsiExpressionList argumentList = exp.getArgumentList();
if (argumentList != null) {
final PsiExpression[] args = argumentList.getExpressions();
for (final PsiExpression arg : args) {
if (expressionDefinitelyRecurses(arg, method)) {
return true;
}
}
}
return false;
}
private static boolean methodCallExpressionDefinitelyRecurses(
PsiMethodCallExpression exp, PsiMethod method) {
final PsiReferenceExpression methodExpression =
exp.getMethodExpression();
final PsiMethod referencedMethod = exp.resolveMethod();
if (referencedMethod == null) {
return false;
}
if (referencedMethod.equals(method)) {
if (method.hasModifierProperty(PsiModifier.STATIC) ||
method.hasModifierProperty(PsiModifier.PRIVATE)) {
return true;
}
final PsiExpression qualifier =
methodExpression.getQualifierExpression();
if (qualifier == null || qualifier instanceof PsiThisExpression) {
return true;
}
}
final PsiExpression qualifier =
methodExpression.getQualifierExpression();
if (expressionDefinitelyRecurses(qualifier, method)) {
return true;
}
final PsiExpressionList argumentList = exp.getArgumentList();
final PsiExpression[] args = argumentList.getExpressions();
for (final PsiExpression arg : args) {
if (expressionDefinitelyRecurses(arg, method)) {
return true;
}
}
return false;
}
private static boolean statementDefinitelyRecurses(PsiStatement statement,
PsiMethod method) {
if (statement == null) {
return false;
}
if (statement instanceof PsiBreakStatement ||
statement instanceof PsiContinueStatement ||
statement instanceof PsiThrowStatement ||
statement instanceof PsiEmptyStatement ||
statement instanceof PsiAssertStatement) {
return false;
}
else if (statement instanceof PsiExpressionListStatement) {
final PsiExpressionListStatement expressionListStatement =
(PsiExpressionListStatement)statement;
final PsiExpressionList expressionList =
expressionListStatement.getExpressionList();
if (expressionList == null) {
return false;
}
final PsiExpression[] expressions = expressionList.getExpressions();
for (final PsiExpression expression : expressions) {
if (expressionDefinitelyRecurses(expression, method)) {
return true;
}
}
return false;
}
else if (statement instanceof PsiExpressionStatement) {
final PsiExpressionStatement expressionStatement =
(PsiExpressionStatement)statement;
final PsiExpression expression =
expressionStatement.getExpression();
return expressionDefinitelyRecurses(expression, method);
}
else if (statement instanceof PsiDeclarationStatement) {
final PsiDeclarationStatement declaration =
(PsiDeclarationStatement)statement;
final PsiElement[] declaredElements =
declaration.getDeclaredElements();
for (final PsiElement declaredElement : declaredElements) {
if (declaredElement instanceof PsiLocalVariable) {
final PsiLocalVariable variable =
(PsiLocalVariable)declaredElement;
final PsiExpression initializer = variable.getInitializer();
if (expressionDefinitelyRecurses(initializer, method)) {
return true;
}
}
}
return false;
}
else if (statement instanceof PsiReturnStatement) {
final PsiReturnStatement returnStatement =
(PsiReturnStatement)statement;
final PsiExpression returnValue = returnStatement.getReturnValue();
if (returnValue != null) {
if (expressionDefinitelyRecurses(returnValue, method)) {
return true;
}
}
return false;
}
else if (statement instanceof PsiForStatement) {
return forStatementDefinitelyRecurses((PsiForStatement)
statement, method);
}
else if (statement instanceof PsiForeachStatement) {
return foreachStatementDefinitelyRecurses(
(PsiForeachStatement)statement, method);
}
else if (statement instanceof PsiWhileStatement) {
return whileStatementDefinitelyRecurses(
(PsiWhileStatement)statement, method);
}
else if (statement instanceof PsiDoWhileStatement) {
return doWhileStatementDefinitelyRecurses(
(PsiDoWhileStatement)statement, method);
}
else if (statement instanceof PsiSynchronizedStatement) {
final PsiCodeBlock body = ((PsiSynchronizedStatement)statement)
.getBody();
return codeBlockDefinitelyRecurses(body, method);
}
else if (statement instanceof PsiBlockStatement) {
final PsiCodeBlock codeBlock = ((PsiBlockStatement)statement)
.getCodeBlock();
return codeBlockDefinitelyRecurses(codeBlock, method);
}
else if (statement instanceof PsiLabeledStatement) {
return labeledStatementDefinitelyRecurses(
(PsiLabeledStatement)statement, method);
}
else if (statement instanceof PsiIfStatement) {
return ifStatementDefinitelyRecurses(
(PsiIfStatement)statement, method);
}
else if (statement instanceof PsiTryStatement) {
return tryStatementDefinitelyRecurses(
(PsiTryStatement)statement, method);
}
else if (statement instanceof PsiSwitchStatement) {
return switchStatementDefinitelyRecurses(
(PsiSwitchStatement)statement, method);
}
else {
// unknown statement type
return false;
}
}
private static boolean switchStatementDefinitelyRecurses(
PsiSwitchStatement switchStatement, PsiMethod method) {
final PsiExpression switchExpression = switchStatement.getExpression();
return expressionDefinitelyRecurses(switchExpression, method);
}
private static boolean tryStatementDefinitelyRecurses(
PsiTryStatement tryStatement, PsiMethod method) {
final PsiCodeBlock tryBlock = tryStatement.getTryBlock();
if (codeBlockDefinitelyRecurses(tryBlock, method)) {
return true;
}
final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
return codeBlockDefinitelyRecurses(finallyBlock, method);
}
private static boolean codeBlockDefinitelyRecurses(PsiCodeBlock block,
PsiMethod method) {
if (block == null) {
return false;
}
final PsiStatement[] statements = block.getStatements();
for (final PsiStatement statement : statements) {
if (statementDefinitelyRecurses(statement, method)) {
return true;
}
}
return false;
}
private static boolean ifStatementDefinitelyRecurses(
PsiIfStatement ifStatement, PsiMethod method) {
final PsiExpression condition = ifStatement.getCondition();
if (expressionDefinitelyRecurses(condition, method)) {
return true;
}
final PsiStatement thenBranch = ifStatement.getThenBranch();
final PsiStatement elseBranch = ifStatement.getElseBranch();
if (thenBranch == null) {
return false;
}
final Object value =
ExpressionUtils.computeConstantExpression(condition);
if (value == Boolean.TRUE) {
return statementDefinitelyRecurses(thenBranch, method);
}
else if (value == Boolean.FALSE) {
return elseBranch != null &&
statementDefinitelyRecurses(elseBranch, method);
}
return statementDefinitelyRecurses(thenBranch, method) &&
statementDefinitelyRecurses(elseBranch, method);
}
private static boolean forStatementDefinitelyRecurses(
PsiForStatement forStatement, PsiMethod method) {
final PsiStatement initialization = forStatement.getInitialization();
if (statementDefinitelyRecurses(initialization, method)) {
return true;
}
final PsiExpression condition = forStatement.getCondition();
if (expressionDefinitelyRecurses(condition, method)) {
return true;
}
final Object value =
ExpressionUtils.computeConstantExpression(condition);
if (value == Boolean.TRUE) {
final PsiStatement body = forStatement.getBody();
return statementDefinitelyRecurses(body, method);
}
return false;
}
private static boolean foreachStatementDefinitelyRecurses(
PsiForeachStatement foreachStatement, PsiMethod method) {
final PsiExpression iteration = foreachStatement.getIteratedValue();
return expressionDefinitelyRecurses(iteration, method);
}
private static boolean whileStatementDefinitelyRecurses(
PsiWhileStatement whileStatement, PsiMethod method) {
final PsiExpression condition = whileStatement.getCondition();
if (expressionDefinitelyRecurses(condition, method)) {
return true;
}
final Object value =
ExpressionUtils.computeConstantExpression(condition);
if (value == Boolean.TRUE) {
final PsiStatement body = whileStatement.getBody();
return statementDefinitelyRecurses(body, method);
}
return false;
}
private static boolean doWhileStatementDefinitelyRecurses(
PsiDoWhileStatement doWhileStatement, PsiMethod method) {
final PsiStatement body = doWhileStatement.getBody();
if (statementDefinitelyRecurses(body, method)) {
return true;
}
final PsiExpression condition = doWhileStatement.getCondition();
return expressionDefinitelyRecurses(condition, method);
}
private static boolean labeledStatementDefinitelyRecurses(
PsiLabeledStatement labeledStatement, PsiMethod method) {
final PsiStatement body = labeledStatement.getStatement();
return statementDefinitelyRecurses(body, method);
}
public static boolean methodDefinitelyRecurses(
@NotNull PsiMethod method) {
final PsiCodeBlock body = method.getBody();
return body != null &&
!codeBlockMayReturnBeforeRecursing(body, method, true);
}
}