Package com.google.dart.engine.internal.hint

Source Code of com.google.dart.engine.internal.hint.ExitDetector

/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html
*
* 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.google.dart.engine.internal.hint;

import com.google.dart.engine.ast.ArgumentList;
import com.google.dart.engine.ast.AsExpression;
import com.google.dart.engine.ast.AssertStatement;
import com.google.dart.engine.ast.AssignmentExpression;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.BinaryExpression;
import com.google.dart.engine.ast.Block;
import com.google.dart.engine.ast.BlockFunctionBody;
import com.google.dart.engine.ast.BooleanLiteral;
import com.google.dart.engine.ast.BreakStatement;
import com.google.dart.engine.ast.CascadeExpression;
import com.google.dart.engine.ast.ConditionalExpression;
import com.google.dart.engine.ast.ContinueStatement;
import com.google.dart.engine.ast.DoStatement;
import com.google.dart.engine.ast.EmptyStatement;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.ExpressionStatement;
import com.google.dart.engine.ast.ForEachStatement;
import com.google.dart.engine.ast.ForStatement;
import com.google.dart.engine.ast.FunctionDeclarationStatement;
import com.google.dart.engine.ast.FunctionExpression;
import com.google.dart.engine.ast.FunctionExpressionInvocation;
import com.google.dart.engine.ast.Identifier;
import com.google.dart.engine.ast.IfStatement;
import com.google.dart.engine.ast.IndexExpression;
import com.google.dart.engine.ast.InstanceCreationExpression;
import com.google.dart.engine.ast.IsExpression;
import com.google.dart.engine.ast.Label;
import com.google.dart.engine.ast.LabeledStatement;
import com.google.dart.engine.ast.Literal;
import com.google.dart.engine.ast.MethodInvocation;
import com.google.dart.engine.ast.NamedExpression;
import com.google.dart.engine.ast.NodeList;
import com.google.dart.engine.ast.ParenthesizedExpression;
import com.google.dart.engine.ast.PostfixExpression;
import com.google.dart.engine.ast.PrefixExpression;
import com.google.dart.engine.ast.PropertyAccess;
import com.google.dart.engine.ast.RethrowExpression;
import com.google.dart.engine.ast.ReturnStatement;
import com.google.dart.engine.ast.Statement;
import com.google.dart.engine.ast.SuperExpression;
import com.google.dart.engine.ast.SwitchCase;
import com.google.dart.engine.ast.SwitchDefault;
import com.google.dart.engine.ast.SwitchMember;
import com.google.dart.engine.ast.SwitchStatement;
import com.google.dart.engine.ast.ThisExpression;
import com.google.dart.engine.ast.ThrowExpression;
import com.google.dart.engine.ast.TryStatement;
import com.google.dart.engine.ast.TypeName;
import com.google.dart.engine.ast.VariableDeclaration;
import com.google.dart.engine.ast.VariableDeclarationList;
import com.google.dart.engine.ast.VariableDeclarationStatement;
import com.google.dart.engine.ast.WhileStatement;
import com.google.dart.engine.ast.visitor.GeneralizingAstVisitor;
import com.google.dart.engine.scanner.TokenType;

/**
* Instances of the class {@code ExitDetector} determine whether the visited AST node is guaranteed
* to terminate by executing a {@code return} statement, {@code throw} expression, {@code rethrow}
* expression, or simple infinite loop such as {@code while(true)}.
*/
public class ExitDetector extends GeneralizingAstVisitor<Boolean> {

  /**
   * Set to {@code true} when a {@code break} is encountered, and reset to {@code false} when a
   * {@code do}, {@code while}, {@code for} or {@code switch} block is entered.
   */
  // TODO (jwren) This support only covers a subset of cases.  Labeled breaks and continues aren't
  // covered.
  private boolean enclosingBlockContainsBreak = false;

  /**
   * Initialize a newly created return detector.
   */
  public ExitDetector() {
    super();
  }

  @Override
  public Boolean visitArgumentList(ArgumentList node) {
    return visitExpressions(node.getArguments());
  }

  @Override
  public Boolean visitAsExpression(AsExpression node) {
    return nodeExits(node.getExpression());
  }

  @Override
  public Boolean visitAssertStatement(AssertStatement node) {
    return nodeExits(node.getCondition());
  }

  @Override
  public Boolean visitAssignmentExpression(AssignmentExpression node) {
    return nodeExits(node.getLeftHandSide()) || nodeExits(node.getRightHandSide());
  }

