fix: the LHS in for-of loop should not start with let (#13049)
* fix: the LHS in for-of loop should not start with let * Update packages/babel-parser/src/parser/statement.js Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com> Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
This commit is contained in:
parent
5c0d2f6032
commit
8efbac4a5d
@ -57,6 +57,7 @@ export const ErrorMessages = Object.freeze({
|
|||||||
"'from' is not allowed as an identifier after 'export default'",
|
"'from' is not allowed as an identifier after 'export default'",
|
||||||
ForInOfLoopInitializer:
|
ForInOfLoopInitializer:
|
||||||
"%0 loop variable declaration may not have an initializer",
|
"%0 loop variable declaration may not have an initializer",
|
||||||
|
ForOfLet: "The left-hand side of a for-of loop may not start with 'let'.",
|
||||||
GeneratorInSingleStatementContext:
|
GeneratorInSingleStatementContext:
|
||||||
"Generators can only be declared at the top level or inside a block",
|
"Generators can only be declared at the top level or inside a block",
|
||||||
IllegalBreakContinue: "Unsyntactic %0",
|
IllegalBreakContinue: "Unsyntactic %0",
|
||||||
|
|||||||
@ -125,6 +125,19 @@ export default class StatementParser extends ExpressionParser {
|
|||||||
if (!this.isContextual("let")) {
|
if (!this.isContextual("let")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return this.isLetKeyword(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assuming we have seen a contextual `let`, check if it starts a variable declaration
|
||||||
|
so that `left` should be interpreted as a `let` keyword.
|
||||||
|
*
|
||||||
|
* @param {?string} context When `context` is non nullish, it will return early and _skip_ checking
|
||||||
|
if the next token after `let` is `{` or a keyword relational operator
|
||||||
|
* @returns {boolean}
|
||||||
|
* @memberof StatementParser
|
||||||
|
*/
|
||||||
|
isLetKeyword(context: ?string): boolean {
|
||||||
const next = this.nextTokenStart();
|
const next = this.nextTokenStart();
|
||||||
const nextCh = this.input.charCodeAt(next);
|
const nextCh = this.input.charCodeAt(next);
|
||||||
// For ambiguous cases, determine if a LexicalDeclaration (or only a
|
// For ambiguous cases, determine if a LexicalDeclaration (or only a
|
||||||
@ -511,7 +524,8 @@ export default class StatementParser extends ExpressionParser {
|
|||||||
return this.parseFor(node, null);
|
return this.parseFor(node, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLet = this.isLet();
|
const startsWithLet = this.isContextual("let");
|
||||||
|
const isLet = startsWithLet && this.isLetKeyword();
|
||||||
if (this.match(tt._var) || this.match(tt._const) || isLet) {
|
if (this.match(tt._var) || this.match(tt._const) || isLet) {
|
||||||
const init = this.startNode();
|
const init = this.startNode();
|
||||||
const kind = isLet ? "let" : this.state.value;
|
const kind = isLet ? "let" : this.state.value;
|
||||||
@ -533,11 +547,13 @@ export default class StatementParser extends ExpressionParser {
|
|||||||
|
|
||||||
const refExpressionErrors = new ExpressionErrors();
|
const refExpressionErrors = new ExpressionErrors();
|
||||||
const init = this.parseExpression(true, refExpressionErrors);
|
const init = this.parseExpression(true, refExpressionErrors);
|
||||||
if (this.match(tt._in) || this.isContextual("of")) {
|
const isForOf = this.isContextual("of");
|
||||||
|
if (isForOf || this.match(tt._in)) {
|
||||||
|
if (isForOf && startsWithLet) {
|
||||||
|
this.raise(init.start, Errors.ForOfLet);
|
||||||
|
}
|
||||||
this.toAssignable(init, /* isLHS */ true);
|
this.toAssignable(init, /* isLHS */ true);
|
||||||
const description = this.isContextual("of")
|
const description = isForOf ? "for-of statement" : "for-in statement";
|
||||||
? "for-of statement"
|
|
||||||
: "for-in statement";
|
|
||||||
this.checkLVal(init, description);
|
this.checkLVal(init, description);
|
||||||
return this.parseForIn(node, init, awaitAt);
|
return this.parseForIn(node, init, awaitAt);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
3
packages/babel-parser/test/fixtures/es2015/for-of/invalid-let-as-identifier/input.js
vendored
Normal file
3
packages/babel-parser/test/fixtures/es2015/for-of/invalid-let-as-identifier/input.js
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
for (let.foo of []);
|
||||||
|
for (let().bar of []);
|
||||||
|
for (let``.bar of []);
|
||||||
130
packages/babel-parser/test/fixtures/es2015/for-of/invalid-let-as-identifier/output.json
vendored
Normal file
130
packages/babel-parser/test/fixtures/es2015/for-of/invalid-let-as-identifier/output.json
vendored
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
{
|
||||||
|
"type": "File",
|
||||||
|
"start":0,"end":66,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":22}},
|
||||||
|
"errors": [
|
||||||
|
"SyntaxError: The left-hand side of a for-of loop may not start with 'let'. (1:5)",
|
||||||
|
"SyntaxError: The left-hand side of a for-of loop may not start with 'let'. (2:5)",
|
||||||
|
"SyntaxError: The left-hand side of a for-of loop may not start with 'let'. (3:5)"
|
||||||
|
],
|
||||||
|
"program": {
|
||||||
|
"type": "Program",
|
||||||
|
"start":0,"end":66,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":22}},
|
||||||
|
"sourceType": "script",
|
||||||
|
"interpreter": null,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ForOfStatement",
|
||||||
|
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
|
||||||
|
"await": false,
|
||||||
|
"left": {
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"start":5,"end":12,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":12}},
|
||||||
|
"object": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start":5,"end":8,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":8},"identifierName":"let"},
|
||||||
|
"name": "let"
|
||||||
|
},
|
||||||
|
"computed": false,
|
||||||
|
"property": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start":9,"end":12,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":12},"identifierName":"foo"},
|
||||||
|
"name": "foo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"start":16,"end":18,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":18}},
|
||||||
|
"elements": []
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"type": "EmptyStatement",
|
||||||
|
"start":19,"end":20,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":20}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ForOfStatement",
|
||||||
|
"start":21,"end":43,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":22}},
|
||||||
|
"await": false,
|
||||||
|
"left": {
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"start":26,"end":35,"loc":{"start":{"line":2,"column":5},"end":{"line":2,"column":14}},
|
||||||
|
"object": {
|
||||||
|
"type": "CallExpression",
|
||||||
|
"start":26,"end":31,"loc":{"start":{"line":2,"column":5},"end":{"line":2,"column":10}},
|
||||||
|
"callee": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start":26,"end":29,"loc":{"start":{"line":2,"column":5},"end":{"line":2,"column":8},"identifierName":"let"},
|
||||||
|
"name": "let"
|
||||||
|
},
|
||||||
|
"arguments": []
|
||||||
|
},
|
||||||
|
"computed": false,
|
||||||
|
"property": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start":32,"end":35,"loc":{"start":{"line":2,"column":11},"end":{"line":2,"column":14},"identifierName":"bar"},
|
||||||
|
"name": "bar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"start":39,"end":41,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":20}},
|
||||||
|
"elements": []
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"type": "EmptyStatement",
|
||||||
|
"start":42,"end":43,"loc":{"start":{"line":2,"column":21},"end":{"line":2,"column":22}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ForOfStatement",
|
||||||
|
"start":44,"end":66,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":22}},
|
||||||
|
"await": false,
|
||||||
|
"left": {
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"start":49,"end":58,"loc":{"start":{"line":3,"column":5},"end":{"line":3,"column":14}},
|
||||||
|
"object": {
|
||||||
|
"type": "TaggedTemplateExpression",
|
||||||
|
"start":49,"end":54,"loc":{"start":{"line":3,"column":5},"end":{"line":3,"column":10}},
|
||||||
|
"tag": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start":49,"end":52,"loc":{"start":{"line":3,"column":5},"end":{"line":3,"column":8},"identifierName":"let"},
|
||||||
|
"name": "let"
|
||||||
|
},
|
||||||
|
"quasi": {
|
||||||
|
"type": "TemplateLiteral",
|
||||||
|
"start":52,"end":54,"loc":{"start":{"line":3,"column":8},"end":{"line":3,"column":10}},
|
||||||
|
"expressions": [],
|
||||||
|
"quasis": [
|
||||||
|
{
|
||||||
|
"type": "TemplateElement",
|
||||||
|
"start":53,"end":53,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":9}},
|
||||||
|
"value": {
|
||||||
|
"raw": "",
|
||||||
|
"cooked": ""
|
||||||
|
},
|
||||||
|
"tail": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"computed": false,
|
||||||
|
"property": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start":55,"end":58,"loc":{"start":{"line":3,"column":11},"end":{"line":3,"column":14},"identifierName":"bar"},
|
||||||
|
"name": "bar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"start":62,"end":64,"loc":{"start":{"line":3,"column":18},"end":{"line":3,"column":20}},
|
||||||
|
"elements": []
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"type": "EmptyStatement",
|
||||||
|
"start":65,"end":66,"loc":{"start":{"line":3,"column":21},"end":{"line":3,"column":22}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"directives": []
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user