Skip to content

Commit

Permalink
refactor: raise AwaitNotInAsyncContext when an AwaitExpression will b…
Browse files Browse the repository at this point in the history
…e parsed (#12716)

* refactor: raise AwaitNotInAsyncContext when an AwaitExpression will be parsed

* tweak logic

* early exit when errors are before thrown error position

* fix: always return true in assert.throws callback

See https://nodejs.org/api/assert.html#assert_assert_throws_fn_error_message

* update test fixtures

* fix flow error
  • Loading branch information
JLHwung committed Feb 1, 2021
1 parent 8cf0a75 commit 108564f
Show file tree
Hide file tree
Showing 15 changed files with 158 additions and 38 deletions.
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) {
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)"
}

0 comments on commit 108564f

Please sign in to comment.