  @Override
  public Boolean visitBinaryExpression(BinaryExpression node) {
    Expression lhsExpression = node.getLeftOperand();
    TokenType operatorType = node.getOperator().getType();
    // If the operator is || and the left hand side is false literal, don't consider the RHS of the
    // binary expression.
    // TODO(jwren) Do we want to take constant expressions into account, evaluate if(false) {}
    // differently than if(<condition>), when <condition> evaluates to a constant false value?
    if (operatorType == TokenType.BAR_BAR) {
      if (lhsExpression instanceof BooleanLiteral) {
        BooleanLiteral booleanLiteral = (BooleanLiteral) lhsExpression;
        if (!booleanLiteral.getValue()) {
          return false;
        }
      }
    }
    // If the operator is && and the left hand side is true literal, don't consider the RHS of the
    // binary expression.
    if (operatorType == TokenType.AMPERSAND_AMPERSAND) {
      if (lhsExpression instanceof BooleanLiteral) {
        BooleanLiteral booleanLiteral = (BooleanLiteral) lhsExpression;
        if (booleanLiteral.getValue()) {
          return false;
        }
      }
    }
    Expression rhsExpression = node.getRightOperand();
    return nodeExits(lhsExpression) || nodeExits(rhsExpression);
  }

  @Override
  public Boolean visitBlock(Block node) {
    return visitStatements(node.getStatements());
  }

  @Override
  public Boolean visitBlockFunctionBody(BlockFunctionBody node) {
    return nodeExits(node.getBlock());
  }

  @Override
  public Boolean visitBreakStatement(BreakStatement node) {
    enclosingBlockContainsBreak = true;
    return false;
  }

  @Override
  public Boolean visitCascadeExpression(CascadeExpression node) {
    return nodeExits(node.getTarget()) || visitExpressions(node.getCascadeSections());
  }

  @Override
  public Boolean visitConditionalExpression(ConditionalExpression node) {
    Expression conditionExpression = node.getCondition();
    Expression thenStatement = node.getThenExpression();
    Expression elseStatement = node.getElseExpression();
    // TODO(jwren) Do we want to take constant expressions into account, evaluate if(false) {}
    // differently than if(<condition>), when <condition> evaluates to a constant false value?
    if (nodeExits(conditionExpression)) {
      return true;
    }
    if (thenStatement == null || elseStatement == null) {
      return false;
    }
    return thenStatement.accept(this) && elseStatement.accept(this);
  }

  @Override
  public Boolean visitContinueStatement(ContinueStatement node) {
    return false;
  }

  @Override
  public Boolean visitDoStatement(DoStatement node) {
    boolean outerBreakValue = enclosingBlockContainsBreak;
    enclosingBlockContainsBreak = false;
    try {
      Expression conditionExpression = node.getCondition();
      if (nodeExits(conditionExpression)) {
        return true;
      }
      // TODO(jwren) Do we want to take all constant expressions into account?
      if (conditionExpression instanceof BooleanLiteral) {
        BooleanLiteral booleanLiteral = (BooleanLiteral) conditionExpression;
        // If do {} while (true), and the body doesn't return or the body doesn't have a break, then
        // return true.
        boolean blockReturns = nodeExits(node.getBody());
        if (booleanLiteral.getValue() && (blockReturns || !enclosingBlockContainsBreak)) {
          return true;
        }
      }
      return false;
    } finally {
      enclosingBlockContainsBreak = outerBreakValue;
    }
  }

  @Override
  public Boolean visitEmptyStatement(EmptyStatement node) {
    return false;
  }

  @Override
  public Boolean visitExpressionStatement(ExpressionStatement node) {
    return nodeExits(node.getExpression());
  }

  @Override
  public Boolean visitForEachStatement(ForEachStatement node) {
    boolean outerBreakValue = enclosingBlockContainsBreak;
    enclosingBlockContainsBreak = false;
    try {
      return nodeExits(node.getIterator());
    } finally {
      enclosingBlockContainsBreak = outerBreakValue;
    }
  }

