use of com.google.javascript.jscomp.base.Tri in project closure-compiler by google.
the class PeepholeMinimizeConditions method performConditionSubstitutions.
/**
* Try to minimize the given condition by applying local substitutions.
*
* The following types of transformations are performed:
* x || true --> true
* x && true --> x
* x ? false : true --> !x
* x ? true : y --> x || y
* x ? x : y --> x || y
*
* Returns the replacement for n, or the original if no change was made
*/
private Node performConditionSubstitutions(Node n) {
Node parent = n.getParent();
switch(n.getToken()) {
case OR:
case AND:
{
Node left = n.getFirstChild();
Node right = n.getLastChild();
// Because the expression is in a boolean context minimize
// the children, this can't be done in the general case.
left = performConditionSubstitutions(left);
right = performConditionSubstitutions(right);
// Remove useless conditionals
// Handle the following cases:
// x || false --> x
// x && true --> x
// This works regardless of whether x has side effects.
//
// If x does not have side effects:
// x || true --> true
// x && false --> false
//
// If x may have side effects:
// x || true --> x,true
// x && false --> x,false
//
// In the last two cases, code size may increase slightly (adding
// some parens because the comma operator has a low precedence) but
// the new AST is easier for other passes to handle.
Tri rightVal = getSideEffectFreeBooleanValue(right);
if (getSideEffectFreeBooleanValue(right) != Tri.UNKNOWN) {
Token type = n.getToken();
Node replacement = null;
boolean rval = rightVal.toBoolean(true);
// (x && TRUE) => x
if ((type == Token.OR && !rval) || (type == Token.AND && rval)) {
replacement = left;
} else if (!mayHaveSideEffects(left)) {
replacement = right;
} else {
// expr_with_sideeffects || true => expr_with_sideeffects, true
// expr_with_sideeffects && false => expr_with_sideeffects, false
n.detachChildren();
replacement = IR.comma(left, right);
}
if (replacement != null) {
n.detachChildren();
n.replaceWith(replacement);
reportChangeToEnclosingScope(parent);
return replacement;
}
}
return n;
}
case HOOK:
{
Node condition = n.getFirstChild();
Node trueNode = n.getSecondChild();
Node falseNode = n.getLastChild();
// Because the expression is in a boolean context minimize
// the result children, this can't be done in the general case.
// The condition is handled in the general case in #optimizeSubtree
trueNode = performConditionSubstitutions(trueNode);
falseNode = performConditionSubstitutions(falseNode);
// Handle five cases:
// x ? true : false --> x
// x ? false : true --> !x
// x ? true : y --> x || y
// x ? y : false --> x && y
// Only when x is NAME, hence x does not have side effects
// x ? x : y --> x || y
Node replacement = null;
Tri trueNodeVal = getSideEffectFreeBooleanValue(trueNode);
Tri falseNodeVal = getSideEffectFreeBooleanValue(falseNode);
if (trueNodeVal == Tri.TRUE && falseNodeVal == Tri.FALSE) {
// Remove useless conditionals, keep the condition
condition.detach();
replacement = condition;
} else if (trueNodeVal == Tri.FALSE && falseNodeVal == Tri.TRUE) {
// Remove useless conditionals, keep the condition
condition.detach();
replacement = IR.not(condition);
} else if (trueNodeVal == Tri.TRUE) {
// Remove useless true case.
n.detachChildren();
replacement = IR.or(condition, falseNode);
} else if (falseNodeVal == Tri.FALSE) {
// Remove useless false case
n.detachChildren();
replacement = IR.and(condition, trueNode);
} else if (!mayHaveSideEffects(condition) && !mayHaveSideEffects(trueNode) && condition.isEquivalentTo(trueNode)) {
// Remove redundant condition
n.detachChildren();
replacement = IR.or(trueNode, falseNode);
}
if (replacement != null) {
n.replaceWith(replacement);
reportChangeToEnclosingScope(replacement);
n = replacement;
}
return n;
}
default:
// while(true) --> while(1)
Tri nVal = getSideEffectFreeBooleanValue(n);
if (nVal != Tri.UNKNOWN) {
boolean result = nVal.toBoolean(true);
int equivalentResult = result ? 1 : 0;
return maybeReplaceChildWithNumber(n, equivalentResult);
}
// We can't do anything else currently.
return n;
}
}
use of com.google.javascript.jscomp.base.Tri in project closure-compiler by google.
the class NodeUtil method getBooleanValue.
/**
* Gets the boolean value of a node that represents an expression, or {@code Tri.UNKNOWN} if no
* such value can be determined by static analysis.
*
* <p>This method does not consider whether the node may have side-effects.
*/
static Tri getBooleanValue(Node n) {
// which we will call if none of these match.
switch(n.getToken()) {
case NULL:
case FALSE:
case VOID:
return Tri.FALSE;
case TRUE:
case REGEXP:
case FUNCTION:
case CLASS:
case NEW:
case ARRAYLIT:
case OBJECTLIT:
return Tri.TRUE;
case TEMPLATELIT:
if (n.hasOneChild()) {
Node templateLitString = n.getOnlyChild();
checkState(templateLitString.isTemplateLitString(), templateLitString);
String cookedString = templateLitString.getCookedString();
return Tri.forBoolean(cookedString != null && !cookedString.isEmpty());
} else {
return Tri.UNKNOWN;
}
case STRINGLIT:
return Tri.forBoolean(n.getString().length() > 0);
case NUMBER:
return Tri.forBoolean(n.getDouble() != 0);
case BIGINT:
return Tri.forBoolean(!n.getBigInt().equals(BigInteger.ZERO));
case NOT:
return getBooleanValue(n.getLastChild()).not();
case NAME:
// We assume here that programs don't change the value of these global variables.
switch(n.getString()) {
case "undefined":
case "NaN":
return Tri.FALSE;
case "Infinity":
return Tri.TRUE;
default:
return Tri.UNKNOWN;
}
case BITNOT:
case POS:
case NEG:
{
Double doubleVal = getNumberValue(n);
if (doubleVal != null) {
boolean isFalsey = doubleVal.isNaN() || isEitherZero(doubleVal);
return Tri.forBoolean(!isFalsey);
}
BigInteger bigintVal = getBigIntValue(n);
if (bigintVal != null) {
boolean isFalsey = bigintVal.equals(BigInteger.ZERO);
return Tri.forBoolean(!isFalsey);
}
return Tri.UNKNOWN;
}
case ASSIGN:
case COMMA:
// For ASSIGN and COMMA the value is the value of the RHS.
return getBooleanValue(n.getLastChild());
case AND:
case ASSIGN_AND:
{
Tri lhs = getBooleanValue(n.getFirstChild());
Tri rhs = getBooleanValue(n.getLastChild());
return lhs.and(rhs);
}
case OR:
case ASSIGN_OR:
{
Tri lhs = getBooleanValue(n.getFirstChild());
Tri rhs = getBooleanValue(n.getLastChild());
return lhs.or(rhs);
}
case HOOK:
{
Tri trueValue = getBooleanValue(n.getSecondChild());
Tri falseValue = getBooleanValue(n.getLastChild());
if (trueValue.equals(falseValue)) {
return trueValue;
} else {
return Tri.UNKNOWN;
}
}
case COALESCE:
case ASSIGN_COALESCE:
{
Tri lhs = getBooleanValue(n.getFirstChild());
Tri rhs = getBooleanValue(n.getLastChild());
if (lhs.equals(Tri.TRUE) || lhs.equals(rhs)) {
return lhs;
} else {
return Tri.UNKNOWN;
}
}
default:
return Tri.UNKNOWN;
}
}
use of com.google.javascript.jscomp.base.Tri in project closure-compiler by google.
the class TypeCheck method visit.
/**
* This is the meat of the type checking. It is basically one big switch, with each case
* representing one type of parse tree node. The individual cases are usually pretty
* straightforward.
*
* @param t The node traversal object that supplies context, such as the scope chain to use in
* name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of the node n.
*/
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
JSType childType;
JSType leftType;
JSType rightType;
Node left;
Node right;
// To be explicitly set to false if the node is not typeable.
boolean typeable = true;
validator.expectWellFormedTemplatizedType(n);
switch(n.getToken()) {
case CAST:
Node expr = n.getFirstChild();
JSType exprType = getJSType(expr);
JSType castType = getJSType(n);
// way.
if (!expr.isObjectLit()) {
validator.expectCanCast(n, castType, exprType);
}
ensureTyped(n, castType);
expr.setJSTypeBeforeCast(exprType);
if (castType.restrictByNotNullOrUndefined().isSubtypeOf(exprType) || expr.isObjectLit()) {
expr.setJSType(castType);
}
break;
case NAME:
typeable = visitName(t, n, parent);
break;
case COMMA:
ensureTyped(n, getJSType(n.getLastChild()));
break;
case THIS:
ensureTyped(n, t.getTypedScope().getTypeOfThis());
break;
case NULL:
ensureTyped(n, NULL_TYPE);
break;
case NUMBER:
ensureTyped(n, NUMBER_TYPE);
break;
case BIGINT:
ensureTyped(n, BIGINT_TYPE);
break;
case GETTER_DEF:
case SETTER_DEF:
// Object literal keys are handled with OBJECTLIT
break;
case ARRAYLIT:
ensureTyped(n, ARRAY_TYPE);
break;
case REGEXP:
ensureTyped(n, REGEXP_TYPE);
break;
case GETPROP:
visitGetProp(t, n);
typeable = !(parent.isAssign() && parent.getFirstChild() == n);
break;
case OPTCHAIN_GETPROP:
visitOptChainGetProp(n);
break;
case OPTCHAIN_GETELEM:
visitOptChainGetElem(n);
break;
case GETELEM:
visitGetElem(n);
// The type of GETELEM is always unknown, so no point counting that.
// If that unknown leaks elsewhere (say by an assignment to another
// variable), then it will be counted.
typeable = false;
break;
case VAR:
case LET:
case CONST:
visitVar(t, n);
typeable = false;
break;
case NEW:
visitNew(n);
break;
case OPTCHAIN_CALL:
// We reuse the `visitCall` functionality for OptChain call nodes because we don't report
// an error for regular calls when the callee is null or undefined. However, we make sure
// `typeable` isn't explicitly unset as OptChain nodes are always typed during inference.
visitCall(t, n);
break;
case CALL:
visitCall(t, n);
typeable = !parent.isExprResult();
break;
case RETURN:
visitReturn(t, n);
typeable = false;
break;
case YIELD:
visitYield(t, n);
break;
case DEC:
case INC:
left = n.getFirstChild();
checkPropCreation(left);
if (getJSType(n).isNumber()) {
validator.expectNumber(left, getJSType(left), "increment/decrement");
ensureTyped(n, NUMBER_TYPE);
} else {
validator.expectBigIntOrNumber(left, getJSType(left), "increment/decrement");
}
break;
case VOID:
ensureTyped(n, VOID_TYPE);
break;
case STRINGLIT:
case TYPEOF:
case TEMPLATELIT:
case TEMPLATELIT_STRING:
ensureTyped(n, STRING_TYPE);
break;
case TAGGED_TEMPLATELIT:
visitTaggedTemplateLit(n);
ensureTyped(n);
break;
case BITNOT:
visitBitwiseNOT(n);
break;
case POS:
visitUnaryPlus(n);
break;
case NEG:
visitUnaryMinus(n);
break;
case EQ:
case NE:
case SHEQ:
case SHNE:
{
left = n.getFirstChild();
right = n.getLastChild();
if (left.isTypeOf()) {
if (right.isStringLit()) {
checkTypeofString(right, right.getString());
}
} else if (right.isTypeOf() && left.isStringLit()) {
checkTypeofString(left, left.getString());
}
leftType = getJSType(left);
rightType = getJSType(right);
// We do not want to warn about explicit comparisons to VOID. People
// often do this if they think their type annotations screwed up.
//
// We do want to warn about cases where people compare things like
// (Array|null) == (Function|null)
// because it probably means they screwed up.
//
// This heuristic here is not perfect, but should catch cases we
// care about without too many false negatives.
JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
Tri result = Tri.UNKNOWN;
if (n.getToken() == Token.EQ || n.isNE()) {
result = leftTypeRestricted.testForEquality(rightTypeRestricted);
if (n.isNE()) {
result = result.not();
}
} else {
// SHEQ or SHNE
if (!leftTypeRestricted.canTestForShallowEqualityWith(rightTypeRestricted)) {
result = n.getToken() == Token.SHEQ ? Tri.FALSE : Tri.TRUE;
}
}
if (result != Tri.UNKNOWN) {
report(n, DETERMINISTIC_TEST, leftType.toString(), rightType.toString(), result.toString());
}
ensureTyped(n, BOOLEAN_TYPE);
break;
}
case LT:
case LE:
case GT:
case GE:
Node leftSide = n.getFirstChild();
Node rightSide = n.getLastChild();
leftType = getJSType(leftSide);
rightType = getJSType(rightSide);
if (rightType.isUnknownType()) {
// validate comparable left
validator.expectUnknownOrComparable(leftSide, leftType, "left side of comparison");
} else if (leftType.isUnknownType()) {
// validate comparable right
validator.expectUnknownOrComparable(rightSide, rightType, "right side of comparison");
} else if (rightType.isBigIntOrNumber()) {
// validate left operand for numeric comparison
validator.expectBigIntOrNumber(leftSide, leftType, "left side of numeric comparison");
} else if (leftType.isBigIntOrNumber()) {
// validate right operand for numeric comparison
validator.expectBigIntOrNumber(rightSide, rightType, "right side of numeric comparison");
} else {
String errorMsg = "expected matching types in comparison";
this.validator.expectMatchingTypesStrict(n, leftType, rightType, errorMsg);
if (!leftType.matchesNumberContext() || !rightType.matchesNumberContext()) {
// Whether the comparison is numeric will be determined at runtime
// each time the expression is evaluated. Regardless, both operands
// should match a string context.
String message = "left side of comparison";
validator.expectString(leftSide, leftType, message);
validator.expectNotNullOrUndefined(t, leftSide, leftType, message, getNativeType(STRING_TYPE));
message = "right side of comparison";
validator.expectString(rightSide, rightType, message);
validator.expectNotNullOrUndefined(t, rightSide, rightType, message, getNativeType(STRING_TYPE));
}
}
ensureTyped(n, BOOLEAN_TYPE);
break;
case IN:
left = n.getFirstChild();
right = n.getLastChild();
rightType = getJSType(right);
validator.expectStringOrSymbol(left, getJSType(left), "left side of 'in'");
validator.expectObject(n, rightType, "'in' requires an object");
if (rightType.isStruct()) {
report(right, IN_USED_WITH_STRUCT);
}
ensureTyped(n, BOOLEAN_TYPE);
break;
case INSTANCEOF:
left = n.getFirstChild();
right = n.getLastChild();
rightType = getJSType(right).restrictByNotNullOrUndefined();
validator.expectAnyObject(left, getJSType(left), "deterministic instanceof yields false");
validator.expectActualObject(right, rightType, "instanceof requires an object");
ensureTyped(n, BOOLEAN_TYPE);
break;
case ASSIGN:
case ASSIGN_OR:
case ASSIGN_AND:
case ASSIGN_COALESCE:
visitAssign(t, n);
typeable = false;
break;
case ASSIGN_LSH:
case ASSIGN_RSH:
case ASSIGN_URSH:
case ASSIGN_DIV:
case ASSIGN_MOD:
case ASSIGN_BITOR:
case ASSIGN_BITXOR:
case ASSIGN_BITAND:
case ASSIGN_SUB:
case ASSIGN_ADD:
case ASSIGN_MUL:
case ASSIGN_EXPONENT:
checkPropCreation(n.getFirstChild());
case LSH:
case RSH:
case URSH:
case DIV:
case MOD:
case BITOR:
case BITXOR:
case BITAND:
case SUB:
case ADD:
case MUL:
case EXPONENT:
visitBinaryOperator(n.getToken(), n);
break;
case TRUE:
case FALSE:
case NOT:
case DELPROP:
ensureTyped(n, BOOLEAN_TYPE);
break;
case CASE:
JSType switchType = getJSType(parent.getFirstChild());
JSType caseType = getJSType(n.getFirstChild());
validator.expectSwitchMatchesCase(n, switchType, caseType);
typeable = false;
break;
case WITH:
{
Node child = n.getFirstChild();
childType = getJSType(child);
validator.expectObject(child, childType, "with requires an object");
typeable = false;
break;
}
case FUNCTION:
visitFunction(n);
break;
case CLASS:
visitClass(n);
break;
case MODULE_BODY:
visitModuleBody(t, n);
break;
// These nodes require data flow analysis.
case PARAM_LIST:
case STRING_KEY:
case MEMBER_FUNCTION_DEF:
case COMPUTED_PROP:
case MEMBER_FIELD_DEF:
case COMPUTED_FIELD_DEF:
case LABEL:
case LABEL_NAME:
case SWITCH:
case BREAK:
case CATCH:
case TRY:
case SCRIPT:
case EXPORT:
case EXPORT_SPEC:
case EXPORT_SPECS:
case IMPORT:
case IMPORT_SPEC:
case IMPORT_SPECS:
case IMPORT_STAR:
case EXPR_RESULT:
case BLOCK:
case ROOT:
case EMPTY:
case DEFAULT_CASE:
case CONTINUE:
case DEBUGGER:
case THROW:
case DO:
case IF:
case WHILE:
case FOR:
case TEMPLATELIT_SUB:
case ITER_REST:
case OBJECT_REST:
case DESTRUCTURING_LHS:
typeable = false;
break;
case DYNAMIC_IMPORT:
visitDynamicImport(t, n);
break;
case ARRAY_PATTERN:
ensureTyped(n);
validator.expectAutoboxesToIterable(n, getJSType(n), "array pattern destructuring requires an Iterable");
break;
case OBJECT_PATTERN:
visitObjectPattern(n);
break;
case DEFAULT_VALUE:
checkCanAssignToWithScope(t, n, n.getFirstChild(), getJSType(n.getSecondChild()), /* info= */
null, "default value has wrong type");
// Every other usage of a destructuring pattern is checked while visiting the pattern,
// but default values are different because they are a conditional assignment and the
// pattern is not given the default value's type
Node lhs = n.getFirstChild();
Node rhs = n.getSecondChild();
if (lhs.isArrayPattern()) {
validator.expectAutoboxesToIterable(rhs, getJSType(rhs), "array pattern destructuring requires an Iterable");
} else if (lhs.isObjectPattern()) {
// Verify that the value is not null/undefined, since those can't be destructured.
validator.expectObject(rhs, getJSType(rhs), "cannot destructure a 'null' or 'undefined' default value");
}
typeable = false;
break;
case CLASS_MEMBERS:
{
JSType typ = parent.getJSType().toMaybeFunctionType().getInstanceType();
for (Node child = n.getFirstChild(); child != null; child = child.getNext()) {
visitObjectOrClassLiteralKey(child, n.getParent(), typ);
if (child.isSetterDef() || child.isGetterDef()) {
checkGetterOrSetterType(child, parent.getJSType().toMaybeFunctionType());
}
}
typeable = false;
break;
}
case FOR_IN:
Node obj = n.getSecondChild();
if (getJSType(obj).isStruct()) {
report(obj, IN_USED_WITH_STRUCT);
}
typeable = false;
break;
case FOR_OF:
case FOR_AWAIT_OF:
ensureTyped(n.getSecondChild());
typeable = false;
break;
// These nodes are typed during the type inference.
case SUPER:
case NEW_TARGET:
case IMPORT_META:
case AWAIT:
case AND:
case HOOK:
case OR:
case COALESCE:
ensureTyped(n);
break;
case OBJECTLIT:
// If this is an enum, then give that type to the objectlit as well.
if (parent.getJSType() instanceof EnumType) {
ensureTyped(n, parent.getJSType());
} else {
ensureTyped(n);
}
JSType typ = getJSType(n);
for (Node key = n.getFirstChild(); key != null; key = key.getNext()) {
visitObjectOrClassLiteralKey(key, n, typ);
}
break;
case ITER_SPREAD:
case OBJECT_SPREAD:
checkSpread(n);
typeable = false;
break;
default:
report(n, UNEXPECTED_TOKEN, n.getToken().toString());
ensureTyped(n);
break;
}
// Visit the body of blockless arrow functions
if (NodeUtil.isBlocklessArrowFunctionResult(n)) {
visitImplicitReturnExpression(t, n);
}
// TypedScope.
if ((n.getParent().isForOf() || n.getParent().isForAwaitOf()) && n.getParent().getFirstChild() == n) {
checkForOfTypes(t, n.getParent());
}
// Don't count externs since the user's code may not even use that part.
typeable = typeable && !inExterns;
if (typeable) {
doPercentTypedAccounting(n);
}
checkJsdocInfoContainsObjectWithBadKey(n);
}
use of com.google.javascript.jscomp.base.Tri in project closure-compiler by google.
the class JSTypeTest method testSymmetryOfTestForEquality.
@Test
public void testSymmetryOfTestForEquality() {
List<JSType> listA = getTypesToTestForSymmetry();
List<JSType> listB = getTypesToTestForSymmetry();
for (JSType typeA : listA) {
for (JSType typeB : listB) {
Tri aOnB = typeA.testForEquality(typeB);
Tri bOnA = typeB.testForEquality(typeA);
assertWithMessage(lines("testForEquality not symmetrical:", "typeA: %s\ntypeB: %s", "a.testForEquality(b): %s", "b.testForEquality(a): %s"), typeA, typeB, aOnB, bOnA).that(aOnB == bOnA).isTrue();
}
}
}
use of com.google.javascript.jscomp.base.Tri in project closure-compiler by google.
the class PeepholeRemoveDeadCode method tryFoldIf.
/**
* Try folding IF nodes by removing dead branches.
* @return the replacement node, if changed, or the original if not
*/
private Node tryFoldIf(Node n) {
checkState(n.isIf(), n);
Node parent = n.getParent();
checkNotNull(parent);
Token type = n.getToken();
Node cond = n.getFirstChild();
Node thenBody = cond.getNext();
Node elseBody = thenBody.getNext();
// if (x) { .. } else { } --> if (x) { ... }
if (elseBody != null && !mayHaveSideEffects(elseBody)) {
elseBody.detach();
reportChangeToEnclosingScope(n);
elseBody = null;
}
// if (x) { } else { ... } --> if (!x) { ... }
if (!mayHaveSideEffects(thenBody) && elseBody != null) {
elseBody.detach();
thenBody.replaceWith(elseBody);
Node notCond = new Node(Token.NOT);
cond.replaceWith(notCond);
reportChangeToEnclosingScope(n);
notCond.addChildToFront(cond);
cond = notCond;
thenBody = cond.getNext();
elseBody = null;
}
// `if (x()) { }` or `if (x?.()) { }`
if (!mayHaveSideEffects(thenBody) && elseBody == null) {
if (mayHaveSideEffects(cond)) {
// `x()` or `x?.()` has side effects, just leave the condition on its own.
cond.detach();
Node replacement = NodeUtil.newExpr(cond);
n.replaceWith(replacement);
reportChangeToEnclosingScope(parent);
return replacement;
} else {
// `x()` or `x?.()` has no side effects, the whole tree is useless now.
NodeUtil.removeChild(parent, n);
reportChangeToEnclosingScope(parent);
return null;
}
}
// Try transforms that apply to both IF and HOOK.
Tri condValue = NodeUtil.getBooleanValue(cond);
if (condValue == Tri.UNKNOWN) {
// We can't remove branches otherwise!
return n;
}
if (mayHaveSideEffects(cond)) {
// Transform "if (a = 2) {x =2}" into "if (true) {a=2;x=2}"
boolean newConditionValue = condValue == Tri.TRUE;
// Add an elseBody if it is needed.
if (!newConditionValue && elseBody == null) {
elseBody = IR.block().srcref(n);
n.addChildToBack(elseBody);
}
Node newCond = NodeUtil.booleanNode(newConditionValue);
cond.replaceWith(newCond);
Node branchToKeep = newConditionValue ? thenBody : elseBody;
branchToKeep.addChildToFront(IR.exprResult(cond).srcref(cond));
reportChangeToEnclosingScope(branchToKeep);
cond = newCond;
}
boolean condTrue = condValue.toBoolean(true);
if (n.hasTwoChildren()) {
checkState(type == Token.IF);
if (condTrue) {
// Replace "if (true) { X }" with "X".
Node thenStmt = n.getSecondChild();
thenStmt.detach();
n.replaceWith(thenStmt);
reportChangeToEnclosingScope(thenStmt);
return thenStmt;
} else {
// Remove "if (false) { X }" completely.
NodeUtil.redeclareVarsInsideBranch(n);
NodeUtil.removeChild(parent, n);
reportChangeToEnclosingScope(parent);
markFunctionsDeleted(n);
return null;
}
} else {
// Replace "if (true) { X } else { Y }" with X, or
// replace "if (false) { X } else { Y }" with Y.
Node trueBranch = n.getSecondChild();
Node falseBranch = trueBranch.getNext();
Node branchToKeep = condTrue ? trueBranch : falseBranch;
Node branchToRemove = condTrue ? falseBranch : trueBranch;
NodeUtil.redeclareVarsInsideBranch(branchToRemove);
branchToKeep.detach();
n.replaceWith(branchToKeep);
reportChangeToEnclosingScope(branchToKeep);
markFunctionsDeleted(n);
return branchToKeep;
}
}
Aggregations