refactor: raise AwaitNotInAsyncContext when an AwaitExpression will be 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
This commit is contained in:
Huáng Jùnliàng 2021-02-01 10:46:43 -05:00 committed by GitHub
parent 8cf0a757d5
commit 108564fdad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 158 additions and 38 deletions

View File

@ -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) {

View File

@ -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 {
@ -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?: {

View File

@ -534,20 +534,20 @@ export default class ExpressionParser extends LValParser {
const expr = this.parseUpdate(node, update, refExpressionErrors);
if (isAwait) {
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 (startsExpr && !this.isAmbiguousAwait()) {
this.raiseOverwrite(
startPos,
this.hasPlugin("topLevelAwait")
? Errors.AwaitNotInAsyncContext
: Errors.AwaitNotInAsyncFunction,
);
}
return this.parseAwait(startPos, startLoc);
}
}
return expr;
}
@ -2348,19 +2348,9 @@ 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);
}
}
}
isAwaitAllowed(): boolean {
if (this.prodParam.hasAwait) return true;

View File

@ -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,
@ -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;
@ -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);
}

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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"
}

View File

@ -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": []
}
}

View File

@ -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"
}

View File

@ -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": []
}
}

View File

@ -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)"
}