  @Override
  public Boolean visitForStatement(ForStatement node) {
    boolean outerBreakValue = enclosingBlockContainsBreak;
    enclosingBlockContainsBreak = false;
    try {
      if (node.getVariables() != null
          && visitVariableDeclarations(node.getVariables().getVariables())) {
        return true;
      }
      if (node.getInitialization() != null && nodeExits(node.getInitialization())) {
        return true;
      }
      Expression conditionExpression = node.getCondition();
      if (conditionExpression != null && nodeExits(conditionExpression)) {
        return true;
      }
      if (visitExpressions(node.getUpdaters())) {
        return true;
      }
      // TODO(jwren) Do we want to take all constant expressions into account?
      // If for(; true; ) (or for(;;)), and the body doesn't return or the body doesn't have a
      // break, then return true.
      boolean implicitOrExplictTrue = conditionExpression == null
          || (conditionExpression instanceof BooleanLiteral && ((BooleanLiteral) conditionExpression).getValue());
      if (implicitOrExplictTrue) {
        boolean blockReturns = nodeExits(node.getBody());
        if (blockReturns || !enclosingBlockContainsBreak) {
          return true;
        }
      }
      return false;
    } finally {
      enclosingBlockContainsBreak = outerBreakValue;
    }
  }

  @Override
  public Boolean visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
    return false;
  }

  @Override
  public Boolean visitFunctionExpression(FunctionExpression node) {
    return false;
  }

  @Override
  public Boolean visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
    if (nodeExits(node.getFunction())) {
      return true;
    }
    return node.getArgumentList().accept(this);
  }

  @Override
  public Boolean visitIdentifier(Identifier node) {
    return false;
  }

  @Override
  public Boolean visitIfStatement(IfStatement node) {
    Expression conditionExpression = node.getCondition();
    Statement thenStatement = node.getThenStatement();
    Statement elseStatement = node.getElseStatement();
    if (nodeExits(conditionExpression)) {
      return true;
    }
    // TODO(jwren) Do we want to take all constant expressions into account?
    if (conditionExpression instanceof BooleanLiteral) {
      BooleanLiteral booleanLiteral = (BooleanLiteral) conditionExpression;
      if (booleanLiteral.getValue()) {
        // if(true) ...
        return nodeExits(thenStatement);
      } else if (elseStatement != null) {
        // if (false) ...
        return nodeExits(elseStatement);
      }
    }
    if (thenStatement == null || elseStatement == null) {
      return false;
    }
    return nodeExits(thenStatement) && nodeExits(elseStatement);
  }

  @Override
  public Boolean visitIndexExpression(IndexExpression node) {
    Expression target = node.getRealTarget();
    if (nodeExits(target)) {
      return true;
    }
    if (nodeExits(node.getIndex())) {
      return true;
    }
    return false;
  }

  @Override
  public Boolean visitInstanceCreationExpression(InstanceCreationExpression node) {
    return nodeExits(node.getArgumentList());
  }

  @Override
  public Boolean visitIsExpression(IsExpression node) {
    return node.getExpression().accept(this);
  }

  @Override
  public Boolean visitLabel(Label node) {
    return false;
  }

  @Override
  public Boolean visitLabeledStatement(LabeledStatement node) {
    return node.getStatement().accept(this);
  }

  @Override
  public Boolean visitLiteral(Literal node) {
    return false;
  }

  @Override
  public Boolean visitMethodInvocation(MethodInvocation node) {
    Expression target = node.getRealTarget();
    if (target != null && target.accept(this)) {
      return true;
    }
    return nodeExits(node.getArgumentList());
  }

  @Override
  public Boolean visitNamedExpression(NamedExpression node) {
    return node.getExpression().accept(this);
  }

  @Override
  public Boolean visitParenthesizedExpression(ParenthesizedExpression node) {
    return node.getExpression().accept(this);
  }

  @Override
  public Boolean visitPostfixExpression(PostfixExpression node) {
    return false;
  }

  @Override
  public Boolean visitPrefixExpression(PrefixExpression node) {
    return false;
  }

  @Override
  public Boolean visitPropertyAccess(PropertyAccess node) {
    Expression target = node.getRealTarget();
    if (target != null && target.accept(this)) {
      return true;
    }
    return false;
  }

  @Override
  public Boolean visitRethrowExpression(RethrowExpression node) {
    return true;
  }

  @Override
  public Boolean visitReturnStatement(ReturnStatement node) {
    return true;
  }

  @Override
  public Boolean visitSuperExpression(SuperExpression node) {
    return false;
  }

  @Override
  public Boolean visitSwitchCase(SwitchCase node) {
    return visitStatements(node.getStatements());
  }

  @Override
  public Boolean visitSwitchDefault(SwitchDefault node) {
    return visitStatements(node.getStatements());
  }

  @Override
  public Boolean visitSwitchStatement(SwitchStatement node) {
    boolean outerBreakValue = enclosingBlockContainsBreak;
    enclosingBlockContainsBreak = false;
    try {
      boolean hasDefault = false;
      NodeList<SwitchMember> memberList = node.getMembers();
      SwitchMember[] members = memberList.toArray(new SwitchMember[memberList.size()]);
      for (int i = 0; i < members.length; i++) {
        SwitchMember switchMember = members[i];
        if (switchMember instanceof SwitchDefault) {
          hasDefault = true;
          // If this is the last member and there are no statements, return false
          if (switchMember.getStatements().isEmpty() && i + 1 == members.length) {
            return false;
          }
        }
        // For switch members with no statements, don't visit the children, otherwise, return false if
        // no return is found in the children statements
        if (!switchMember.getStatements().isEmpty() && !switchMember.accept(this)) {
          return false;
        }
      }
      return hasDefault;
    } finally {
      enclosingBlockContainsBreak = outerBreakValue;
    }
  }

  @Override
  public Boolean visitThisExpression(ThisExpression node) {
    return false;
  }

  @Override
  public Boolean visitThrowExpression(ThrowExpression node) {
    return true;
  }

  @Override
  public Boolean visitTryStatement(TryStatement node) {
    if (nodeExits(node.getBody())) {
      return true;
    }
    Block finallyBlock = node.getFinallyBlock();
    if (nodeExits(finallyBlock)) {
      return true;
    }
    return false;
  }

  @Override
  public Boolean visitTypeName(TypeName node) {
    return false;
  }

  @Override
  public Boolean visitVariableDeclaration(VariableDeclaration node) {
    Expression initializer = node.getInitializer();
    if (initializer != null) {
      return initializer.accept(this);
    }
    return false;
  }

  @Override
  public Boolean visitVariableDeclarationList(VariableDeclarationList node) {
    return visitVariableDeclarations(node.getVariables());
  }

  @Override
  public Boolean visitVariableDeclarationStatement(VariableDeclarationStatement node) {
    NodeList<VariableDeclaration> variables = node.getVariables().getVariables();
    for (int i = 0; i < variables.size(); i++) {
      if (variables.get(i).accept(this)) {
        return true;
      }
    }
    return false;
  }

  @Override
  public Boolean visitWhileStatement(WhileStatement node) {
    boolean outerBreakValue = enclosingBlockContainsBreak;
    enclosingBlockContainsBreak = false;
    try {
      Expression conditionExpression = node.getCondition();
      if (conditionExpression.accept(this)) {
        return true;
      }
      // TODO(jwren) Do we want to take all constant expressions into account?
      if (conditionExpression instanceof BooleanLiteral) {
        BooleanLiteral booleanLiteral = (BooleanLiteral) conditionExpression;
        // If while(true), and the body doesn't return or the body doesn't have a break, then return
        // true.
        boolean blockReturns = node.getBody().accept(this);
        if (booleanLiteral.getValue() && (blockReturns || !enclosingBlockContainsBreak)) {
          return true;
        }
      }
      return false;
    } finally {
      enclosingBlockContainsBreak = outerBreakValue;
    }
  }

  /**
   * Return {@code true} if the given node exits.
   *
   * @param node the node being tested
   * @return {@code true} if the given node exits
   */
  private boolean nodeExits(AstNode node) {
    if (node == null) {
      return false;
    }
    return node.accept(this);
  }

  private boolean visitExpressions(NodeList<Expression> expressions) {
    for (int i = expressions.size() - 1; i >= 0; i--) {
      if (expressions.get(i).accept(this)) {
        return true;
      }
    }
    return false;
  }

  private boolean visitStatements(NodeList<Statement> statements) {
    for (int i = statements.size() - 1; i >= 0; i--) {
      if (statements.get(i).accept(this)) {
        return true;
      }
    }
    return false;
  }

  private boolean visitVariableDeclarations(NodeList<VariableDeclaration> variableDeclarations) {
    for (int i = variableDeclarations.size() - 1; i >= 0; i--) {
      if (variableDeclarations.get(i).accept(this)) {
        return true;
      }
    }
    return false;
  }
}
TOP

Related Classes of com.google.dart.engine.internal.hint.ExitDetector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.