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

refactor: raise AwaitNotInAsyncContext when an AwaitExpression will be parsed #12716

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -420,7 +420,13 @@ export default function (
delete task.options.throws;

assert.throws(runTask, function (err) {
return throwMsg === true || err.message.indexOf(throwMsg) >= 0;
assert.ok(
throwMsg === true || err.message.includes(throwMsg),
`
Expected Error: ${throwMsg}
Actual Error: ${err.message}`,
);
return true;
});
} else {
if (task.exec.code) {
Expand Down
37 changes: 37 additions & 0 deletions packages/babel-parser/src/parser/error.js
Expand Up @@ -16,6 +16,8 @@ type ErrorContext = {
code?: string,
};

export type ParsingError = SyntaxError & ErrorContext;

export { ErrorMessages as Errors } from "./error-message.js";

export default class ParserError extends CommentsParser {
Expand All @@ -39,6 +41,41 @@ export default class ParserError extends CommentsParser {
return this.raiseWithData(pos, undefined, errorTemplate, ...params);
}

/**
* Raise a parsing error on given postion pos. If errorRecovery is true,
* it will first search current errors and overwrite the error thrown on the exact
* position before with the new error message. If errorRecovery is false, it
* fallbacks to `raise`.
*
* @param {number} pos
* @param {string} errorTemplate
* @param {...any} params
* @returns {(Error | empty)}
* @memberof ParserError
*/
raiseOverwrite(
pos: number,
errorTemplate: string,
...params: any
): Error | empty {
const loc = this.getLocationForPosition(pos);
const message =
errorTemplate.replace(/%(\d+)/g, (_, i: number) => params[i]) +
` (${loc.line}:${loc.column})`;
if (this.options.errorRecovery) {
const errors = this.state.errors;
for (let i = errors.length - 1; i >= 0; i--) {
const error = errors[i];
if (error.pos === pos) {
nicolo-ribaudo marked this conversation as resolved.
Show resolved Hide resolved
return Object.assign(error, { message });
} else if (error.pos < pos) {
break;
}
}
}
return this._raise({ loc, pos }, message);
}

raiseWithData(
pos: number,
data?: {
Expand Down
26 changes: 8 additions & 18 deletions packages/babel-parser/src/parser/expression.js
Expand Up @@ -534,19 +534,19 @@ export default class ExpressionParser extends LValParser {

const expr = this.parseUpdate(node, update, refExpressionErrors);

const startsExpr = this.hasPlugin("v8intrinsic")
? this.state.type.startsExpr
: this.state.type.startsExpr && !this.match(tt.modulo);
if (isAwait && startsExpr && !this.isAmbiguousAwait()) {
if (!this.state.invalidAwaitErrors.has(startPos)) {
this.raise(
if (isAwait) {
const startsExpr = this.hasPlugin("v8intrinsic")
? this.state.type.startsExpr
: this.state.type.startsExpr && !this.match(tt.modulo);
if (startsExpr && !this.isAmbiguousAwait()) {
this.raiseOverwrite(
startPos,
this.hasPlugin("topLevelAwait")
? Errors.AwaitNotInAsyncContext
: Errors.AwaitNotInAsyncFunction,
);
return this.parseAwait(startPos, startLoc);
}
return this.parseAwait(startPos, startLoc);
}

return expr;
Expand Down Expand Up @@ -2348,17 +2348,7 @@ export default class ExpressionParser extends LValParser {
: isStrictReservedWord;

if (reservedTest(word, this.inModule)) {
if (!this.prodParam.hasAwait && word === "await") {
this.raise(
startLoc,
this.hasPlugin("topLevelAwait")
? Errors.AwaitNotInAsyncContext
: Errors.AwaitNotInAsyncFunction,
);
this.state.invalidAwaitErrors.add(startLoc);
} else {
this.raise(startLoc, Errors.UnexpectedReservedWord, word);
}
this.raise(startLoc, Errors.UnexpectedReservedWord, word);
}
}

Expand Down
6 changes: 2 additions & 4 deletions packages/babel-parser/src/tokenizer/state.js
Expand Up @@ -6,6 +6,7 @@ import { Position } from "../util/location";

import { types as ct, type TokContext } from "./context";
import { types as tt, type TokenType } from "./types";
import type { ParsingError } from "../parser/error";

type TopicContextState = {
// When a topic binding has been currently established,
Expand Down Expand Up @@ -37,7 +38,7 @@ export default class State {
this.startLoc = this.endLoc = this.curPosition();
}

errors: SyntaxError[] = [];
errors: ParsingError[] = [];

// Used to signify the start of a potential arrow function
potentialArrowAt: number = -1;
Expand Down Expand Up @@ -155,9 +156,6 @@ export default class State {
// Tokens length in token store
tokensLength: number = 0;

// Positions of invalid await errors
invalidAwaitErrors: Set<number> = new Set();

curPosition(): Position {
return new Position(this.curLine, this.pos - this.lineStart);
}
Expand Down
Expand Up @@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":14,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":14}},
"errors": [
"SyntaxError: 'await' is only allowed within async functions (1:0)"
"SyntaxError: Unexpected reserved word 'await' (1:0)"
],
"program": {
"type": "Program",
Expand Down
Expand Up @@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"errors": [
"SyntaxError: 'await' is only allowed within async functions (1:6)"
"SyntaxError: Unexpected reserved word 'await' (1:6)"
],
"program": {
"type": "Program",
Expand Down
Expand Up @@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":24,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":24}},
"errors": [
"SyntaxError: 'await' is only allowed within async functions (1:8)"
"SyntaxError: Unexpected reserved word 'await' (1:8)"
],
"program": {
"type": "Program",
Expand Down
Expand Up @@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"errors": [
"SyntaxError: 'await' is only allowed within async functions (1:15)"
"SyntaxError: Unexpected reserved word 'await' (1:15)"
],
"program": {
"type": "Program",
Expand Down
Expand Up @@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}},
"errors": [
"SyntaxError: 'await' is only allowed within async functions (1:9)"
"SyntaxError: Unexpected reserved word 'await' (1:9)"
],
"program": {
"type": "Program",
Expand Down
Expand Up @@ -2,7 +2,7 @@
"type": "File",
"start":0,"end":14,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":14}},
"errors": [
"SyntaxError: 'await' is only allowed within async functions (1:6)"
"SyntaxError: Unexpected reserved word 'await' (1:6)"
],
"program": {
"type": "Program",
Expand Down
Expand Up @@ -2,7 +2,5 @@
"plugins": [
"topLevelAwait"
],
"sourceType": "module",
"errorRecovery": false,
"throws": "'await' is only allowed within async functions and at the top levels of modules (1:6)"
}
"sourceType": "module"
}
@@ -0,0 +1,41 @@
{
"type": "File",
"start":0,"end":14,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":14}},
"errors": [
"SyntaxError: 'await' is only allowed within async functions and at the top levels of modules (1:6)"
],
"program": {
"type": "Program",
"start":0,"end":14,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":14}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":14,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":14}},
"expression": {
"type": "ArrowFunctionExpression",
"start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}},
"id": null,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "AwaitExpression",
"start":6,"end":13,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":13}},
"argument": {
"type": "NumericLiteral",
"start":12,"end":13,"loc":{"start":{"line":1,"column":12},"end":{"line":1,"column":13}},
"extra": {
"rawValue": 0,
"raw": "0"
},
"value": 0
}
}
}
}
],
"directives": []
}
}
Expand Up @@ -2,7 +2,5 @@
"plugins": [
"topLevelAwait"
],
"sourceType": "module",
"errorRecovery": false,
"throws": "'await' is only allowed within async functions and at the top levels of modules (2:2)"
}
"sourceType": "module"
}
@@ -0,0 +1,52 @@
{
"type": "File",
"start":0,"end":28,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"errors": [
"SyntaxError: 'await' is only allowed within async functions and at the top levels of modules (2:2)"
],
"program": {
"type": "Program",
"start":0,"end":28,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "FunctionDeclaration",
"start":0,"end":28,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}},
"id": {
"type": "Identifier",
"start":9,"end":11,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":11},"identifierName":"fn"},
"name": "fn"
},
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start":14,"end":28,"loc":{"start":{"line":1,"column":14},"end":{"line":3,"column":1}},
"body": [
{
"type": "ExpressionStatement",
"start":18,"end":26,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":10}},
"expression": {
"type": "AwaitExpression",
"start":18,"end":25,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":9}},
"argument": {
"type": "NumericLiteral",
"start":24,"end":25,"loc":{"start":{"line":2,"column":8},"end":{"line":2,"column":9}},
"extra": {
"rawValue": 0,
"raw": "0"
},
"value": 0
}
}
}
],
"directives": []
}
}
],
"directives": []
}
}
Expand Up @@ -6,5 +6,5 @@
"supportsTopLevelAwait": false
},
"presets": ["env"],
"throws": "'await' is only allowed within async functions (1:0)"
"throws": "Unexpected reserved word 'await' (1:0)"
}