Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse using[foo] as computed member expression #15225

Merged
merged 13 commits into from Nov 28, 2022
Merged
98 changes: 54 additions & 44 deletions packages/babel-parser/src/parser/statement.ts
Expand Up @@ -291,38 +291,12 @@ export default abstract class StatementParser extends ExpressionParser {
if (!this.isContextual(tt._let)) {
return false;
}
return this.hasFollowingIdentifier(true);
return this.hasFollowingBindingAtom();
}

/**
* Assuming we have seen a contextual `let` / `using`, check if it starts a variable declaration
so that it should be interpreted as a keyword.
*
* @param {number} allowDeclaration When `allowDeclaration` is false, it will return early and _skip_ checking
if the next token after `let` is `{` or a keyword relational operator
* @returns {boolean}
* @memberof StatementParser
*/
hasFollowingIdentifier(allowDeclaration: boolean): boolean {
const next = this.nextTokenStart();
const nextCh = this.codePointAtPos(next);
// For ambiguous cases, determine if a LexicalDeclaration (or only a
// Statement) is allowed here. If context is not empty then only a Statement
// is allowed. However, `let [` is an explicit negative lookahead for
// ExpressionStatement, so special-case it first.
// Also, `let \` is never valid as an expression so this must be a keyword.
if (
nextCh === charCodes.backslash ||
nextCh === charCodes.leftSquareBracket
) {
return true;
}
if (!allowDeclaration) return false;

if (nextCh === charCodes.leftCurlyBrace) return true;

if (isIdentifierStart(nextCh)) {
keywordRelationalOperator.lastIndex = next;
chStartsBindingIdentifier(ch: number, pos: number) {
if (isIdentifierStart(ch)) {
keywordRelationalOperator.lastIndex = pos;
if (keywordRelationalOperator.test(this.input)) {
// We have seen `in` or `instanceof` so far, now check if the identfier
// ends here
Expand All @@ -332,8 +306,33 @@ export default abstract class StatementParser extends ExpressionParser {
}
}
return true;
} else if (ch === charCodes.backslash) {
return true;
} else {
return false;
}
return false;
}

chStartsBindingPattern(ch: number) {
return (
ch === charCodes.leftSquareBracket || ch === charCodes.leftCurlyBrace
);
}

/**
* Assuming we have seen a contextual `let` and declaration is allowed, check if it
* starts a variable declaration so that it should be interpreted as a keyword.
*
* @returns {boolean}
* @memberof StatementParser
*/
hasFollowingBindingAtom(): boolean {
const next = this.nextTokenStart();
const nextCh = this.codePointAtPos(next);
return (
this.chStartsBindingPattern(nextCh) ||
this.chStartsBindingIdentifier(nextCh, next)
);
}

startsUsingForOf(): boolean {
Expand Down Expand Up @@ -464,14 +463,33 @@ export default abstract class StatementParser extends ExpressionParser {

case tt._using:
// using [no LineTerminator here] BindingList[+Using]
if (this.hasFollowingLineBreak()) {
if (
this.hasFollowingLineBreak() ||
this.state.containsEsc ||
!this.hasFollowingBindingAtom()
) {
break;
}
// fall through
this.expectPlugin("explicitResourceManagement");
if (!allowDeclaration) {
this.raise(Errors.UnexpectedLexicalDeclaration, {
at: this.state.startLoc,
});
}
if (!this.scope.inModule && this.scope.inTopLevel) {
this.raise(Errors.UnexpectedUsingDeclaration, {
at: this.state.startLoc,
});
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this report a double error when parsing label: using x = y as a script?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! In this case I lean to the UnexpectedUsingDeclaration because it's more specific than the other.

return this.parseVarStatement(
node as Undone<N.VariableDeclaration>,
"using",
);
case tt._let:
if (
this.state.containsEsc ||
!this.hasFollowingIdentifier(allowDeclaration)
!allowDeclaration ||
!this.hasFollowingBindingAtom()
) {
break;
}
Expand All @@ -486,14 +504,6 @@ export default abstract class StatementParser extends ExpressionParser {
// fall through
case tt._var: {
const kind = this.state.value;
if (kind === "using") {
this.expectPlugin("explicitResourceManagement");
if (!this.scope.inModule && this.scope.inTopLevel) {
this.raise(Errors.UnexpectedUsingDeclaration, {
at: this.state.startLoc,
});
}
}
return this.parseVarStatement(
node as Undone<N.VariableDeclaration>,
kind,
Expand Down Expand Up @@ -857,9 +867,9 @@ export default abstract class StatementParser extends ExpressionParser {
const startsWithUsing =
this.isContextual(tt._using) && !this.hasFollowingLineBreak();
const isLetOrUsing =
(startsWithLet && this.hasFollowingIdentifier(true)) ||
(startsWithLet && this.hasFollowingBindingAtom()) ||
(startsWithUsing &&
this.hasFollowingIdentifier(true) &&
this.hasFollowingBindingAtom() &&
this.startsUsingForOf());
if (this.match(tt._var) || this.match(tt._const) || isLetOrUsing) {
const initNode = this.startNode<N.VariableDeclaration>();
Expand Down
6 changes: 2 additions & 4 deletions packages/babel-parser/src/plugins/placeholders.ts
Expand Up @@ -152,13 +152,11 @@ export default (superClass: typeof Parser) =>
* parser/statement.js *
* ============================================================ */

hasFollowingIdentifier(allowDeclaration: boolean): boolean {
if (super.hasFollowingIdentifier(allowDeclaration)) {
chStartsBindingIdentifier(ch: number, pos: number): boolean {
if (super.chStartsBindingIdentifier(ch, pos)) {
return true;
}

if (!allowDeclaration) return false;

// Accept "let %%" as the start of "let %%placeholder%%", as though the
// placeholder were an identifier.
const nextToken = this.lookahead();
Expand Down
@@ -0,0 +1,8 @@
{
while (1) using a;
for (;;) using b;
do using c; while (1);
if (1) using d;
with (1) using e;
label: using f;
}
@@ -0,0 +1,199 @@
{
"type": "File",
"start":0,"end":125,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":8,"column":1,"index":125}},
"errors": [
"SyntaxError: Lexical declaration cannot appear in a single-statement context. (2:12)",
"SyntaxError: Lexical declaration cannot appear in a single-statement context. (3:11)",
"SyntaxError: Lexical declaration cannot appear in a single-statement context. (4:5)",
"SyntaxError: Lexical declaration cannot appear in a single-statement context. (5:9)",
"SyntaxError: Lexical declaration cannot appear in a single-statement context. (6:11)",
"SyntaxError: Lexical declaration cannot appear in a single-statement context. (7:9)"
],
"program": {
"type": "Program",
"start":0,"end":125,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":8,"column":1,"index":125}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "BlockStatement",
"start":0,"end":125,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":8,"column":1,"index":125}},
"body": [
{
"type": "WhileStatement",
"start":4,"end":22,"loc":{"start":{"line":2,"column":2,"index":4},"end":{"line":2,"column":20,"index":22}},
"test": {
"type": "NumericLiteral",
"start":11,"end":12,"loc":{"start":{"line":2,"column":9,"index":11},"end":{"line":2,"column":10,"index":12}},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
},
"body": {
"type": "VariableDeclaration",
"start":14,"end":22,"loc":{"start":{"line":2,"column":12,"index":14},"end":{"line":2,"column":20,"index":22}},
"declarations": [
{
"type": "VariableDeclarator",
"start":20,"end":21,"loc":{"start":{"line":2,"column":18,"index":20},"end":{"line":2,"column":19,"index":21}},
"id": {
"type": "Identifier",
"start":20,"end":21,"loc":{"start":{"line":2,"column":18,"index":20},"end":{"line":2,"column":19,"index":21},"identifierName":"a"},
"name": "a"
},
"init": null
}
],
"kind": "using"
}
},
{
"type": "ForStatement",
"start":25,"end":42,"loc":{"start":{"line":3,"column":2,"index":25},"end":{"line":3,"column":19,"index":42}},
"init": null,
"test": null,
"update": null,
"body": {
"type": "VariableDeclaration",
"start":34,"end":42,"loc":{"start":{"line":3,"column":11,"index":34},"end":{"line":3,"column":19,"index":42}},
"declarations": [
{
"type": "VariableDeclarator",
"start":40,"end":41,"loc":{"start":{"line":3,"column":17,"index":40},"end":{"line":3,"column":18,"index":41}},
"id": {
"type": "Identifier",
"start":40,"end":41,"loc":{"start":{"line":3,"column":17,"index":40},"end":{"line":3,"column":18,"index":41},"identifierName":"b"},
"name": "b"
},
"init": null
}
],
"kind": "using"
}
},
{
"type": "DoWhileStatement",
"start":45,"end":67,"loc":{"start":{"line":4,"column":2,"index":45},"end":{"line":4,"column":24,"index":67}},
"body": {
"type": "VariableDeclaration",
"start":48,"end":56,"loc":{"start":{"line":4,"column":5,"index":48},"end":{"line":4,"column":13,"index":56}},
"declarations": [
{
"type": "VariableDeclarator",
"start":54,"end":55,"loc":{"start":{"line":4,"column":11,"index":54},"end":{"line":4,"column":12,"index":55}},
"id": {
"type": "Identifier",
"start":54,"end":55,"loc":{"start":{"line":4,"column":11,"index":54},"end":{"line":4,"column":12,"index":55},"identifierName":"c"},
"name": "c"
},
"init": null
}
],
"kind": "using"
},
"test": {
"type": "NumericLiteral",
"start":64,"end":65,"loc":{"start":{"line":4,"column":21,"index":64},"end":{"line":4,"column":22,"index":65}},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
}
},
{
"type": "IfStatement",
"start":70,"end":85,"loc":{"start":{"line":5,"column":2,"index":70},"end":{"line":5,"column":17,"index":85}},
"test": {
"type": "NumericLiteral",
"start":74,"end":75,"loc":{"start":{"line":5,"column":6,"index":74},"end":{"line":5,"column":7,"index":75}},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
},
"consequent": {
"type": "VariableDeclaration",
"start":77,"end":85,"loc":{"start":{"line":5,"column":9,"index":77},"end":{"line":5,"column":17,"index":85}},
"declarations": [
{
"type": "VariableDeclarator",
"start":83,"end":84,"loc":{"start":{"line":5,"column":15,"index":83},"end":{"line":5,"column":16,"index":84}},
"id": {
"type": "Identifier",
"start":83,"end":84,"loc":{"start":{"line":5,"column":15,"index":83},"end":{"line":5,"column":16,"index":84},"identifierName":"d"},
"name": "d"
},
"init": null
}
],
"kind": "using"
},
"alternate": null
},
{
"type": "WithStatement",
"start":88,"end":105,"loc":{"start":{"line":6,"column":2,"index":88},"end":{"line":6,"column":19,"index":105}},
"object": {
"type": "NumericLiteral",
"start":94,"end":95,"loc":{"start":{"line":6,"column":8,"index":94},"end":{"line":6,"column":9,"index":95}},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
},
"body": {
"type": "VariableDeclaration",
"start":97,"end":105,"loc":{"start":{"line":6,"column":11,"index":97},"end":{"line":6,"column":19,"index":105}},
"declarations": [
{
"type": "VariableDeclarator",
"start":103,"end":104,"loc":{"start":{"line":6,"column":17,"index":103},"end":{"line":6,"column":18,"index":104}},
"id": {
"type": "Identifier",
"start":103,"end":104,"loc":{"start":{"line":6,"column":17,"index":103},"end":{"line":6,"column":18,"index":104},"identifierName":"e"},
"name": "e"
},
"init": null
}
],
"kind": "using"
}
},
{
"type": "LabeledStatement",
"start":108,"end":123,"loc":{"start":{"line":7,"column":2,"index":108},"end":{"line":7,"column":17,"index":123}},
"body": {
"type": "VariableDeclaration",
"start":115,"end":123,"loc":{"start":{"line":7,"column":9,"index":115},"end":{"line":7,"column":17,"index":123}},
"declarations": [
{
"type": "VariableDeclarator",
"start":121,"end":122,"loc":{"start":{"line":7,"column":15,"index":121},"end":{"line":7,"column":16,"index":122}},
"id": {
"type": "Identifier",
"start":121,"end":122,"loc":{"start":{"line":7,"column":15,"index":121},"end":{"line":7,"column":16,"index":122},"identifierName":"f"},
"name": "f"
},
"init": null
}
],
"kind": "using"
},
"label": {
"type": "Identifier",
"start":108,"end":113,"loc":{"start":{"line":7,"column":2,"index":108},"end":{"line":7,"column":7,"index":113},"identifierName":"label"},
"name": "label"
}
}
],
"directives": []
}
],
"directives": []
}
}