Skip to content

Commit

Permalink
Update the AstValidator to better validate destructuring assignments.
Browse files Browse the repository at this point in the history
First, reject destructuring on the LHS of compound assignments.
Second, accept full LValue expressions as well as names in patterns in the LHS of DEFAULT_VALUE nodes when used on the LHS of a assignment expression.

For example this expression was rejected:
  ([ x['y'] = z ] = [])

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=171842831
  • Loading branch information
concavelenz authored and Tyler Breisacher committed Oct 11, 2017
1 parent 55ece60 commit 6fd75a2
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 15 deletions.
38 changes: 25 additions & 13 deletions src/com/google/javascript/jscomp/AstValidator.java
Expand Up @@ -240,6 +240,8 @@ public void validateExpression(Node n) {


// Assignments // Assignments
case ASSIGN: case ASSIGN:
validateAssignmentExpression(n);
return;
case ASSIGN_BITOR: case ASSIGN_BITOR:
case ASSIGN_BITXOR: case ASSIGN_BITXOR:
case ASSIGN_BITAND: case ASSIGN_BITAND:
Expand All @@ -252,7 +254,7 @@ public void validateExpression(Node n) {
case ASSIGN_DIV: case ASSIGN_DIV:
case ASSIGN_MOD: case ASSIGN_MOD:
case ASSIGN_EXPONENT: case ASSIGN_EXPONENT:
validateAssignmentExpression(n); validateCompoundAssignmentExpression(n);
return; return;


case HOOK: case HOOK:
Expand Down Expand Up @@ -770,19 +772,11 @@ private void validateParameters(Node n) {
} }
} }


private void validateDefaultValue(Token type, Node n) { private void validateDefaultValue(Token contextType, Node n) {
validateFeature(Feature.DEFAULT_PARAMETERS, n); validateFeature(Feature.DEFAULT_PARAMETERS, n);
validateAssignmentExpression(n); validateChildCount(n);
Node lhs = n.getFirstChild(); validateLHS(contextType, n.getFirstChild());

validateExpression(n.getLastChild());
// LHS can only be a name or destructuring pattern.
if (lhs.isName()) {
validateName(lhs);
} else if (lhs.isArrayPattern()) {
validateArrayPattern(type, lhs);
} else {
validateObjectPattern(type, lhs);
}
} }


private void validateCall(Node n) { private void validateCall(Node n) {
Expand Down Expand Up @@ -1171,6 +1165,24 @@ private void validateAssignmentExpression(Node n) {
validateExpression(n.getLastChild()); validateExpression(n.getLastChild());
} }


private void validateCompoundAssignmentExpression(Node n) {
validateChildCount(n);
Token contextType = n.getToken();
Node lhs = n.getFirstChild();
switch (lhs.getToken()) {
case NAME:
validateName(lhs);
break;
case GETPROP:
case GETELEM:
validateGetPropGetElemInLHS(contextType, lhs);
break;
default:
violation("Invalid child for " + contextType + " node", lhs);
}
validateExpression(n.getLastChild());
}

private void validateGetProp(Node n) { private void validateGetProp(Node n) {
validateNodeType(Token.GETPROP, n); validateNodeType(Token.GETPROP, n);
validateChildCount(n); validateChildCount(n);
Expand Down
68 changes: 66 additions & 2 deletions test/com/google/javascript/jscomp/AstValidatorTest.java
Expand Up @@ -164,12 +164,76 @@ public void testAwaitExpressionNoFunction() {
expectInvalid(n, Check.EXPRESSION); expectInvalid(n, Check.EXPRESSION);
} }


public void testValidDestructuringAssignment() { public void testInvalidArrayPattern0() {
setAcceptedLanguage(LanguageMode.ECMASCRIPT_2015); setAcceptedLanguage(LanguageMode.ECMASCRIPT_2015);

setAcceptedLanguage(LanguageMode.ECMASCRIPT_2015);

// [...x = 1] = [];
Node n = IR.assign(
new Node(Token.ARRAY_PATTERN,
new Node(Token.REST,
new Node(Token.DEFAULT_VALUE,
IR.name("x"), IR.arraylit()))),
IR.arraylit());
expectInvalid(n, Check.EXPRESSION);
}

public void testValidDestructuringAssignment0() {
setAcceptedLanguage(LanguageMode.ECMASCRIPT_2015);
valid("var [x] = obj;");
valid("var [x = 1] = obj;");
valid("var [...y] = obj;");
valid("var [x, ...y] = obj;"); valid("var [x, ...y] = obj;");

valid("[x] = [];");
valid("[x = 1] = [];");
valid("[x.y] = [];");
valid("[x.y = 1] = [];");
valid("[x['y']] = [];");
valid("[x['y'] = 1] = [];");
valid("[x().y] = [];");
valid("[x().y = 1] = [];");
valid("[x()['y']] = [];");
valid("[x()['y'] = 1] = [];");

valid("([...y] = obj);");
valid("([x, ...y] = obj);");
valid("([...this.x] = obj);"); valid("([...this.x] = obj);");
valid("([...this['x']] = obj);");
valid("([...x.y] = obj);");
valid("([...x['y']] = obj);");
valid("([...x().y] = obj);");
valid("([...x()['y']] = obj);");
}

public void testValidDestructuringAssignment1() {
setAcceptedLanguage(LanguageMode.ECMASCRIPT_2015);
valid("var {a:b} = obj;"); valid("var {a:b} = obj;");
valid("({a:this.b} = obj);"); valid("({a:b} = obj);");
valid("({a:b.c} = obj);");
valid("({a:b().c} = obj);");
valid("({a:b['c']} = obj);");
valid("({a:b()['c']} = obj);");
valid("({a:b.c = 1} = obj);");
valid("({a:b().c = 1} = obj);");
valid("({a:b['c'] = 1} = obj);");
valid("({a:b()['c'] = 1} = obj);");
}

public void testValidDestructuringAssignment2() {
setAcceptedLanguage(LanguageMode.ECMASCRIPT_2015);
valid("var {['a']:b} = obj;");
valid("({['a']:b} = obj);");
valid("({['a']:this.b} = obj);");
valid("({['a']:b.c} = obj);");
valid("({['a']:b.c = 1} = obj);");
valid("({['a']:b().c} = obj);");
valid("({['a']:b().c = 1} = obj);");
valid("({['a']:b['c']} = obj);");
valid("({['a']:b['c'] = 1} = obj);");
valid("({['a']:b()['c']} = obj);");
valid("({['a']:b()['c'] = 1} = obj);");
} }


public void testInvalidDestructuringAssignment() { public void testInvalidDestructuringAssignment() {
Expand Down

0 comments on commit 6fd75a2

Please sign in to comment.