Skip to content

Commit

Permalink
Fix NodeUtil.isLhsFromDestructuring for destructuring patterns contai…
Browse files Browse the repository at this point in the history
…ning default values.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=177492588
  • Loading branch information
lauraharker authored and brad4d committed Dec 1, 2017
1 parent 8196374 commit e1311b0
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 15 deletions.
62 changes: 47 additions & 15 deletions src/com/google/javascript/jscomp/NodeUtil.java
Expand Up @@ -3435,28 +3435,60 @@ public static boolean isImportedName(Node n) {
}

/**
* Returns true if the node is a lhs value of a destructuring assignment
* For example,
* x in {@code var [x] = [1];}, {@code var [...x] = [1];}, and {@code var {a: x} = {a: 1}}
* Returns true if the node is a lhs value of a destructuring assignment For example, x in {@code
* var [x] = [1];}, {@code var [...x] = [1];}, and {@code var {a: x} = {a: 1}} or a.b in {@code
* ([a.b] = [1]);} or {@code ({key: a.b} = {key: 1});}
*/
public static boolean isLhsByDestructuring(Node n) {
Node node = n;
if (!(n.isName() || n.isGetProp() || n.isStringKey() || n.isGetElem())) {
return false;
}
return isLhsByDestructuringHelper(n);
}

/**
* Returns true if the given node is either an LHS node in a destructuring pattern or if one of
* its descendants contains an LHS node in a destructuring pattern. For example, in {@code var {a:
* b = 3}}}, this returns true given the NAME b or the DEFAULT_VALUE node containing b.
*/
private static boolean isLhsByDestructuringHelper(Node n) {
Node parent = n.getParent();
if (parent.isRest()) {
node = parent;
parent = parent.getParent();
Node grandparent = n.getGrandparent();

if (parent.isArrayPattern()) {
// e.g. var [b] = ...
return true;
}

if (parent.isDestructuringPattern()
|| (parent.isStringKey() && parent.getParent().isObjectPattern())
|| (parent.isComputedProp()
&& parent.getParent().isObjectPattern()
&& node == parent.getSecondChild())) {
if (node.isStringKey() && node.hasChildren()) {
return false;
}
if (parent.isStringKey() && grandparent.isObjectPattern()) {
// e.g. the "b" in "var {a: b} = ..."
return true;
}

if (parent.isObjectPattern()) {
// STRING_KEY children of object patterns are only LHS nodes if they have no children,
// meaning that they represent the object pattern shorthand (e.g. "var {a} = ...").
// If n is not a STRING_KEY, it is an OBJECT_PATTERN, DEFAULT_VALUE, or
// COMPUTED_PROP and contains a LHS node.
return !(n.isStringKey() && n.hasChildren());
}

if (parent.isComputedProp() && n == parent.getSecondChild()) {
// The first child of a COMPUTED_PROP is the property expression, not a LHS.
// The second is the value, which in an object pattern will contain the LHS.
return isLhsByDestructuringHelper(parent);
}

if (parent.isRest()) {
// The only child of a REST node is the LHS.
return isLhsByDestructuringHelper(parent);
}

if (parent.isDefaultValue() && n == parent.getFirstChild()) {
// The first child of a DEFAULT_VALUE is a NAME node and a potential LHS.
// The second child is the value, so never a LHS node.
return isLhsByDestructuringHelper(parent);
}
return false;
}

Expand Down
73 changes: 73 additions & 0 deletions test/com/google/javascript/jscomp/NodeUtilTest.java
Expand Up @@ -2545,6 +2545,79 @@ public void testLhsByDestructuring1c() {
assertNotLhsByDestructuring(nameNodeObj);
}

public void testLhsByDestructuring1d() {
Node root = parse("var {a = defaultValue} = obj;");
Node destructLhs = root.getFirstFirstChild();
checkState(destructLhs.isDestructuringLhs(), destructLhs);
Node destructPat = destructLhs.getFirstChild();
checkState(destructPat.isObjectPattern(), destructPat);

Node defaultNodeA = destructPat.getFirstChild();
checkState(defaultNodeA.isDefaultValue(), defaultNodeA);
Node strKeyNodeA = defaultNodeA.getFirstChild();
checkState(strKeyNodeA.getString().equals("a"), strKeyNodeA);

Node nameNodeDefault = defaultNodeA.getSecondChild();
checkState(nameNodeDefault.getString().equals("defaultValue"), nameNodeDefault);
Node nameNodeObj = destructPat.getNext();
checkState(nameNodeObj.getString().equals("obj"), nameNodeObj);

assertLhsByDestructuring(strKeyNodeA);
assertNotLhsByDestructuring(nameNodeDefault);
assertNotLhsByDestructuring(nameNodeObj);
}

public void testLhsByDestructuring1e() {
Node root = parse("var {a: b = defaultValue} = obj;");
Node destructLhs = root.getFirstFirstChild();
checkState(destructLhs.isDestructuringLhs(), destructLhs);
Node destructPat = destructLhs.getFirstChild();
checkState(destructPat.isObjectPattern(), destructPat);

Node strKeyNodeA = destructPat.getFirstChild();
checkState(strKeyNodeA.getString().equals("a"), strKeyNodeA);

Node defaultNodeA = strKeyNodeA.getOnlyChild();
checkState(defaultNodeA.isDefaultValue(), defaultNodeA);

Node nameNodeB = defaultNodeA.getFirstChild();
checkState(nameNodeB.getString().equals("b"), nameNodeB);

Node nameNodeDefaultValue = defaultNodeA.getSecondChild();
checkState(nameNodeDefaultValue.getString().equals("defaultValue"), nameNodeDefaultValue);
Node nameNodeObj = destructPat.getNext();
checkState(nameNodeObj.getString().equals("obj"), nameNodeObj);

assertNotLhsByDestructuring(strKeyNodeA);
assertLhsByDestructuring(nameNodeB);
assertNotLhsByDestructuring(nameNodeDefaultValue);
assertNotLhsByDestructuring(nameNodeObj);
}

public void testLhsByDestructuring1f() {
Node root = parse("var [a = defaultValue] = arr;");
Node destructLhs = root.getFirstFirstChild();
checkState(destructLhs.isDestructuringLhs(), destructLhs);
Node destructPat = destructLhs.getFirstChild();
checkState(destructPat.isArrayPattern(), destructPat);

Node defaultNodeA = destructPat.getFirstChild();

checkState(defaultNodeA.isDefaultValue(), defaultNodeA);

Node nameNodeA = defaultNodeA.getFirstChild();
checkState(nameNodeA.getString().equals("a"), nameNodeA);

Node nameNodeDefault = defaultNodeA.getSecondChild();
checkState(nameNodeDefault.getString().equals("defaultValue"), nameNodeDefault);
Node nameNodeObj = destructPat.getNext();
checkState(nameNodeObj.getString().equals("arr"), nameNodeObj);

assertLhsByDestructuring(nameNodeA);
assertNotLhsByDestructuring(nameNodeDefault);
assertNotLhsByDestructuring(nameNodeObj);
}

public void testLhsByDestructuring2() {
Node root = parse("var [a, [b, c]] = obj;");
Node destructLhs = root.getFirstFirstChild();
Expand Down

0 comments on commit e1311b0

Please sign in to comment.