All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.siyeh.ig.psiutils.RecursionUtils Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition java-analysis-impl library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * 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);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy