From 57d6e985af72149c3d99f4c4daf6232d7875b6dd Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 10 Jan 2022 15:56:11 -0800 Subject: [PATCH 1/6] Have raise() take a Position instead of an integer index so that we can correctly and efficiently track line and column offsets. Additionally: 1. Make Errors.ElementAfterRest a recoverable error. 2. Make expectContextual throw a nicer error telling you what it expected to find. Reviewed by @tolmasky. --- .../src/rules/dry-error-messages.ts | 2 +- .../test/rules/dry-error-messages.js | 144 +++--- packages/babel-parser/src/parser/error.js | 43 +- .../babel-parser/src/parser/expression.js | 378 +++++++------- packages/babel-parser/src/parser/lval.js | 90 ++-- packages/babel-parser/src/parser/node.js | 25 +- packages/babel-parser/src/parser/statement.js | 217 ++++---- packages/babel-parser/src/parser/util.js | 135 ++--- packages/babel-parser/src/plugins/estree.js | 12 +- .../babel-parser/src/plugins/flow/index.js | 463 +++++++++--------- .../babel-parser/src/plugins/flow/scope.js | 5 +- .../babel-parser/src/plugins/jsx/index.js | 72 +-- .../babel-parser/src/plugins/placeholders.js | 12 +- .../src/plugins/typescript/index.js | 248 +++++----- .../src/plugins/typescript/scope.js | 5 +- .../babel-parser/src/plugins/v8intrinsic.js | 4 +- packages/babel-parser/src/tokenizer/index.js | 187 ++++--- packages/babel-parser/src/tokenizer/state.js | 9 +- packages/babel-parser/src/util/class-scope.js | 21 +- .../babel-parser/src/util/expression-scope.js | 43 +- packages/babel-parser/src/util/location.js | 31 +- packages/babel-parser/src/util/scope.js | 16 +- .../test/unit/util/location.skip-bundled.js | 38 -- 23 files changed, 1150 insertions(+), 1050 deletions(-) delete mode 100644 packages/babel-parser/test/unit/util/location.skip-bundled.js diff --git a/eslint/babel-eslint-plugin-development-internal/src/rules/dry-error-messages.ts b/eslint/babel-eslint-plugin-development-internal/src/rules/dry-error-messages.ts index d6e2226e2e76..f9add86a6fe7 100644 --- a/eslint/babel-eslint-plugin-development-internal/src/rules/dry-error-messages.ts +++ b/eslint/babel-eslint-plugin-development-internal/src/rules/dry-error-messages.ts @@ -146,7 +146,7 @@ export default { "CallExpression[callee.type='MemberExpression'][callee.object.type='ThisExpression'][callee.property.name='raise'][arguments.length>=2]"( node, ) { - const [, errorMsgNode] = node.arguments; + const [errorMsgNode] = node.arguments; const nodesToCheck = findIdNodes(errorMsgNode); if ( diff --git a/eslint/babel-eslint-plugin-development-internal/test/rules/dry-error-messages.js b/eslint/babel-eslint-plugin-development-internal/test/rules/dry-error-messages.js index ebf21288950e..4be879e2f6db 100644 --- a/eslint/babel-eslint-plugin-development-internal/test/rules/dry-error-messages.js +++ b/eslint/babel-eslint-plugin-development-internal/test/rules/dry-error-messages.js @@ -67,164 +67,164 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { Errors } from 'errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { Errors } from 'errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], }, { filename: FILENAME, - code: "import { Errors } from './errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { Errors } from './errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_SAME_DIR }], }, { filename: FILENAME, - code: "import { Errors } from '../errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { Errors } from '../errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_PARENT_DIR }], }, { filename: FILENAME, - code: "import { NotErrors, Errors } from 'errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { NotErrors, Errors } from 'errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], }, { filename: FILENAME, - code: "import { NotErrors, Errors } from './errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { NotErrors, Errors } from './errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_SAME_DIR }], }, { filename: FILENAME, - code: "import { NotErrors, Errors } from '../errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { NotErrors, Errors } from '../errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_PARENT_DIR }], }, { filename: FILENAME, - code: "import { Errors } from 'errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { Errors } from 'errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: ERRORS_MODULE }], }, { filename: FILENAME, - code: "import { Errors } from './errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { Errors } from './errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_SAME_DIR }], }, { filename: FILENAME, - code: "import { Errors } from '../errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { Errors } from '../errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_PARENT_DIR }], }, { filename: FILENAME, - code: "import { NotErrors, Errors } from 'errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { NotErrors, Errors } from 'errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: ERRORS_MODULE }], }, { filename: FILENAME, - code: "import { NotErrors, Errors } from './errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { NotErrors, Errors } from './errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_SAME_DIR }], }, { filename: FILENAME, - code: "import { NotErrors, Errors } from '../errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { NotErrors, Errors } from '../errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_PARENT_DIR }], }, { filename: FILENAME, - code: "import Errors from 'errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors from 'errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], }, { filename: FILENAME, - code: "import Errors from './errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors from './errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_SAME_DIR }], }, { filename: FILENAME, - code: "import Errors from '../errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors from '../errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_PARENT_DIR }], }, { filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], }, { filename: FILENAME, - code: "import Errors, { NotErrors } from './errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors, { NotErrors } from './errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_SAME_DIR }], }, { filename: FILENAME, - code: "import Errors, { NotErrors } from '../errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors, { NotErrors } from '../errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_PARENT_DIR }], }, { filename: FILENAME, - code: "import NotErrors, { Errors } from 'errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import NotErrors, { Errors } from 'errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], }, { filename: FILENAME, - code: "import NotErrors, { Errors } from './errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import NotErrors, { Errors } from './errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_SAME_DIR }], }, { filename: FILENAME, - code: "import NotErrors, { Errors } from '../errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import NotErrors, { Errors } from '../errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_PARENT_DIR }], }, { filename: FILENAME, - code: "import Errors from 'errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors from 'errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: ERRORS_MODULE }], }, { filename: FILENAME, - code: "import Errors from './errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors from './errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_SAME_DIR }], }, { filename: FILENAME, - code: "import Errors from '../errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors from '../errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_PARENT_DIR }], }, { filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors, { NotErrors } from 'errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: ERRORS_MODULE }], }, { filename: FILENAME, - code: "import Errors, { NotErrors } from './errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors, { NotErrors } from './errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_SAME_DIR }], }, { filename: FILENAME, - code: "import Errors, { NotErrors } from '../errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors, { NotErrors } from '../errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_PARENT_DIR }], }, { filename: FILENAME, - code: "import NotErrors, { Errors } from 'errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import NotErrors, { Errors } from 'errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: ERRORS_MODULE }], }, { filename: FILENAME, - code: "import NotErrors, { Errors } from './errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import NotErrors, { Errors } from './errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_SAME_DIR }], }, { filename: FILENAME, - code: "import NotErrors, { Errors } from '../errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import NotErrors, { Errors } from '../errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_PARENT_DIR }], }, // Do not warn when file linted is error module. { filename: FILENAME, - code: "this.raise(loc, 'Oh no!');", + code: "this.raise( 'Oh no!', loc);", options: [{ errorModule: FILENAME }], }, { filename: MODULE_SAME_DIR, - code: "this.raise(loc, 'Oh no!');", + code: "this.raise('Oh no!', loc);", options: [{ errorModule: MODULE_SAME_DIR }], }, @@ -238,14 +238,14 @@ ruleTester.run("dry-error-messages", rule.default, { // Support ternary as second argument { filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(loc, a ? Errors.someErrorMessage : Errors.someOtherErrorMessage);", + code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(a ? Errors.someErrorMessage : Errors.someOtherErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], }, ], invalid: [ { filename: FILENAME, - code: "this.raise(loc, new Error('Uh oh'));", + code: "this.raise(new Error('Uh oh'), loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -256,7 +256,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "throw this.raise(loc, new Error('Uh oh'));", + code: "throw this.raise(new Error('Uh oh'), loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -267,7 +267,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "this.raise(loc, Errors.someErrorMessage);", + code: "this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -278,7 +278,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "const Errors = { someErrorMessage: 'Uh oh!' }; this.raise(loc, Errors.someErrorMessage);", + code: "const Errors = { someErrorMessage: 'Uh oh!' }; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -289,7 +289,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { Errors } from 'errorsModule'; this.raise(loc, 'Uh oh!');", + code: "import { Errors } from 'errorsModule'; this.raise('Uh oh!', loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -300,7 +300,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { Errors } from 'errorsModule'; const msg = 'Uh oh!'; this.raise(loc, msg);", + code: "import { Errors } from 'errorsModule'; const msg = 'Uh oh!'; this.raise(msg, loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -311,7 +311,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { Errors } from 'not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { Errors } from 'not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -322,7 +322,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { Errors } from './not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { Errors } from './not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_SAME_DIR }], errors: [ { @@ -333,7 +333,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { Errors } from '../not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { Errors } from '../not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_PARENT_DIR }], errors: [ { @@ -344,7 +344,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { NotErrors, Errors } from 'not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { NotErrors, Errors } from 'not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -355,7 +355,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { NotErrors, Errors } from './not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { NotErrors, Errors } from './not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_SAME_DIR }], errors: [ { @@ -366,7 +366,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { NotErrors, Errors } from '../not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import { NotErrors, Errors } from '../not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_PARENT_DIR }], errors: [ { @@ -377,7 +377,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { Errors } from 'not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { Errors } from 'not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -388,7 +388,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { Errors } from './not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { Errors } from './not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_SAME_DIR }], errors: [ { @@ -399,7 +399,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { Errors } from '../not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { Errors } from '../not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_PARENT_DIR }], errors: [ { @@ -410,7 +410,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { NotErrors, Errors } from 'not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { NotErrors, Errors } from 'not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -421,7 +421,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { NotErrors, Errors } from './not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { NotErrors, Errors } from './not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_SAME_DIR }], errors: [ { @@ -432,7 +432,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import { NotErrors, Errors } from '../not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import { NotErrors, Errors } from '../not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_PARENT_DIR }], errors: [ { @@ -443,7 +443,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors from 'not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors from 'not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -454,7 +454,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors from './not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors from './not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_SAME_DIR }], errors: [ { @@ -465,7 +465,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors from '../not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors from '../not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_PARENT_DIR }], errors: [ { @@ -476,7 +476,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors, { NotErrors } from 'not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors, { NotErrors } from 'not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -487,7 +487,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors, { NotErrors } from './not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors, { NotErrors } from './not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_SAME_DIR }], errors: [ { @@ -498,7 +498,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors, { NotErrors } from '../not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import Errors, { NotErrors } from '../not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_PARENT_DIR }], errors: [ { @@ -509,7 +509,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import NotErrors, { Errors } from 'not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import NotErrors, { Errors } from 'not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -520,7 +520,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import NotErrors, { Errors } from './not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import NotErrors, { Errors } from './not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_SAME_DIR }], errors: [ { @@ -531,7 +531,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import NotErrors, { Errors } from '../not-errorsModule'; this.raise(loc, Errors.someErrorMessage);", + code: "import NotErrors, { Errors } from '../not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", options: [{ errorModule: MODULE_PARENT_DIR }], errors: [ { @@ -542,7 +542,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors from 'not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors from 'not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -553,7 +553,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors from './not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors from './not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_SAME_DIR }], errors: [ { @@ -564,7 +564,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors from '../not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors from '../not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_PARENT_DIR }], errors: [ { @@ -575,7 +575,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors, { NotErrors } from 'not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors, { NotErrors } from 'not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -586,7 +586,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors, { NotErrors } from './not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors, { NotErrors } from './not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_SAME_DIR }], errors: [ { @@ -597,7 +597,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors, { NotErrors } from '../not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import Errors, { NotErrors } from '../not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_PARENT_DIR }], errors: [ { @@ -608,7 +608,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import NotErrors, { Errors } from 'not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import NotErrors, { Errors } from 'not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -619,7 +619,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import NotErrors, { Errors } from './not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import NotErrors, { Errors } from './not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_SAME_DIR }], errors: [ { @@ -630,7 +630,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import NotErrors, { Errors } from '../not-errorsModule'; function fn() { this.raise(loc, Errors.someErrorMessage); }", + code: "import NotErrors, { Errors } from '../not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", options: [{ errorModule: MODULE_PARENT_DIR }], errors: [ { @@ -643,7 +643,7 @@ ruleTester.run("dry-error-messages", rule.default, { // Should error if either part of a ternary isn't from error module { filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(loc, a ? Errors.someErrorMessage : 'hello');", + code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(a ? Errors.someErrorMessage : 'hello', loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -654,7 +654,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(loc, a ? 'hello' : Errors.someErrorMessage);", + code: "import Errors, { NotErrors } from 'errorsModule'; this.raise( a ? 'hello' : Errors.someErrorMessage, loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { @@ -665,7 +665,7 @@ ruleTester.run("dry-error-messages", rule.default, { }, { filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(loc, a ? 'hello' : 'world');", + code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(a ? 'hello' : 'world', loc);", options: [{ errorModule: ERRORS_MODULE }], errors: [ { diff --git a/packages/babel-parser/src/parser/error.js b/packages/babel-parser/src/parser/error.js index 4dc30ba98925..db9e3de14d19 100644 --- a/packages/babel-parser/src/parser/error.js +++ b/packages/babel-parser/src/parser/error.js @@ -1,8 +1,9 @@ // @flow /* eslint sort-keys: "error" */ -import { getLineInfo, type Position } from "../util/location"; +import { type Position } from "../util/location"; import CommentsParser from "./comments"; import { type ErrorCode, ErrorCodes } from "./error-codes"; +import { type Node } from "../types"; // This function is used to raise exceptions on parse errors. It // takes an offset integer (into the current `input`) to indicate @@ -28,7 +29,14 @@ export type ErrorTemplates = { [key: string]: ErrorTemplate, }; -type SyntaxPlugin = "flow" | "typescript" | "jsx" | typeof undefined; +type Origin = {| node: Node |} | {| at: Position |}; + +type SyntaxPlugin = + | "flow" + | "typescript" + | "jsx" + | "placeholders" + | typeof undefined; function keepReasonCodeCompat(reasonCode: string, syntaxPlugin: SyntaxPlugin) { if (!process.env.BABEL_8_BREAKING) { @@ -64,7 +72,8 @@ export { SourceTypeModuleErrorMessages as SourceTypeModuleErrors, } from "./error-message"; -export type raiseFunction = (number, ErrorTemplate, ...any) => void; +export type raiseFunction = (ErrorTemplate, Origin, ...any) => void; +export type ErrorData = {| message: ErrorTemplate, loc: Position |}; export default class ParserError extends CommentsParser { // Forward-declaration: defined in tokenizer/index.js @@ -72,23 +81,17 @@ export default class ParserError extends CommentsParser { +isLookahead: boolean; */ - getLocationForPosition(pos: number): Position { - let loc; - if (pos === this.state.start) loc = this.state.startLoc; - else if (pos === this.state.lastTokStart) loc = this.state.lastTokStartLoc; - else if (pos === this.state.end) loc = this.state.endLoc; - else if (pos === this.state.lastTokEnd) loc = this.state.lastTokEndLoc; - else loc = getLineInfo(this.input, pos); - - return loc; - } - raise( - pos: number, { code, reasonCode, template }: ErrorTemplate, + origin: Origin, ...params: any ): Error | empty { - return this.raiseWithData(pos, { code, reasonCode }, template, ...params); + return this.raiseWithData( + origin.node ? origin.node.loc.start : origin.at, + { code, reasonCode }, + template, + ...params, + ); } /** @@ -104,11 +107,11 @@ export default class ParserError extends CommentsParser { * @memberof ParserError */ raiseOverwrite( - pos: number, + loc: Position, { code, template }: ErrorTemplate, ...params: any ): Error | empty { - const loc = this.getLocationForPosition(pos); + const pos = loc.index; const message = template.replace(/%(\d+)/g, (_, i: number) => params[i]) + ` (${loc.line}:${loc.column})`; @@ -127,7 +130,7 @@ export default class ParserError extends CommentsParser { } raiseWithData( - pos: number, + loc: Position, data?: { missingPlugin?: Array, code?: string, @@ -135,7 +138,7 @@ export default class ParserError extends CommentsParser { errorTemplate: string, ...params: any ): Error | empty { - const loc = this.getLocationForPosition(pos); + const pos = loc.index; const message = errorTemplate.replace(/%(\d+)/g, (_, i: number) => params[i]) + ` (${loc.line}:${loc.column})`; diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 544388abc781..9759b69544cd 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -145,18 +145,18 @@ export default class ExpressionParser extends LValParser { if (name === "__proto__") { if (isRecord) { - this.raise(key.start, Errors.RecordNoProto); + this.raise(Errors.RecordNoProto, { node: key }); return; } if (protoRef.used) { if (refExpressionErrors) { // Store the first redefinition's position, otherwise ignore because // we are parsing ambiguous pattern - if (refExpressionErrors.doubleProto === -1) { - refExpressionErrors.doubleProto = key.start; + if (refExpressionErrors.doubleProtoLoc === null) { + refExpressionErrors.doubleProtoLoc = key.loc.start; } } else { - this.raise(key.start, Errors.DuplicateProto); + this.raise(Errors.DuplicateProto, { node: key }); } } @@ -263,8 +263,8 @@ export default class ExpressionParser extends LValParser { refExpressionErrors: ExpressionErrors, resultError?: ParsingError, ) { - refExpressionErrors.optionalParameters = - resultError?.pos ?? this.state.start; + refExpressionErrors.optionalParametersLoc = + resultError?.loc ?? this.state.startLoc; } // Parse an assignment expression. This includes applications of @@ -311,11 +311,17 @@ export default class ExpressionParser extends LValParser { if (this.match(tt.eq)) { node.left = this.toAssignable(left, /* isLHS */ true); - if (refExpressionErrors.doubleProto >= startPos) { - refExpressionErrors.doubleProto = -1; // reset because double __proto__ is valid in assignment expression + if ( + refExpressionErrors.doubleProtoLoc != null && + refExpressionErrors.doubleProtoLoc.index >= startPos + ) { + refExpressionErrors.doubleProtoLoc = null; // reset because double __proto__ is valid in assignment expression } - if (refExpressionErrors.shorthandAssign >= startPos) { - refExpressionErrors.shorthandAssign = -1; // reset because shorthand default was used correctly + if ( + refExpressionErrors.shorthandAssignLoc != null && + refExpressionErrors.shorthandAssignLoc.index >= startPos + ) { + refExpressionErrors.shorthandAssignLoc = null; // reset because shorthand default was used correctly } } else { node.left = left; @@ -408,17 +414,16 @@ export default class ExpressionParser extends LValParser { // [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await] const value = this.getPrivateNameSV(left); - const { start } = left; if ( minPrec >= tokenOperatorPrecedence(tt._in) || !this.prodParam.hasIn || !this.match(tt._in) ) { - this.raise(start, Errors.PrivateInExpectedIn, value); + this.raise(Errors.PrivateInExpectedIn, { node: left }, value); } - this.classScope.usePrivateName(value, start); + this.classScope.usePrivateName(value, left.loc.start); } const op = this.state.type; @@ -430,7 +435,7 @@ export default class ExpressionParser extends LValParser { if (this.state.inFSharpPipelineDirectBody) { return left; } - this.checkPipelineAtInfixOperator(left, leftStartPos); + this.checkPipelineAtInfixOperator(left, leftStartLoc); } const node = this.startNodeAt(leftStartPos, leftStartLoc); node.left = left; @@ -452,10 +457,9 @@ export default class ExpressionParser extends LValParser { this.hasPlugin(["pipelineOperator", { proposal: "minimal" }]) ) { if (this.state.type === tt._await && this.prodParam.hasAwait) { - throw this.raise( - this.state.start, - Errors.UnexpectedAwaitAfterPipelineBody, - ); + throw this.raise(Errors.UnexpectedAwaitAfterPipelineBody, { + at: this.state.startLoc, + }); } } @@ -474,7 +478,9 @@ export default class ExpressionParser extends LValParser { (coalesce && (nextOp === tt.logicalOR || nextOp === tt.logicalAND)) || (logical && nextOp === tt.nullishCoalescing) ) { - throw this.raise(this.state.start, Errors.MixingCoalesceWithLogical); + throw this.raise(Errors.MixingCoalesceWithLogical, { + at: this.state.startLoc, + }); } return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec); @@ -501,8 +507,8 @@ export default class ExpressionParser extends LValParser { return this.withTopicBindingContext(() => { if (this.prodParam.hasYield && this.isContextual(tt._yield)) { throw this.raise( - this.state.start, Errors.PipeBodyIsTighter, + { at: this.state.startLoc }, this.state.value, ); } @@ -541,21 +547,20 @@ export default class ExpressionParser extends LValParser { } parseHackPipeBody(): N.Expression { - const { start } = this.state; - + const { startLoc } = this.state; const body = this.parseMaybeAssign(); // TODO: Check how to handle type casts in Flow and TS once they are supported if (invalidHackPipeBodies.has(body.type) && !body.extra?.parenthesized) { this.raise( - start, Errors.PipeUnparenthesizedBody, + { at: startLoc }, invalidHackPipeBodies.get(body.type), ); } if (!this.topicReferenceWasUsedInCurrentContext()) { // A Hack pipe body must use the topic reference at least once. - this.raise(start, Errors.PipeTopicUnused); + this.raise(Errors.PipeTopicUnused, { at: startLoc }); } return body; @@ -563,10 +568,9 @@ export default class ExpressionParser extends LValParser { checkExponentialAfterUnary(node: N.AwaitExpression | N.UnaryExpression) { if (this.match(tt.exponent)) { - this.raise( - node.argument.start, - Errors.UnexpectedTokenUnaryExponentiation, - ); + this.raise(Errors.UnexpectedTokenUnaryExponentiation, { + node: node.argument, + }); } } @@ -606,9 +610,9 @@ export default class ExpressionParser extends LValParser { const arg = node.argument; if (arg.type === "Identifier") { - this.raise(node.start, Errors.StrictDelete); + this.raise(Errors.StrictDelete, { node }); } else if (this.hasPropertyAsPrivateName(arg)) { - this.raise(node.start, Errors.DeletePrivateField); + this.raise(Errors.DeletePrivateField, { node }); } } @@ -626,7 +630,7 @@ export default class ExpressionParser extends LValParser { ? tokenCanStartExpression(type) : tokenCanStartExpression(type) && !this.match(tt.modulo); if (startsExpr && !this.isAmbiguousAwait()) { - this.raiseOverwrite(startPos, Errors.AwaitNotInAsyncContext); + this.raiseOverwrite(startLoc, Errors.AwaitNotInAsyncContext); return this.parseAwait(startPos, startLoc); } } @@ -777,9 +781,9 @@ export default class ExpressionParser extends LValParser { this.expect(tt.bracketR); } else if (this.match(tt.privateName)) { if (base.type === "Super") { - this.raise(startPos, Errors.SuperPrivateField); + this.raise(Errors.SuperPrivateField, { at: startLoc }); } - this.classScope.usePrivateName(this.state.value, this.state.start); + this.classScope.usePrivateName(this.state.value, this.state.startLoc); node.property = this.parsePrivateName(); } else { node.property = this.parseIdentifier(true); @@ -899,7 +903,7 @@ export default class ExpressionParser extends LValParser { node.tag = base; node.quasi = this.parseTemplate(true); if (state.optionalChainMember) { - this.raise(startPos, Errors.OptionalChainingNoTemplate); + this.raise(Errors.OptionalChainingNoTemplate, { at: startLoc }); } return this.finishNode(node, "TaggedTemplateExpression"); } @@ -908,7 +912,7 @@ export default class ExpressionParser extends LValParser { return ( base.type === "Identifier" && base.name === "async" && - this.state.lastTokEnd === base.end && + this.state.lastTokEndLoc.index === base.end && !this.canInsertSemicolon() && // check there are no escape sequences, such as \u{61}sync base.end - base.start === 5 && @@ -932,8 +936,8 @@ export default class ExpressionParser extends LValParser { } if (node.arguments.length === 0 || node.arguments.length > 2) { this.raise( - node.start, Errors.ImportCallArity, + { node }, this.hasPlugin("importAssertions") || this.hasPlugin("moduleAttributes") ? "one or two arguments" @@ -942,7 +946,7 @@ export default class ExpressionParser extends LValParser { } else { for (const arg of node.arguments) { if (arg.type === "SpreadElement") { - this.raise(arg.start, Errors.ImportCallSpreadArgument); + this.raise(Errors.ImportCallSpreadArgument, { node: arg }); } } } @@ -976,17 +980,12 @@ export default class ExpressionParser extends LValParser { !this.hasPlugin("importAssertions") && !this.hasPlugin("moduleAttributes") ) { - this.raise( - this.state.lastTokStart, - Errors.ImportCallArgumentTrailingComma, - ); + this.raise(Errors.ImportCallArgumentTrailingComma, { + at: this.state.lastTokStartLoc, + }); } if (nodeForExtra) { - this.addExtra( - nodeForExtra, - "trailingComma", - this.state.lastTokStart, - ); + this.addTrailingCommaExtraToNode(nodeForExtra); } this.next(); break; @@ -1017,7 +1016,7 @@ export default class ExpressionParser extends LValParser { node, call.arguments, true, - call.extra?.trailingComma, + call.extra?.trailingCommaLoc, ); // mark inner comments of `async()` as inner comments of `async () =>` if (call.innerComments) { @@ -1067,7 +1066,9 @@ export default class ExpressionParser extends LValParser { } if (!this.match(tt.parenL)) { - this.raise(this.state.lastTokStart, Errors.UnsupportedImport); + this.raise(Errors.UnsupportedImport, { + at: this.state.lastTokStartLoc, + }); } return this.finishNode(node, "Import"); case tt._this: @@ -1170,7 +1171,7 @@ export default class ExpressionParser extends LValParser { if (callee.type === "MemberExpression") { return this.finishNode(node, "BindExpression"); } else { - throw this.raise(callee.start, Errors.UnsupportedBind); + throw this.raise(Errors.UnsupportedBind, { node: callee }); } } @@ -1182,8 +1183,8 @@ export default class ExpressionParser extends LValParser { // just throwing "Unexpected token" (which is the default // behavior of this big switch statement). this.raise( - this.state.start, Errors.PrivateInExpectedIn, + { at: this.state.startLoc }, this.state.value, ); return this.parsePrivateName(); @@ -1321,7 +1322,9 @@ export default class ExpressionParser extends LValParser { // so that the following token starts at the equals sign after that topic token. this.state.pos--; this.state.end--; - this.state.endLoc.column--; + // This is safe to do since the preceding character was either ^ or %, and + // thus not a newline. + this.state.endLoc = createPositionWithColumnOffset(this.state.endLoc, -1); // Now actually consume the topic token. return this.parseTopicReference(pipeProposal); } else { @@ -1337,7 +1340,7 @@ export default class ExpressionParser extends LValParser { // See . parseTopicReference(pipeProposal: string): N.Expression { const node = this.startNode(); - const start = this.state.start; + const startLoc = this.state.startLoc; const tokenType = this.state.type; // Consume the current token. @@ -1346,7 +1349,7 @@ export default class ExpressionParser extends LValParser { // If the pipe-operator plugin’s configuration matches the current token’s type, // then this will return `node`, will have been finished as a topic reference. // Otherwise, this will throw a `PipeTopicUnconfiguredToken` error. - return this.finishTopicReference(node, start, pipeProposal, tokenType); + return this.finishTopicReference(node, startLoc, pipeProposal, tokenType); } // This helper method attempts to finish the given `node` @@ -1364,35 +1367,36 @@ export default class ExpressionParser extends LValParser { // then this method will throw a `PipeTopicUnconfiguredToken` error. finishTopicReference( node: N.Node, - start: number, + startLoc: Position, pipeProposal: string, tokenType: TokenType, ): N.Expression { - if (this.testTopicReferenceConfiguration(pipeProposal, start, tokenType)) { + if ( + this.testTopicReferenceConfiguration(pipeProposal, startLoc, tokenType) + ) { // The token matches the plugin’s configuration. // The token is therefore a topic reference. // Determine the node type for the topic reference // that is appropriate for the active pipe-operator proposal. - let nodeType; - if (pipeProposal === "smart") { - nodeType = "PipelinePrimaryTopicReference"; - } else { - // The proposal must otherwise be "hack", - // as enforced by testTopicReferenceConfiguration. - nodeType = "TopicReference"; - } + const nodeType = + pipeProposal === "smart" + ? "PipelinePrimaryTopicReference" + : // The proposal must otherwise be "hack", + // as enforced by testTopicReferenceConfiguration. + "TopicReference"; if (!this.topicReferenceIsAllowedInCurrentContext()) { - // The topic reference is not allowed in the current context: - // it is outside of a pipe body. - // Raise recoverable errors. - if (pipeProposal === "smart") { - this.raise(start, Errors.PrimaryTopicNotAllowed); - } else { - // In this case, `pipeProposal === "hack"` is true. - this.raise(start, Errors.PipeTopicUnbound); - } + this.raise( + // The topic reference is not allowed in the current context: + // it is outside of a pipe body. + // Raise recoverable errors. + pipeProposal === "smart" + ? Errors.PrimaryTopicNotAllowed + : // In this case, `pipeProposal === "hack"` is true. + Errors.PipeTopicUnbound, + { at: startLoc }, + ); } // Register the topic reference so that its pipe body knows @@ -1403,8 +1407,8 @@ export default class ExpressionParser extends LValParser { } else { // The token does not match the plugin’s configuration. throw this.raise( - start, Errors.PipeTopicUnconfiguredToken, + { at: startLoc }, tokenLabelName(tokenType), ); } @@ -1421,7 +1425,7 @@ export default class ExpressionParser extends LValParser { // then an error is thrown. testTopicReferenceConfiguration( pipeProposal: string, - start: number, + startLoc: Position, tokenType: TokenType, ): boolean { switch (pipeProposal) { @@ -1436,7 +1440,7 @@ export default class ExpressionParser extends LValParser { case "smart": return tokenType === tt.hash; default: - throw this.raise(start, Errors.PipeTopicRequiresHackPipes); + throw this.raise(Errors.PipeTopicRequiresHackPipes, { at: startLoc }); } } @@ -1448,7 +1452,9 @@ export default class ExpressionParser extends LValParser { const params = [this.parseIdentifier()]; this.prodParam.exit(); if (this.hasPrecedingLineBreak()) { - this.raise(this.state.pos, Errors.LineTerminatorBeforeArrow); + this.raise(Errors.LineTerminatorBeforeArrow, { + at: this.state.curPosition(), + }); } this.expect(tt.arrow); // let foo = async bar => {}; @@ -1490,12 +1496,12 @@ export default class ExpressionParser extends LValParser { !this.scope.allowDirectSuper && !this.options.allowSuperOutsideMethod ) { - this.raise(node.start, Errors.SuperNotAllowed); + this.raise(Errors.SuperNotAllowed, { node }); } else if ( !this.scope.allowSuper && !this.options.allowSuperOutsideMethod ) { - this.raise(node.start, Errors.UnexpectedSuper); + this.raise(Errors.UnexpectedSuper, { node }); } if ( @@ -1503,7 +1509,7 @@ export default class ExpressionParser extends LValParser { !this.match(tt.bracketL) && !this.match(tt.dot) ) { - this.raise(node.start, Errors.UnsupportedSuper); + this.raise(Errors.UnsupportedSuper, { node }); } return this.finishNode(node, "Super"); @@ -1518,6 +1524,7 @@ export default class ExpressionParser extends LValParser { new Position( this.state.curLine, this.state.start + 1 - this.state.lineStart, + this.state.start + 1, ), ); const name = this.state.value; @@ -1567,8 +1574,8 @@ export default class ExpressionParser extends LValParser { if (node.property.name !== propertyName || containsEsc) { this.raise( - node.property.start, Errors.UnsupportedMetaProperty, + { node: node.property }, meta.name, propertyName, ); @@ -1584,7 +1591,9 @@ export default class ExpressionParser extends LValParser { if (this.isContextual(tt._meta)) { if (!this.inModule) { - this.raise(id.start, SourceTypeModuleErrors.ImportMetaOutsideModule); + this.raise(SourceTypeModuleErrors.ImportMetaOutsideModule, { + node: id, + }); } this.sawUnambiguousESM = true; } @@ -1667,8 +1676,8 @@ export default class ExpressionParser extends LValParser { const exprList = []; const refExpressionErrors = new ExpressionErrors(); let first = true; - let spreadStart; - let optionalCommaStart; + let spreadStartLoc; + let optionalCommaStartLoc; while (!this.match(tt.parenR)) { if (first) { @@ -1676,12 +1685,12 @@ export default class ExpressionParser extends LValParser { } else { this.expect( tt.comma, - refExpressionErrors.optionalParameters === -1 + refExpressionErrors.optionalParametersLoc === null ? null - : refExpressionErrors.optionalParameters, + : refExpressionErrors.optionalParametersLoc, ); if (this.match(tt.parenR)) { - optionalCommaStart = this.state.start; + optionalCommaStartLoc = this.state.startLoc; break; } } @@ -1689,7 +1698,7 @@ export default class ExpressionParser extends LValParser { if (this.match(tt.ellipsis)) { const spreadNodeStartPos = this.state.start; const spreadNodeStartLoc = this.state.startLoc; - spreadStart = this.state.start; + spreadStartLoc = this.state.startLoc; exprList.push( this.parseParenItem( this.parseRestBinding(), @@ -1698,9 +1707,9 @@ export default class ExpressionParser extends LValParser { ), ); - this.checkCommaAfterRest(charCodes.rightParenthesis); - - break; + if (!this.checkCommaAfterRest(charCodes.rightParenthesis)) { + break; + } } else { exprList.push( this.parseMaybeAssignAllowIn( @@ -1711,7 +1720,6 @@ export default class ExpressionParser extends LValParser { } } - const innerEndPos = this.state.lastTokEnd; const innerEndLoc = this.state.lastTokEndLoc; this.expect(tt.parenR); @@ -1732,10 +1740,10 @@ export default class ExpressionParser extends LValParser { this.expressionScope.exit(); if (!exprList.length) { - this.unexpected(this.state.lastTokStart); + this.unexpected(this.state.lastTokStartLoc); } - if (optionalCommaStart) this.unexpected(optionalCommaStart); - if (spreadStart) this.unexpected(spreadStart); + if (optionalCommaStartLoc) this.unexpected(optionalCommaStartLoc); + if (spreadStartLoc) this.unexpected(spreadStartLoc); this.checkExpressionErrors(refExpressionErrors, true); this.toReferencedListDeep(exprList, /* isParenthesizedExpr */ true); @@ -1744,7 +1752,7 @@ export default class ExpressionParser extends LValParser { val.expressions = exprList; // finish node at current location so it can pick up comments after `)` this.finishNode(val, "SequenceExpression"); - this.resetEndLocation(val, innerEndPos, innerEndLoc); + this.resetEndLocation(val, innerEndLoc); } else { val = exprList[0]; } @@ -1753,7 +1761,11 @@ export default class ExpressionParser extends LValParser { this.addExtra(val, "parenthesized", true); this.addExtra(val, "parenStart", startPos); - this.takeSurroundingComments(val, startPos, this.state.lastTokEnd); + this.takeSurroundingComments( + val, + startPos, + this.state.lastTokEndLoc.index, + ); return val; } @@ -1793,7 +1805,7 @@ export default class ExpressionParser extends LValParser { const metaProp = this.parseMetaProperty(node, meta, "target"); if (!this.scope.inNonArrowFunction && !this.scope.inClass) { - this.raise(metaProp.start, Errors.UnexpectedNewTarget); + this.raise(Errors.UnexpectedNewTarget, { node: metaProp }); } return metaProp; @@ -1811,11 +1823,15 @@ export default class ExpressionParser extends LValParser { parseNew(node: N.Expression): N.NewExpression { node.callee = this.parseNoCallExpr(); if (node.callee.type === "Import") { - this.raise(node.callee.start, Errors.ImportCallNotNewExpression); + this.raise(Errors.ImportCallNotNewExpression, { node: node.callee }); } else if (this.isOptionalChain(node.callee)) { - this.raise(this.state.lastTokEnd, Errors.OptionalChainingNoNew); + this.raise(Errors.OptionalChainingNoNew, { + at: this.state.lastTokEndLoc, + }); } else if (this.eat(tt.questionDot)) { - this.raise(this.state.start, Errors.OptionalChainingNoNew); + this.raise(Errors.OptionalChainingNoNew, { + at: this.state.startLoc, + }); } this.parseNewArguments(node); @@ -1836,15 +1852,18 @@ export default class ExpressionParser extends LValParser { // Parse template expression. parseTemplateElement(isTagged: boolean): N.TemplateElement { - const { start, end, value } = this.state; + const { start, startLoc, end, value } = this.state; const elemStart = start + 1; const elem = this.startNodeAt( elemStart, - createPositionWithColumnOffset(this.state.startLoc, 1), + createPositionWithColumnOffset(startLoc, 1), ); if (value === null) { if (!isTagged) { - this.raise(start + 2, Errors.InvalidEscapeSequenceTemplate); + this.raise(Errors.InvalidEscapeSequenceTemplate, { + // FIXME: explain + at: createPositionWithColumnOffset(startLoc, 2), + }); } } @@ -1860,7 +1879,6 @@ export default class ExpressionParser extends LValParser { this.finishNode(elem, "TemplateElement"); this.resetEndLocation( elem, - elemEnd, createPositionWithColumnOffset(this.state.lastTokEndLoc, endOffset), ); return elem; @@ -1911,7 +1929,7 @@ export default class ExpressionParser extends LValParser { } else { this.expect(tt.comma); if (this.match(close)) { - this.addExtra(node, "trailingComma", this.state.lastTokStart); + this.addTrailingCommaExtraToNode(node); break; } } @@ -1929,7 +1947,7 @@ export default class ExpressionParser extends LValParser { !this.isObjectProperty(prop) && prop.type !== "SpreadElement" ) { - this.raise(prop.start, Errors.InvalidRecordProperty); + this.raise(Errors.InvalidRecordProperty, { node: prop }); } // $FlowIgnore @@ -1952,6 +1970,11 @@ export default class ExpressionParser extends LValParser { return this.finishNode(node, type); } + addTrailingCommaExtraToNode(node: N.Node): void { + this.addExtra(node, "trailingComma", this.state.lastTokStart); + this.addExtra(node, "trailingCommaLoc", this.state.lastTokStartLoc, false); + } + // Check grammar production: // IdentifierName *_opt PropertyName // It is used in `parsePropertyDefinition` to detect AsyncMethod and Accessors @@ -1972,7 +1995,9 @@ export default class ExpressionParser extends LValParser { let decorators = []; if (this.match(tt.at)) { if (this.hasPlugin("decorators")) { - this.raise(this.state.start, Errors.UnsupportedPropertyDecorator); + this.raise(Errors.UnsupportedPropertyDecorator, { + at: this.state.startLoc, + }); } // we needn't check if decorators (stage 0) plugin is enabled since it's checked by @@ -2028,7 +2053,11 @@ export default class ExpressionParser extends LValParser { prop.kind = keyName; if (this.match(tt.star)) { isGenerator = true; - this.raise(this.state.pos, Errors.AccessorIsGenerator, keyName); + this.raise( + Errors.AccessorIsGenerator, + { at: this.state.curPosition() }, + keyName, + ); this.next(); } this.parsePropertyName(prop); @@ -2066,21 +2095,18 @@ export default class ExpressionParser extends LValParser { const paramCount = this.getGetterSetterExpectedParamCount(method); const params = this.getObjectOrClassMethodParams(method); - const start = method.start; - if (params.length !== paramCount) { - if (method.kind === "get") { - this.raise(start, Errors.BadGetterArity); - } else { - this.raise(start, Errors.BadSetterArity); - } + this.raise( + method.kind === "get" ? Errors.BadGetterArity : Errors.BadSetterArity, + { node: method }, + ); } if ( method.kind === "set" && params[params.length - 1]?.type === "RestElement" ) { - this.raise(start, Errors.BadSetterRestParameter); + this.raise(Errors.BadSetterRestParameter, { node: method }); } } @@ -2147,7 +2173,7 @@ export default class ExpressionParser extends LValParser { // IdentifierReference // CoverInitializedName // Note: `{ eval } = {}` will be checked in `checkLVal` later. - this.checkReservedWord(prop.key.name, prop.key.start, true, false); + this.checkReservedWord(prop.key.name, prop.key.loc.start, true, false); if (isPattern) { prop.value = this.parseMaybeDefault( @@ -2156,13 +2182,15 @@ export default class ExpressionParser extends LValParser { cloneIdentifier(prop.key), ); } else if (this.match(tt.eq)) { - const shorthandAssign = this.state.start; + const shorthandAssignLoc = this.state.startLoc; if (refExpressionErrors != null) { - if (refExpressionErrors.shorthandAssign === -1) { - refExpressionErrors.shorthandAssign = shorthandAssign; + if (refExpressionErrors.shorthandAssignLoc === null) { + refExpressionErrors.shorthandAssignLoc = shorthandAssignLoc; } } else { - this.raise(shorthandAssign, Errors.InvalidCoverInitializedName); + this.raise(Errors.InvalidCoverInitializedName, { + at: shorthandAssignLoc, + }); } prop.value = this.parseMaybeDefault( startPos, @@ -2240,8 +2268,10 @@ export default class ExpressionParser extends LValParser { break; case tt.privateName: { // the class private key has been handled in parseClassElementName - const privateKeyPos = this.state.start + 1; - this.raise(privateKeyPos, Errors.UnexpectedPrivateField); + this.raise(Errors.UnexpectedPrivateField, { + // FIXME: explain + at: createPositionWithColumnOffset(this.state.startLoc, 1), + }); key = this.parsePrivateName(); break; } @@ -2332,7 +2362,7 @@ export default class ExpressionParser extends LValParser { node: N.ArrowFunctionExpression, params: ?(N.Expression[]), isAsync: boolean, - trailingCommaPos: ?number, + trailingCommaLoc: ?Position, ): N.ArrowFunctionExpression { this.scope.enter(SCOPE_FUNCTION | SCOPE_ARROW); let flags = functionFlags(isAsync, false); @@ -2346,7 +2376,7 @@ export default class ExpressionParser extends LValParser { if (params) { this.state.maybeInArrowParameters = true; - this.setArrowFunctionParameters(node, params, trailingCommaPos); + this.setArrowFunctionParameters(node, params, trailingCommaLoc); } this.state.maybeInArrowParameters = false; this.parseFunctionBody(node, true); @@ -2361,9 +2391,9 @@ export default class ExpressionParser extends LValParser { setArrowFunctionParameters( node: N.ArrowFunctionExpression, params: N.Expression[], - trailingCommaPos: ?number, + trailingCommaLoc: ?Position, ): void { - node.params = this.toAssignableList(params, trailingCommaPos, false); + node.params = this.toAssignableList(params, trailingCommaLoc, false); } parseFunctionBodyAndFinish( @@ -2408,14 +2438,15 @@ export default class ExpressionParser extends LValParser { if (hasStrictModeDirective && nonSimple) { // This logic is here to align the error location with the ESTree plugin. - const errorPos = + const errorOrigin = // $FlowIgnore (node.kind === "method" || node.kind === "constructor") && // $FlowIgnore !!node.key - ? node.key.end - : node.start; - this.raise(errorPos, Errors.IllegalLanguageModeDirective); + ? { at: node.key.loc.end } + : { node }; + + this.raise(Errors.IllegalLanguageModeDirective, errorOrigin); } const strictModeChanged = !oldStrict && this.state.strict; @@ -2499,11 +2530,7 @@ export default class ExpressionParser extends LValParser { this.expect(tt.comma); if (this.match(close)) { if (nodeForExtra) { - this.addExtra( - nodeForExtra, - "trailingComma", - this.state.lastTokStart, - ); + this.addTrailingCommaExtraToNode(nodeForExtra); } this.next(); break; @@ -2523,7 +2550,11 @@ export default class ExpressionParser extends LValParser { let elt; if (this.match(tt.comma)) { if (!allowEmpty) { - this.raise(this.state.pos, Errors.UnexpectedToken, ","); + this.raise( + Errors.UnexpectedToken, + { at: this.state.curPosition() }, + ",", + ); } elt = null; } else if (this.match(tt.ellipsis)) { @@ -2538,7 +2569,9 @@ export default class ExpressionParser extends LValParser { } else if (this.match(tt.question)) { this.expectPlugin("partialApplication"); if (!allowPlaceholder) { - this.raise(this.state.start, Errors.UnexpectedArgumentPlaceholder); + this.raise(Errors.UnexpectedArgumentPlaceholder, { + at: this.state.startLoc, + }); } const node = this.startNode(); this.next(); @@ -2575,7 +2608,7 @@ export default class ExpressionParser extends LValParser { parseIdentifierName(pos: number, liberal?: boolean): string { let name: string; - const { start, type } = this.state; + const { startLoc, type } = this.state; if (tokenIsKeywordOrIdentifier(type)) { name = this.state.value; @@ -2592,7 +2625,7 @@ export default class ExpressionParser extends LValParser { this.replaceToken(tt.name); } } else { - this.checkReservedWord(name, start, tokenIsKeyword, false); + this.checkReservedWord(name, startLoc, tokenIsKeyword, false); } this.next(); @@ -2602,7 +2635,7 @@ export default class ExpressionParser extends LValParser { checkReservedWord( word: string, - startLoc: number, + startLoc: Position, checkKeywords: boolean, isBinding: boolean, ): void { @@ -2618,31 +2651,35 @@ export default class ExpressionParser extends LValParser { if (word === "yield") { if (this.prodParam.hasYield) { - this.raise(startLoc, Errors.YieldBindingIdentifier); + this.raise(Errors.YieldBindingIdentifier, { at: startLoc }); return; } } else if (word === "await") { if (this.prodParam.hasAwait) { - this.raise(startLoc, Errors.AwaitBindingIdentifier); + this.raise(Errors.AwaitBindingIdentifier, { at: startLoc }); return; - } else if (this.scope.inStaticBlock) { - this.raise(startLoc, Errors.AwaitBindingIdentifierInStaticBlock); + } + + if (this.scope.inStaticBlock) { + this.raise(Errors.AwaitBindingIdentifierInStaticBlock, { + at: startLoc, + }); return; - } else { - this.expressionScope.recordAsyncArrowParametersError( - startLoc, - Errors.AwaitBindingIdentifier, - ); } + + this.expressionScope.recordAsyncArrowParametersError( + Errors.AwaitBindingIdentifier, + startLoc, + ); } else if (word === "arguments") { if (this.scope.inClassAndNotInNonArrowFunction) { - this.raise(startLoc, Errors.ArgumentsInClass); + this.raise(Errors.ArgumentsInClass, { at: startLoc }); return; } } if (checkKeywords && isKeyword(word)) { - this.raise(startLoc, Errors.UnexpectedKeyword, word); + this.raise(Errors.UnexpectedKeyword, { at: startLoc }, word); return; } @@ -2653,7 +2690,7 @@ export default class ExpressionParser extends LValParser { : isStrictReservedWord; if (reservedTest(word, this.inModule)) { - this.raise(startLoc, Errors.UnexpectedReservedWord, word); + this.raise(Errors.UnexpectedReservedWord, { at: startLoc }, word); } } @@ -2671,12 +2708,12 @@ export default class ExpressionParser extends LValParser { const node = this.startNodeAt(startPos, startLoc); this.expressionScope.recordParameterInitializerError( - node.start, + node.loc.start, Errors.AwaitExpressionFormalParameter, ); if (this.eat(tt.star)) { - this.raise(node.start, Errors.ObsoleteAwaitStar); + this.raise(Errors.ObsoleteAwaitStar, { node }); } if (!this.scope.inFunction && !this.options.allowAwaitOutsideFunction) { @@ -2720,7 +2757,7 @@ export default class ExpressionParser extends LValParser { const node = this.startNode(); this.expressionScope.recordParameterInitializerError( - node.start, + node.loc.start, Errors.YieldInParameter, ); @@ -2755,27 +2792,18 @@ export default class ExpressionParser extends LValParser { // Validates a pipeline (for any of the pipeline Babylon plugins) at the point // of the infix operator `|>`. - checkPipelineAtInfixOperator(left: N.Expression, leftStartPos: number) { + checkPipelineAtInfixOperator(left: N.Expression, leftStartLoc: Position) { if (this.hasPlugin(["pipelineOperator", { proposal: "smart" }])) { if (left.type === "SequenceExpression") { // Ensure that the pipeline head is not a comma-delimited // sequence expression. - this.raise(leftStartPos, Errors.PipelineHeadSequenceExpression); + this.raise(Errors.PipelineHeadSequenceExpression, { + at: leftStartLoc, + }); } } } - // This helper method is to be called immediately - // after a Hack-style pipe body is parsed. - // The `startPos` is the starting position of the pipe body. - - checkHackPipeBodyEarlyErrors(startPos: number): void { - if (!this.topicReferenceWasUsedInCurrentContext()) { - // A Hack pipe body must use the topic reference at least once. - this.raise(startPos, Errors.PipeTopicUnused); - } - } - parseSmartPipelineBodyInStyle( childExpr: N.Expression, startPos: number, @@ -2786,7 +2814,7 @@ export default class ExpressionParser extends LValParser { bodyNode.callee = childExpr; return this.finishNode(bodyNode, "PipelineBareFunction"); } else { - this.checkSmartPipeTopicBodyEarlyErrors(startPos); + this.checkSmartPipeTopicBodyEarlyErrors(startLoc); bodyNode.expression = childExpr; return this.finishNode(bodyNode, "PipelineTopicExpression"); } @@ -2807,9 +2835,9 @@ export default class ExpressionParser extends LValParser { // This helper method is to be called immediately // after a topic-style smart-mix pipe body is parsed. - // The `startPos` is the starting position of the pipe body. + // The `startLoc` is the starting position of the pipe body. - checkSmartPipeTopicBodyEarlyErrors(startPos: number): void { + checkSmartPipeTopicBodyEarlyErrors(startLoc: Position): void { // If the following token is invalidly `=>`, then throw a human-friendly error // instead of something like 'Unexpected token, expected ";"'. // For example, `x => x |> y => #` (assuming `#` is the topic reference) @@ -2817,12 +2845,12 @@ export default class ExpressionParser extends LValParser { // and `(x |> y) => #` is an invalid arrow function. // This is because smart-mix `|>` has tighter precedence than `=>`. if (this.match(tt.arrow)) { - throw this.raise(this.state.start, Errors.PipelineBodyNoArrow); + throw this.raise(Errors.PipelineBodyNoArrow, { at: this.state.startLoc }); } // A topic-style smart-mix pipe body must use the topic reference at least once. - else if (!this.topicReferenceWasUsedInCurrentContext()) { - this.raise(startPos, Errors.PipelineTopicUnused); + if (!this.topicReferenceWasUsedInCurrentContext()) { + this.raise(Errors.PipelineTopicUnused, { at: startLoc }); } } diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 7cbb35e4c516..0d0feaf26ead 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -98,17 +98,17 @@ export default class LValParser extends NodeUtils { // see also `recordParenthesizedIdentifierError` signature in packages/babel-parser/src/util/expression-scope.js if (parenthesized.type === "Identifier") { this.expressionScope.recordParenthesizedIdentifierError( - node.start, Errors.InvalidParenthesizedAssignment, + node.loc.start, ); } else if (parenthesized.type !== "MemberExpression") { // A parenthesized member expression can be in LHS but not in pattern. // If the LHS is later interpreted as a pattern, `checkLVal` will throw for member expression binding // i.e. `([(a.b) = []] = []) => {}` - this.raise(node.start, Errors.InvalidParenthesizedAssignment); + this.raise(Errors.InvalidParenthesizedAssignment, { node }); } } else { - this.raise(node.start, Errors.InvalidParenthesizedAssignment); + this.raise(Errors.InvalidParenthesizedAssignment, { node }); } } @@ -134,9 +134,11 @@ export default class LValParser extends NodeUtils { if ( isLast && prop.type === "RestElement" && - node.extra?.trailingComma + node.extra?.trailingCommaLoc ) { - this.raiseRestNotLast(node.extra.trailingComma); + this.raise(Errors.RestTrailingComma, { + at: node.extra.trailingCommaLoc, + }); } } break; @@ -156,12 +158,16 @@ export default class LValParser extends NodeUtils { case "ArrayExpression": node.type = "ArrayPattern"; - this.toAssignableList(node.elements, node.extra?.trailingComma, isLHS); + this.toAssignableList( + node.elements, + node.extra?.trailingCommaLoc, + isLHS, + ); break; case "AssignmentExpression": if (node.operator !== "=") { - this.raise(node.left.end, Errors.MissingEqInAssignment); + this.raise(Errors.MissingEqInAssignment, { at: node.left.loc.end }); } node.type = "AssignmentPattern"; @@ -187,16 +193,16 @@ export default class LValParser extends NodeUtils { isLHS: boolean, ) { if (prop.type === "ObjectMethod") { - const error = + /* eslint-disable @babel/development-internal/dry-error-messages */ + this.raise( prop.kind === "get" || prop.kind === "set" ? Errors.PatternHasAccessor - : Errors.PatternHasMethod; - - /* eslint-disable @babel/development-internal/dry-error-messages */ - this.raise(prop.key.start, error); + : Errors.PatternHasMethod, + { node: prop.key }, + ); /* eslint-enable @babel/development-internal/dry-error-messages */ } else if (prop.type === "SpreadElement" && !isLast) { - this.raiseRestNotLast(prop.start); + this.raise(Errors.RestTrailingComma, { node: prop }); } else { this.toAssignable(prop, isLHS); } @@ -206,7 +212,7 @@ export default class LValParser extends NodeUtils { toAssignableList( exprList: Expression[], - trailingCommaPos?: ?number, + trailingCommaLoc?: ?Position, isLHS: boolean, ): $ReadOnlyArray { let end = exprList.length; @@ -228,8 +234,8 @@ export default class LValParser extends NodeUtils { this.unexpected(arg.start); } - if (trailingCommaPos) { - this.raiseTrailingCommaAfterRest(trailingCommaPos); + if (trailingCommaLoc) { + this.raise(Errors.RestTrailingComma, { at: trailingCommaLoc }); } --end; @@ -240,7 +246,7 @@ export default class LValParser extends NodeUtils { if (elt) { this.toAssignable(elt, isLHS); if (elt.type === "RestElement") { - this.raiseRestNotLast(elt.start); + this.raise(Errors.RestTrailingComma, { node: elt }); } } } @@ -385,13 +391,16 @@ export default class LValParser extends NodeUtils { break; } else if (this.match(tt.ellipsis)) { elts.push(this.parseAssignableListItemTypes(this.parseRestBinding())); - this.checkCommaAfterRest(closeCharCode); - this.expect(close); - break; + if (!this.checkCommaAfterRest(closeCharCode)) { + this.expect(close); + break; + } } else { const decorators = []; if (this.match(tt.at) && this.hasPlugin("decorators")) { - this.raise(this.state.start, Errors.UnsupportedParameterDecorator); + this.raise(Errors.UnsupportedParameterDecorator, { + at: this.state.startLoc, + }); } // invariant: hasPlugin("decorators-legacy") while (this.match(tt.at)) { @@ -509,33 +518,35 @@ export default class LValParser extends NodeUtils { : isStrictBindOnlyReservedWord(name)) ) { this.raise( - expr.start, bindingType === BIND_NONE ? Errors.StrictEvalArguments : Errors.StrictEvalArgumentsBinding, + { node: expr }, name, ); } if (checkClashes) { if (checkClashes.has(name)) { - this.raise(expr.start, Errors.ParamDupe); + this.raise(Errors.ParamDupe, { node: expr }); } else { checkClashes.add(name); } } if (disallowLetBinding && name === "let") { - this.raise(expr.start, Errors.LetInLexicalBinding); + this.raise(Errors.LetInLexicalBinding, { node: expr }); } if (!(bindingType & BIND_NONE)) { - this.scope.declareName(name, bindingType, expr.start); + this.scope.declareName(name, bindingType, expr.loc.start); } break; } case "MemberExpression": if (bindingType !== BIND_NONE) { - this.raise(expr.start, Errors.InvalidPropertyBindingPattern); + this.raise(Errors.InvalidPropertyBindingPattern, { + node: expr, + }); } break; @@ -600,10 +611,10 @@ export default class LValParser extends NodeUtils { default: { this.raise( - expr.start, bindingType === BIND_NONE ? Errors.InvalidLhs : Errors.InvalidLhsBinding, + { node: expr }, contextDescription, ); } @@ -615,25 +626,24 @@ export default class LValParser extends NodeUtils { node.argument.type !== "Identifier" && node.argument.type !== "MemberExpression" ) { - this.raise(node.argument.start, Errors.InvalidRestAssignmentPattern); + this.raise(Errors.InvalidRestAssignmentPattern, { + node: node.argument, + }); } } checkCommaAfterRest(close: $Values): void { - if (this.match(tt.comma)) { - if (this.lookaheadCharCode() === close) { - this.raiseTrailingCommaAfterRest(this.state.start); - } else { - this.raiseRestNotLast(this.state.start); - } + if (!this.match(tt.comma)) { + return false; } - } - raiseRestNotLast(pos: number) { - throw this.raise(pos, Errors.ElementAfterRest); - } + this.raise( + this.lookaheadCharCode() === close + ? Errors.RestTrailingComma + : Errors.ElementAfterRest, + { at: this.state.startLoc }, + ); - raiseTrailingCommaAfterRest(pos: number) { - this.raise(pos, Errors.RestTrailingComma); + return true; } } diff --git a/packages/babel-parser/src/parser/node.js b/packages/babel-parser/src/parser/node.js index de4a7da943ba..b12c533ce96b 100644 --- a/packages/babel-parser/src/parser/node.js +++ b/packages/babel-parser/src/parser/node.js @@ -113,22 +113,12 @@ export class NodeUtils extends UtilParser { // Finish an AST node, adding `type` and `end` properties. finishNode(node: T, type: string): T { - return this.finishNodeAt( - node, - type, - this.state.lastTokEnd, - this.state.lastTokEndLoc, - ); + return this.finishNodeAt(node, type, this.state.lastTokEndLoc); } // Finish node at given position - finishNodeAt( - node: T, - type: string, - pos: number, - loc: Position, - ): T { + finishNodeAt(node: T, type: string, endLoc: Position): T { if (process.env.NODE_ENV !== "production" && node.end > 0) { throw new Error( "Do not call finishNode*() twice on the same node." + @@ -136,9 +126,9 @@ export class NodeUtils extends UtilParser { ); } node.type = type; - node.end = pos; - node.loc.end = loc; - if (this.options.ranges) node.range[1] = pos; + node.end = endLoc.index; + node.loc.end = endLoc; + if (this.options.ranges) node.range[1] = endLoc.index; if (this.options.attachComment) this.processComment(node); return node; } @@ -151,12 +141,11 @@ export class NodeUtils extends UtilParser { resetEndLocation( node: NodeBase, - end?: number = this.state.lastTokEnd, endLoc?: Position = this.state.lastTokEndLoc, ): void { - node.end = end; + node.end = endLoc.index; node.loc.end = endLoc; - if (this.options.ranges) node.range[1] = end; + if (this.options.ranges) node.range[1] = endLoc.index; } /** diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 7cd39dd4bc2e..b04bc4b69e75 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -40,7 +40,7 @@ import { } from "../util/expression-scope"; import type { SourceType } from "../options"; import { Token } from "../tokenizer"; -import { createPositionWithColumnOffset } from "../util/location"; +import { Position, createPositionWithColumnOffset } from "../util/location"; import { cloneStringLiteral, cloneIdentifier } from "./node"; const loopLabel = { kind: "loop" }, @@ -217,10 +217,8 @@ export default class StatementParser extends ExpressionParser { !this.options.allowUndeclaredExports && this.scope.undefinedExports.size > 0 ) { - for (const [name] of Array.from(this.scope.undefinedExports)) { - const pos = this.scope.undefinedExports.get(name); - // $FlowIssue - this.raise(pos, Errors.ModuleExportUndefined, name); + for (const [name, loc] of Array.from(this.scope.undefinedExports)) { + this.raise(Errors.ModuleExportUndefined, { at: loc }, name); } } return this.finishNode(program, "Program"); @@ -360,9 +358,9 @@ export default class StatementParser extends ExpressionParser { if (this.lookaheadCharCode() === charCodes.dot) break; if (context) { if (this.state.strict) { - this.raise(this.state.start, Errors.StrictFunction); + this.raise(Errors.StrictFunction, { at: this.state.startLoc }); } else if (context !== "if" && context !== "label") { - this.raise(this.state.start, Errors.SloppyFunction); + this.raise(Errors.SloppyFunction, { at: this.state.startLoc }); } } return this.parseFunctionStatement(node, false, !context); @@ -386,7 +384,9 @@ export default class StatementParser extends ExpressionParser { case tt._var: kind = kind || this.state.value; if (context && kind !== "var") { - this.raise(this.state.start, Errors.UnexpectedLexicalDeclaration); + this.raise(Errors.UnexpectedLexicalDeclaration, { + at: this.state.startLoc, + }); } return this.parseVarStatement(node, kind); @@ -410,7 +410,9 @@ export default class StatementParser extends ExpressionParser { // fall through case tt._export: { if (!this.options.allowImportExportEverywhere && !topLevel) { - this.raise(this.state.start, Errors.UnexpectedImportExport); + this.raise(Errors.UnexpectedImportExport, { + at: this.state.startLoc, + }); } this.next(); // eat `import`/`export` @@ -447,10 +449,9 @@ export default class StatementParser extends ExpressionParser { default: { if (this.isAsyncFunction()) { if (context) { - this.raise( - this.state.start, - Errors.AsyncFunctionInSingleStatementContext, - ); + this.raise(Errors.AsyncFunctionInSingleStatementContext, { + at: this.state.startLoc, + }); } this.next(); return this.parseFunctionStatement(node, true, !context); @@ -479,7 +480,7 @@ export default class StatementParser extends ExpressionParser { assertModuleNodeAllowed(node: N.Node): void { if (!this.options.allowImportExportEverywhere && !this.inModule) { - this.raise(node.start, SourceTypeModuleErrors.ImportOutsideModule); + this.raise(SourceTypeModuleErrors.ImportOutsideModule, { node }); } } @@ -514,10 +515,12 @@ export default class StatementParser extends ExpressionParser { this.hasPlugin("decorators") && !this.getPluginOption("decorators", "decoratorsBeforeExport") ) { - this.raise(this.state.start, Errors.DecoratorExportClass); + this.raise(Errors.DecoratorExportClass, { at: this.state.startLoc }); } } else if (!this.canHaveLeadingDecorator()) { - throw this.raise(this.state.start, Errors.UnexpectedLeadingDecorator); + throw this.raise(Errors.UnexpectedLeadingDecorator, { + at: this.state.startLoc, + }); } } @@ -606,8 +609,8 @@ export default class StatementParser extends ExpressionParser { } if (i === this.state.labels.length) { this.raise( - node.start, Errors.IllegalBreakContinue, + { node }, isBreak ? "break" : "continue", ); } @@ -660,15 +663,16 @@ export default class StatementParser extends ExpressionParser { this.next(); this.state.labels.push(loopLabel); - let awaitAt = -1; + let awaitAt = null; + if (this.isAwaitAllowed() && this.eatContextual(tt._await)) { - awaitAt = this.state.lastTokStart; + awaitAt = this.state.lastTokStartLoc; } this.scope.enter(SCOPE_OTHER); this.expect(tt.parenL); if (this.match(tt.semi)) { - if (awaitAt > -1) { + if (awaitAt !== null) { this.unexpected(awaitAt); } return this.parseFor(node, null); @@ -689,7 +693,7 @@ export default class StatementParser extends ExpressionParser { ) { return this.parseForIn(node, init, awaitAt); } - if (awaitAt > -1) { + if (awaitAt !== null) { this.unexpected(awaitAt); } return this.parseFor(node, init); @@ -705,10 +709,12 @@ export default class StatementParser extends ExpressionParser { if (isForOf) { // Check for leading tokens that are forbidden in for-of loops: if (startsWithLet) { - this.raise(init.start, Errors.ForOfLet); - } else if ( + this.raise(Errors.ForOfLet, { node: init }); + } + + if ( // `for await (async of []);` is allowed. - awaitAt === -1 && + awaitAt === null && startsWithAsync && init.type === "Identifier" ) { @@ -716,7 +722,7 @@ export default class StatementParser extends ExpressionParser { // parsed as an identifier. If it was parsed as the start of an async // arrow function (e.g. `for (async of => {} of []);`), the LVal check // further down will raise a more appropriate error. - this.raise(init.start, Errors.ForOfAsync); + this.raise(Errors.ForOfAsync, { node: init }); } } if (isForOf || this.match(tt._in)) { @@ -727,7 +733,7 @@ export default class StatementParser extends ExpressionParser { } else { this.checkExpressionErrors(refExpressionErrors, true); } - if (awaitAt > -1) { + if (awaitAt !== null) { this.unexpected(awaitAt); } return this.parseFor(node, init); @@ -756,7 +762,7 @@ export default class StatementParser extends ExpressionParser { parseReturnStatement(node: N.ReturnStatement): N.ReturnStatement { if (!this.prodParam.hasReturn && !this.options.allowReturnOutsideFunction) { - this.raise(this.state.start, Errors.IllegalReturn); + this.raise(Errors.IllegalReturn, { at: this.state.startLoc }); } this.next(); @@ -799,10 +805,9 @@ export default class StatementParser extends ExpressionParser { cur.test = this.parseExpression(); } else { if (sawDefault) { - this.raise( - this.state.lastTokStart, - Errors.MultipleDefaultsInSwitch, - ); + this.raise(Errors.MultipleDefaultsInSwitch, { + at: this.state.lastTokStartLoc, + }); } sawDefault = true; cur.test = null; @@ -826,7 +831,7 @@ export default class StatementParser extends ExpressionParser { parseThrowStatement(node: N.ThrowStatement): N.ThrowStatement { this.next(); if (this.hasPrecedingLineBreak()) { - this.raise(this.state.lastTokEnd, Errors.NewlineAfterThrow); + this.raise(Errors.NewlineAfterThrow, { at: this.state.lastTokEndLoc }); } node.argument = this.parseExpression(); this.semicolon(); @@ -877,7 +882,7 @@ export default class StatementParser extends ExpressionParser { node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null; if (!node.handler && !node.finalizer) { - this.raise(node.start, Errors.NoCatchOrFinally); + this.raise(Errors.NoCatchOrFinally, { node }); } return this.finishNode(node, "TryStatement"); @@ -915,7 +920,7 @@ export default class StatementParser extends ExpressionParser { parseWithStatement(node: N.WithStatement): N.WithStatement { if (this.state.strict) { - this.raise(this.state.start, Errors.StrictWith); + this.raise(Errors.StrictWith, { at: this.state.startLoc }); } this.next(); node.object = this.parseHeaderExpression(); @@ -947,7 +952,7 @@ export default class StatementParser extends ExpressionParser { ): N.LabeledStatement { for (const label of this.state.labels) { if (label.name === maybeName) { - this.raise(expr.start, Errors.LabelRedeclaration, maybeName); + this.raise(Errors.LabelRedeclaration, { node: expr }, maybeName); } } @@ -1136,15 +1141,15 @@ export default class StatementParser extends ExpressionParser { parseForIn( node: N.ForInOf, init: N.VariableDeclaration | N.AssignmentPattern, - awaitAt: number, + awaitAt: ?Position, ): N.ForInOf { const isForIn = this.match(tt._in); this.next(); if (isForIn) { - if (awaitAt > -1) this.unexpected(awaitAt); + if (awaitAt !== null) this.unexpected(awaitAt); } else { - node.await = awaitAt > -1; + node.await = awaitAt !== null; } if ( @@ -1156,12 +1161,14 @@ export default class StatementParser extends ExpressionParser { init.declarations[0].id.type !== "Identifier") ) { this.raise( - init.start, Errors.ForInOfLoopInitializer, + { node: init }, isForIn ? "for-in" : "for-of", ); - } else if (init.type === "AssignmentPattern") { - this.raise(init.start, Errors.InvalidLhs, "for-loop"); + } + + if (init.type === "AssignmentPattern") { + this.raise(Errors.InvalidLhs, { node: init }, "for-loop"); } node.left = init; @@ -1212,8 +1219,8 @@ export default class StatementParser extends ExpressionParser { // It could be a declaration like `const x: number;`. if (!isTypescript) { this.raise( - this.state.lastTokEnd, Errors.DeclarationMissingInitializer, + { at: this.state.lastTokEndLoc }, "Const declarations", ); } @@ -1222,8 +1229,8 @@ export default class StatementParser extends ExpressionParser { !(isFor && (this.match(tt._in) || this.isContextual(tt._of))) ) { this.raise( - this.state.lastTokEnd, Errors.DeclarationMissingInitializer, + { at: this.state.lastTokEndLoc }, "Complex binding patterns", ); } @@ -1261,7 +1268,9 @@ export default class StatementParser extends ExpressionParser { this.initFunction(node, isAsync); if (this.match(tt.star) && isHangingStatement) { - this.raise(this.state.start, Errors.GeneratorInSingleStatementContext); + this.raise(Errors.GeneratorInSingleStatementContext, { + at: this.state.startLoc, + }); } node.generator = this.eat(tt.star); @@ -1338,7 +1347,7 @@ export default class StatementParser extends ExpressionParser { ? BIND_VAR : BIND_LEXICAL : BIND_FUNCTION, - node.id.start, + node.id.loc.start, ); } @@ -1406,7 +1415,9 @@ export default class StatementParser extends ExpressionParser { while (!this.match(tt.braceR)) { if (this.eat(tt.semi)) { if (decorators.length > 0) { - throw this.raise(this.state.lastTokEnd, Errors.DecoratorSemicolon); + throw this.raise(Errors.DecoratorSemicolon, { + at: this.state.lastTokEndLoc, + }); } continue; } @@ -1432,7 +1443,7 @@ export default class StatementParser extends ExpressionParser { member.decorators && member.decorators.length > 0 ) { - this.raise(member.start, Errors.DecoratorConstructor); + this.raise(Errors.DecoratorConstructor, { node: member }); } } }); @@ -1442,7 +1453,7 @@ export default class StatementParser extends ExpressionParser { this.next(); // eat `}` if (decorators.length) { - throw this.raise(this.state.start, Errors.TrailingDecorator); + throw this.raise(Errors.TrailingDecorator, { at: this.state.startLoc }); } this.classScope.exit(); @@ -1540,7 +1551,9 @@ export default class StatementParser extends ExpressionParser { } if (this.isNonstaticConstructor(publicMethod)) { - this.raise(publicMethod.key.start, Errors.ConstructorIsGenerator); + this.raise(Errors.ConstructorIsGenerator, { + node: publicMethod.key, + }); } this.pushClassMethod( @@ -1559,7 +1572,7 @@ export default class StatementParser extends ExpressionParser { tokenIsIdentifier(this.state.type) && !this.state.containsEsc; const isPrivate = this.match(tt.privateName); const key = this.parseClassElementName(member); - const maybeQuestionTokenStart = this.state.start; + const maybeQuestionTokenStartLoc = this.state.startLoc; this.parsePostMemberNameModifiers(publicMember); @@ -1579,10 +1592,10 @@ export default class StatementParser extends ExpressionParser { // TypeScript allows multiple overloaded constructor declarations. if (state.hadConstructor && !this.hasPlugin("typescript")) { - this.raise(key.start, Errors.DuplicateConstructor); + this.raise(Errors.DuplicateConstructor, { node: key }); } if (isConstructor && this.hasPlugin("typescript") && member.override) { - this.raise(key.start, Errors.OverrideOnConstructor); + this.raise(Errors.OverrideOnConstructor, { node: key }); } state.hadConstructor = true; allowsDirectSuper = state.hadSuperClass; @@ -1612,7 +1625,7 @@ export default class StatementParser extends ExpressionParser { const isGenerator = this.eat(tt.star); if (publicMember.optional) { - this.unexpected(maybeQuestionTokenStart); + this.unexpected(maybeQuestionTokenStartLoc); } method.kind = "method"; @@ -1631,7 +1644,7 @@ export default class StatementParser extends ExpressionParser { ); } else { if (this.isNonstaticConstructor(publicMethod)) { - this.raise(publicMethod.key.start, Errors.ConstructorIsAsync); + this.raise(Errors.ConstructorIsAsync, { node: publicMethod.key }); } this.pushClassMethod( @@ -1661,7 +1674,7 @@ export default class StatementParser extends ExpressionParser { this.pushClassPrivateMethod(classBody, privateMethod, false, false); } else { if (this.isNonstaticConstructor(publicMethod)) { - this.raise(publicMethod.key.start, Errors.ConstructorIsAccessor); + this.raise(Errors.ConstructorIsAccessor, { node: publicMethod.key }); } this.pushClassMethod( classBody, @@ -1688,18 +1701,20 @@ export default class StatementParser extends ExpressionParser { // https://tc39.es/ecma262/#prod-ClassElementName parseClassElementName(member: N.ClassMember): N.Expression | N.Identifier { - const { type, value, start } = this.state; + const { type, value } = this.state; if ( (type === tt.name || type === tt.string) && member.static && value === "prototype" ) { - this.raise(start, Errors.StaticPrototype); + this.raise(Errors.StaticPrototype, { at: this.state.startLoc }); } if (type === tt.privateName) { if (value === "constructor") { - this.raise(start, Errors.ConstructorClassPrivateField); + this.raise(Errors.ConstructorClassPrivateField, { + at: this.state.startLoc, + }); } const key = this.parsePrivateName(); member.key = key; @@ -1728,7 +1743,7 @@ export default class StatementParser extends ExpressionParser { this.state.labels = oldLabels; classBody.body.push(this.finishNode(member, "StaticBlock")); if (member.decorators?.length) { - this.raise(member.start, Errors.DecoratorStaticBlock); + this.raise(Errors.DecoratorStaticBlock, { node: member }); } } @@ -1739,7 +1754,7 @@ export default class StatementParser extends ExpressionParser { ) { // Non-computed field, which is either an identifier named "constructor" // or a string literal named "constructor" - this.raise(prop.key.start, Errors.ConstructorClassField); + this.raise(Errors.ConstructorClassField, { node: prop.key }); } classBody.body.push(this.parseClassProperty(prop)); @@ -1755,7 +1770,7 @@ export default class StatementParser extends ExpressionParser { this.classScope.declarePrivateName( this.getPrivateNameSV(node.key), CLASS_ELEMENT_OTHER, - node.key.start, + node.key.loc.start, ); } @@ -1817,7 +1832,7 @@ export default class StatementParser extends ExpressionParser { this.classScope.declarePrivateName( this.getPrivateNameSV(node.key), kind, - node.key.start, + node.key.loc.start, ); } @@ -1869,7 +1884,7 @@ export default class StatementParser extends ExpressionParser { if (optionalId || !isStatement) { node.id = null; } else { - this.unexpected(null, Errors.MissingClassName); + throw this.raise(Errors.MissingClassName, { at: this.state.startLoc }); } } } @@ -2024,24 +2039,32 @@ export default class StatementParser extends ExpressionParser { FUNC_STATEMENT | FUNC_NULLABLE_ID, isAsync, ); - } else if (this.match(tt._class)) { + } + + if (this.match(tt._class)) { return this.parseClass(expr, true, true); - } else if (this.match(tt.at)) { + } + + if (this.match(tt.at)) { if ( this.hasPlugin("decorators") && this.getPluginOption("decorators", "decoratorsBeforeExport") ) { - this.raise(this.state.start, Errors.DecoratorBeforeExport); + this.raise(Errors.DecoratorBeforeExport, { at: this.state.startLoc }); } this.parseDecorators(false); return this.parseClass(expr, true, true); - } else if (this.match(tt._const) || this.match(tt._var) || this.isLet()) { - throw this.raise(this.state.start, Errors.UnsupportedDefaultExport); - } else { - const res = this.parseMaybeAssignAllowIn(); - this.semicolon(); - return res; } + + if (this.match(tt._const) || this.match(tt._var) || this.isLet()) { + throw this.raise(Errors.UnsupportedDefaultExport, { + at: this.state.startLoc, + }); + } + + const res = this.parseMaybeAssignAllowIn(); + this.semicolon(); + return res; } // eslint-disable-next-line no-unused-vars @@ -2118,10 +2141,12 @@ export default class StatementParser extends ExpressionParser { this.expectOnePlugin(["decorators", "decorators-legacy"]); if (this.hasPlugin("decorators")) { if (this.getPluginOption("decorators", "decoratorsBeforeExport")) { - this.unexpected(this.state.start, Errors.DecoratorBeforeExport); - } else { - return true; + throw this.raise(Errors.DecoratorBeforeExport, { + at: this.state.startLoc, + }); } + + return true; } } @@ -2155,7 +2180,9 @@ export default class StatementParser extends ExpressionParser { declaration.end - declaration.start === 4 && // does not contain escape !declaration.extra?.parenthesized ) { - this.raise(declaration.start, Errors.ExportDefaultFromAsIdentifier); + this.raise(Errors.ExportDefaultFromAsIdentifier, { + node: declaration, + }); } } } else if (node.specifiers && node.specifiers.length) { @@ -2170,14 +2197,14 @@ export default class StatementParser extends ExpressionParser { const { local } = specifier; if (local.type !== "Identifier") { this.raise( - specifier.start, Errors.ExportBindingIsString, + { node: specifier }, local.value, exportedName, ); } else { // check for keywords used as local names - this.checkReservedWord(local.name, local.start, true, false); + this.checkReservedWord(local.name, local.loc.start, true, false); // check if export is defined this.scope.checkLocalExport(local); } @@ -2206,7 +2233,7 @@ export default class StatementParser extends ExpressionParser { // If node.declaration is a class, it will take all decorators in the current context. // Thus we should throw if we see non-empty decorators here. if (currentContextDecorators.length) { - throw this.raise(node.start, Errors.UnsupportedDecoratorExport); + throw this.raise(Errors.UnsupportedDecoratorExport, { node }); } } @@ -2243,10 +2270,10 @@ export default class StatementParser extends ExpressionParser { ): void { if (this.exportedIdentifiers.has(name)) { this.raise( - node.start, name === "default" ? Errors.DuplicateDefaultExport : Errors.DuplicateExport, + { node }, name, ); } @@ -2311,8 +2338,8 @@ export default class StatementParser extends ExpressionParser { const surrogate = result.value.match(loneSurrogate); if (surrogate) { this.raise( - result.start, Errors.ModuleExportNameHasLoneSurrogate, + { node: result }, surrogate[0].charCodeAt(0).toString(16), ); } @@ -2410,8 +2437,8 @@ export default class StatementParser extends ExpressionParser { // for now this logic will come into play only when someone declares `type` twice if (attrNames.has(keyName)) { this.raise( - this.state.start, Errors.ModuleAttributesWithDuplicateKeys, + { at: this.state.startLoc }, keyName, ); } @@ -2424,10 +2451,9 @@ export default class StatementParser extends ExpressionParser { this.expect(tt.colon); if (!this.match(tt.string)) { - throw this.unexpected( - this.state.start, - Errors.ModuleAttributeInvalidValue, - ); + throw this.raise(Errors.ModuleAttributeInvalidValue, { + at: this.state.startLoc, + }); } node.value = this.parseStringLiteral(this.state.value); this.finishNode(node, "ImportAttribute"); @@ -2459,26 +2485,25 @@ export default class StatementParser extends ExpressionParser { if (node.key.name !== "type") { this.raise( - node.key.start, Errors.ModuleAttributeDifferentFromType, + { node: node.key }, node.key.name, ); } if (attributes.has(node.key.name)) { this.raise( - node.key.start, Errors.ModuleAttributesWithDuplicateKeys, + { node: node.key }, node.key.name, ); } attributes.add(node.key.name); this.expect(tt.colon); if (!this.match(tt.string)) { - throw this.unexpected( - this.state.start, - Errors.ModuleAttributeInvalidValue, - ); + throw this.raise(Errors.ModuleAttributeInvalidValue, { + at: this.state.startLoc, + }); } node.value = this.parseStringLiteral(this.state.value); this.finishNode(node, "ImportAttribute"); @@ -2545,7 +2570,9 @@ export default class StatementParser extends ExpressionParser { } else { // Detect an attempt to deep destructure if (this.eat(tt.colon)) { - throw this.raise(this.state.start, Errors.DestructureNamedImport); + throw this.raise(Errors.DestructureNamedImport, { + at: this.state.startLoc, + }); } this.expect(tt.comma); @@ -2581,12 +2608,12 @@ export default class StatementParser extends ExpressionParser { const { imported } = specifier; if (importedIsString) { throw this.raise( - specifier.start, Errors.ImportBindingIsString, + { node: specifier }, imported.value, ); } - this.checkReservedWord(imported.name, specifier.start, true, true); + this.checkReservedWord(imported.name, specifier.loc.start, true, true); if (!specifier.local) { specifier.local = cloneIdentifier(imported); } diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index e8fae9c79e62..3818fbc6838e 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -1,7 +1,7 @@ // @flow +import { type Position } from "../util/location"; import { - isTokenType, tokenIsLiteralPropertyName, tokenLabelName, tt, @@ -44,11 +44,20 @@ export default class UtilParser extends Tokenizer { // TODO - addExtra(node: Node, key: string, val: any): void { + addExtra( + node: Node, + key: string, + value: any, + enumerable: boolean = true, + ): void { if (!node) return; const extra = (node.extra = node.extra || {}); - extra[key] = val; + if (enumerable) { + extra[key] = value; + } else { + Object.defineProperty(extra, key, { enumerable, value }); + } } // Tests whether parsed token is a contextual keyword. @@ -90,7 +99,13 @@ export default class UtilParser extends Tokenizer { // Asserts that following token is given contextual keyword. expectContextual(token: TokenType, template?: ErrorTemplate): void { - if (!this.eatContextual(token)) this.unexpected(null, template); + if (!this.eatContextual(token)) { + if (template != null) { + /* eslint-disable @babel/development-internal/dry-error-messages */ + throw this.raise(template, { at: this.state.startLoc }); + } + throw this.unexpected(null, token); + } } // Test whether a semicolon can be inserted at the current position. @@ -105,7 +120,7 @@ export default class UtilParser extends Tokenizer { hasPrecedingLineBreak(): boolean { return lineBreak.test( - this.input.slice(this.state.lastTokEnd, this.state.start), + this.input.slice(this.state.lastTokEndLoc.index, this.state.start), ); } @@ -125,54 +140,48 @@ export default class UtilParser extends Tokenizer { semicolon(allowAsi: boolean = true): void { if (allowAsi ? this.isLineTerminator() : this.eat(tt.semi)) return; - this.raise(this.state.lastTokEnd, Errors.MissingSemicolon); + this.raise(Errors.MissingSemicolon, { at: this.state.lastTokEndLoc }); } // Expect a token of a given type. If found, consume it, otherwise, // raise an unexpected token error at given pos. - expect(type: TokenType, pos?: ?number): void { - this.eat(type) || this.unexpected(pos, type); + expect(type: TokenType, loc?: ?Position): void { + this.eat(type) || this.unexpected(loc, type); } // Throws if the current token and the prev one are separated by a space. assertNoSpace(message: string = "Unexpected space."): void { - if (this.state.start > this.state.lastTokEnd) { + if (this.state.start > this.state.lastTokEndLoc.index) { /* eslint-disable @babel/development-internal/dry-error-messages */ - this.raise(this.state.lastTokEnd, { - code: ErrorCodes.SyntaxError, - reasonCode: "UnexpectedSpace", - template: message, - }); - /* eslint-enable @babel/development-internal/dry-error-messages */ + this.raise( + { + code: ErrorCodes.SyntaxError, + reasonCode: "UnexpectedSpace", + template: message, + }, + { at: this.state.lastTokEndLoc }, + /* eslint-enable @babel/development-internal/dry-error-messages */ + ); } } // Raise an unexpected token error. Can take the expected token type // instead of a message string. - unexpected( - pos: ?number, - messageOrType: ErrorTemplate | TokenType = { - code: ErrorCodes.SyntaxError, - reasonCode: "UnexpectedToken", - template: "Unexpected token", - }, - ): empty { - if (isTokenType(messageOrType)) { - messageOrType = { + unexpected(loc?: ?Position, type?: ?TokenType): empty { + /* eslint-disable @babel/development-internal/dry-error-messages */ + throw this.raise( + { code: ErrorCodes.SyntaxError, reasonCode: "UnexpectedToken", - template: `Unexpected token, expected "${tokenLabelName( - // $FlowIgnore: Flow does not support assertion signature and TokenType is opaque - messageOrType, - )}"`, - }; - } - - /* eslint-disable @babel/development-internal/dry-error-messages */ - // $FlowIgnore: Flow does not support assertion signature and TokenType is opaque - throw this.raise(pos != null ? pos : this.state.start, messageOrType); + template: + type != null + ? `Unexpected token, expected "${tokenLabelName(type)}"` + : "Unexpected token", + }, + { at: loc != null ? loc : this.state.startLoc }, + ); /* eslint-enable @babel/development-internal/dry-error-messages */ } @@ -186,10 +195,10 @@ export default class UtilParser extends Tokenizer { }); } - expectPlugin(pluginConfig: PluginConfig, pos?: ?number): true { + expectPlugin(pluginConfig: PluginConfig, loc?: ?Position): true { if (!this.hasPlugin(pluginConfig)) { throw this.raiseWithData( - pos != null ? pos : this.state.start, + loc != null ? loc : this.state.startLoc, { missingPlugin: this.getPluginNamesFromConfigs([pluginConfig]) }, `This experimental syntax requires enabling the parser plugin: ${JSON.stringify( pluginConfig, @@ -200,10 +209,10 @@ export default class UtilParser extends Tokenizer { return true; } - expectOnePlugin(pluginConfigs: Array, pos?: ?number): void { + expectOnePlugin(pluginConfigs: Array): void { if (!pluginConfigs.some(c => this.hasPlugin(c))) { throw this.raiseWithData( - pos != null ? pos : this.state.start, + this.state.startLoc, { missingPlugin: this.getPluginNamesFromConfigs(pluginConfigs) }, `This experimental syntax requires enabling one of the following parser plugin(s): ${pluginConfigs .map(c => JSON.stringify(c)) @@ -273,24 +282,26 @@ export default class UtilParser extends Tokenizer { checkExpressionErrors( refExpressionErrors: ?ExpressionErrors, andThrow: boolean, - ) { - if (!refExpressionErrors) return false; - const { shorthandAssign, doubleProto, optionalParameters } = + ): void { + if (!andThrow || !refExpressionErrors) { + return; + } + + const { shorthandAssignLoc, doubleProtoLoc, optionalParametersLoc } = refExpressionErrors; - // shorthandAssign >= 0 || doubleProto >= 0 || optionalParameters >= 0 - const hasErrors = shorthandAssign + doubleProto + optionalParameters > -3; - if (!andThrow) { - return hasErrors; - } else if (hasErrors) { - if (shorthandAssign >= 0) { - this.raise(shorthandAssign, Errors.InvalidCoverInitializedName); - } - if (doubleProto >= 0) { - this.raise(doubleProto, Errors.DuplicateProto); - } - if (optionalParameters >= 0) { - this.unexpected(optionalParameters); - } + + if (shorthandAssignLoc != null) { + this.raise(Errors.InvalidCoverInitializedName, { + at: shorthandAssignLoc, + }); + } + + if (doubleProtoLoc != null) { + this.raise(Errors.DuplicateProto, { at: doubleProtoLoc }); + } + + if (optionalParametersLoc != null) { + this.unexpected(optionalParametersLoc); } } @@ -410,13 +421,13 @@ export default class UtilParser extends Tokenizer { * * Types of ExpressionErrors: * - * - **shorthandAssign**: track initializer `=` position - * - **doubleProto**: track the duplicate `__proto__` key position - * - **optionalParameters**: track the optional paramter (`?`). + * - **shorthandAssignLoc**: track initializer `=` position + * - **doubleProtoLoc**: track the duplicate `__proto__` key position + * - **optionalParametersLoc**: track the optional paramter (`?`). * It's only used by typescript and flow plugins */ export class ExpressionErrors { - shorthandAssign = -1; - doubleProto = -1; - optionalParameters = -1; + shorthandAssignLoc: ?Position = null; + doubleProtoLoc: ?Position = null; + optionalParametersLoc: ?Position = null; } diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index 38b97f76672d..471066293a70 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -83,17 +83,11 @@ export default (superClass: Class): Class => stmt.expression = this.finishNodeAt( expression, "Literal", - directiveLiteral.end, directiveLiteral.loc.end, ); stmt.directive = directiveLiteral.extra.raw.slice(1, -1); - return this.finishNodeAt( - stmt, - "ExpressionStatement", - directive.end, - directive.loc.end, - ); + return this.finishNodeAt(stmt, "ExpressionStatement", directive.loc.end); } // ================================== @@ -346,9 +340,9 @@ export default (superClass: Class): Class => toAssignableObjectExpressionProp(prop: N.Node, ...args) { if (prop.kind === "get" || prop.kind === "set") { - this.raise(prop.key.start, Errors.PatternHasAccessor); + this.raise(Errors.PatternHasAccessor, { node: prop.key }); } else if (prop.method) { - this.raise(prop.key.start, Errors.PatternHasMethod); + this.raise(Errors.PatternHasMethod, { node: prop.key }); } else { super.toAssignableObjectExpressionProp(prop, ...args); } diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index da4b5a8bb80a..07ea2109d1e8 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -17,7 +17,7 @@ import { tokenIsFlowInterfaceOrTypeOrOpaque, } from "../../tokenizer/types"; import * as N from "../../types"; -import type { Position } from "../../util/location"; +import { Position } from "../../util/location"; import { types as tc } from "../../tokenizer/context"; import * as charCodes from "charcodes"; import { isIteratorStart } from "../../util/identifier"; @@ -198,11 +198,11 @@ type EnumContext = {| memberName: string, |}; type EnumMemberInit = - | {| type: "number", pos: number, value: N.Node |} - | {| type: "string", pos: number, value: N.Node |} - | {| type: "boolean", pos: number, value: N.Node |} - | {| type: "invalid", pos: number |} - | {| type: "none", pos: number |}; + | {| type: "number", loc: Position, value: N.Node |} + | {| type: "string", loc: Position, value: N.Node |} + | {| type: "boolean", loc: Position, value: N.Node |} + | {| type: "invalid", loc: Position |} + | {| type: "none", loc: Position |}; export default (superClass: Class): Class => class extends superClass { @@ -265,12 +265,14 @@ export default (superClass: Class): Class => flowParsePredicate(): N.FlowType { const node = this.startNode(); - const moduloPos = this.state.start; + const moduloLoc = this.state.startLoc; this.next(); // eat `%` this.expectContextual(tt._checks); // Force '%' and 'checks' to be adjacent - if (this.state.lastTokStart > moduloPos + 1) { - this.raise(moduloPos, FlowErrors.UnexpectedSpaceBetweenModuloChecks); + if (this.state.lastTokStart > moduloLoc.index + 1) { + this.raise(FlowErrors.UnexpectedSpaceBetweenModuloChecks, { + at: moduloLoc, + }); } if (this.eat(tt.parenL)) { node.value = this.parseExpression(); @@ -346,7 +348,11 @@ export default (superClass: Class): Class => this.resetEndLocation(id); this.semicolon(); - this.scope.declareName(node.id.name, BIND_FLOW_DECLARE_FN, node.id.start); + this.scope.declareName( + node.id.name, + BIND_FLOW_DECLARE_FN, + node.id.loc.start, + ); return this.finishNode(node, "DeclareFunction"); } @@ -366,7 +372,9 @@ export default (superClass: Class): Class => return this.flowParseDeclareModuleExports(node); } else { if (insideModule) { - this.raise(this.state.lastTokStart, FlowErrors.NestedDeclareModule); + this.raise(FlowErrors.NestedDeclareModule, { + at: this.state.lastTokStartLoc, + }); } return this.flowParseDeclareModule(node); } @@ -390,7 +398,7 @@ export default (superClass: Class): Class => node.id = this.flowParseTypeAnnotatableIdentifier( /*allowPrimitiveOverride*/ true, ); - this.scope.declareName(node.id.name, BIND_VAR, node.id.start); + this.scope.declareName(node.id.name, BIND_VAR, node.id.loc.start); this.semicolon(); return this.finishNode(node, "DeclareVariable"); } @@ -413,10 +421,9 @@ export default (superClass: Class): Class => if (this.match(tt._import)) { this.next(); if (!this.isContextual(tt._type) && !this.match(tt._typeof)) { - this.raise( - this.state.lastTokStart, - FlowErrors.InvalidNonTypeImportInDeclareModule, - ); + this.raise(FlowErrors.InvalidNonTypeImportInDeclareModule, { + at: this.state.lastTokStartLoc, + }); } this.parseImport(bodyNode); } else { @@ -442,24 +449,21 @@ export default (superClass: Class): Class => body.forEach(bodyElement => { if (isEsModuleType(bodyElement)) { if (kind === "CommonJS") { - this.raise( - bodyElement.start, - FlowErrors.AmbiguousDeclareModuleKind, - ); + this.raise(FlowErrors.AmbiguousDeclareModuleKind, { + node: bodyElement, + }); } kind = "ES"; } else if (bodyElement.type === "DeclareModuleExports") { if (hasModuleExport) { - this.raise( - bodyElement.start, - FlowErrors.DuplicateDeclareModuleExports, - ); + this.raise(FlowErrors.DuplicateDeclareModuleExports, { + node: bodyElement, + }); } if (kind === "ES") { - this.raise( - bodyElement.start, - FlowErrors.AmbiguousDeclareModuleKind, - ); + this.raise(FlowErrors.AmbiguousDeclareModuleKind, { + node: bodyElement, + }); } kind = "CommonJS"; hasModuleExport = true; @@ -500,8 +504,8 @@ export default (superClass: Class): Class => const suggestion = exportSuggestions[label]; throw this.raise( - this.state.start, FlowErrors.UnsupportedDeclareExportKind, + { at: this.state.startLoc }, label, suggestion, ); @@ -597,7 +601,7 @@ export default (superClass: Class): Class => this.scope.declareName( node.id.name, isClass ? BIND_FUNCTION : BIND_LEXICAL, - node.id.start, + node.id.loc.start, ); if (this.match(tt.lt)) { @@ -659,18 +663,20 @@ export default (superClass: Class): Class => checkNotUnderscore(word: string) { if (word === "_") { - this.raise(this.state.start, FlowErrors.UnexpectedReservedUnderscore); + this.raise(FlowErrors.UnexpectedReservedUnderscore, { + at: this.state.startLoc, + }); } } - checkReservedType(word: string, startLoc: number, declaration?: boolean) { + checkReservedType(word: string, startLoc: Position, declaration?: boolean) { if (!reservedTypes.has(word)) return; this.raise( - startLoc, declaration ? FlowErrors.AssignReservedType : FlowErrors.UnexpectedReservedType, + { at: startLoc }, word, ); } @@ -679,7 +685,11 @@ export default (superClass: Class): Class => liberal?: boolean, declaration?: boolean, ): N.Identifier { - this.checkReservedType(this.state.value, this.state.start, declaration); + this.checkReservedType( + this.state.value, + this.state.startLoc, + declaration, + ); return this.parseIdentifier(liberal); } @@ -690,7 +700,7 @@ export default (superClass: Class): Class => /* liberal */ false, /* declaration */ true, ); - this.scope.declareName(node.id.name, BIND_LEXICAL, node.id.start); + this.scope.declareName(node.id.name, BIND_LEXICAL, node.id.loc.start); if (this.match(tt.lt)) { node.typeParameters = this.flowParseTypeParameterDeclaration(); @@ -713,7 +723,7 @@ export default (superClass: Class): Class => /* liberal */ true, /* declaration */ true, ); - this.scope.declareName(node.id.name, BIND_LEXICAL, node.id.start); + this.scope.declareName(node.id.name, BIND_LEXICAL, node.id.loc.start); if (this.match(tt.lt)) { node.typeParameters = this.flowParseTypeParameterDeclaration(); @@ -739,7 +749,7 @@ export default (superClass: Class): Class => // Type annotations flowParseTypeParameter(requireDefault?: boolean = false): N.TypeParameter { - const nodeStart = this.state.start; + const nodeStartLoc = this.state.startLoc; const node = this.startNode(); @@ -755,7 +765,7 @@ export default (superClass: Class): Class => node.default = this.flowParseType(); } else { if (requireDefault) { - this.raise(nodeStart, FlowErrors.MissingTypeParamDefault); + this.raise(FlowErrors.MissingTypeParamDefault, { at: nodeStartLoc }); } } @@ -1005,8 +1015,8 @@ export default (superClass: Class): Class => while (!this.match(endDelim)) { let isStatic = false; - let protoStart: ?number = null; - let inexactStart: ?number = null; + let protoStartLoc: ?Position = null; + let inexactStartLoc: ?Position = null; const node = this.startNode(); if (allowProto && this.isContextual(tt._proto)) { @@ -1014,7 +1024,7 @@ export default (superClass: Class): Class => if (lookahead.type !== tt.colon && lookahead.type !== tt.question) { this.next(); - protoStart = this.state.start; + protoStartLoc = this.state.startLoc; allowStatic = false; } } @@ -1032,12 +1042,12 @@ export default (superClass: Class): Class => const variance = this.flowParseVariance(); if (this.eat(tt.bracketL)) { - if (protoStart != null) { - this.unexpected(protoStart); + if (protoStartLoc != null) { + this.unexpected(protoStartLoc); } if (this.eat(tt.bracketL)) { if (variance) { - this.unexpected(variance.start); + this.unexpected(variance.loc.start); } nodeStart.internalSlots.push( this.flowParseObjectTypeInternalSlot(node, isStatic), @@ -1048,11 +1058,11 @@ export default (superClass: Class): Class => ); } } else if (this.match(tt.parenL) || this.match(tt.lt)) { - if (protoStart != null) { - this.unexpected(protoStart); + if (protoStartLoc != null) { + this.unexpected(protoStartLoc); } if (variance) { - this.unexpected(variance.start); + this.unexpected(variance.loc.start); } nodeStart.callProperties.push( this.flowParseObjectTypeCallProperty(node, isStatic), @@ -1071,7 +1081,7 @@ export default (superClass: Class): Class => const propOrInexact = this.flowParseObjectTypeProperty( node, isStatic, - protoStart, + protoStartLoc, variance, kind, allowSpread, @@ -1080,7 +1090,7 @@ export default (superClass: Class): Class => if (propOrInexact === null) { inexact = true; - inexactStart = this.state.lastTokStart; + inexactStartLoc = this.state.lastTokStartLoc; } else { nodeStart.properties.push(propOrInexact); } @@ -1089,14 +1099,13 @@ export default (superClass: Class): Class => this.flowObjectTypeSemicolon(); if ( - inexactStart && + inexactStartLoc && !this.match(tt.braceR) && !this.match(tt.braceBarR) ) { - this.raise( - inexactStart, - FlowErrors.UnexpectedExplicitInexactInObject, - ); + this.raise(FlowErrors.UnexpectedExplicitInexactInObject, { + at: inexactStartLoc, + }); } } @@ -1121,7 +1130,7 @@ export default (superClass: Class): Class => flowParseObjectTypeProperty( node: N.FlowObjectTypeProperty | N.FlowObjectTypeSpreadProperty, isStatic: boolean, - protoStart: ?number, + protoStartLoc: ?Position, variance: ?N.FlowVariance, kind: string, allowSpread: boolean, @@ -1136,28 +1145,31 @@ export default (superClass: Class): Class => if (isInexactToken) { if (!allowSpread) { - this.raise( - this.state.lastTokStart, - FlowErrors.InexactInsideNonObject, - ); + this.raise(FlowErrors.InexactInsideNonObject, { + at: this.state.lastTokStartLoc, + }); } else if (!allowInexact) { - this.raise(this.state.lastTokStart, FlowErrors.InexactInsideExact); + this.raise(FlowErrors.InexactInsideExact, { + at: this.state.lastTokStartLoc, + }); } if (variance) { - this.raise(variance.start, FlowErrors.InexactVariance); + this.raise(FlowErrors.InexactVariance, { node: variance }); } return null; } if (!allowSpread) { - this.raise(this.state.lastTokStart, FlowErrors.UnexpectedSpreadType); + this.raise(FlowErrors.UnexpectedSpreadType, { + at: this.state.lastTokStartLoc, + }); } - if (protoStart != null) { - this.unexpected(protoStart); + if (protoStartLoc != null) { + this.unexpected(protoStartLoc); } if (variance) { - this.raise(variance.start, FlowErrors.SpreadVariance); + this.raise(FlowErrors.SpreadVariance, { node: variance }); } node.argument = this.flowParseType(); @@ -1165,7 +1177,7 @@ export default (superClass: Class): Class => } else { node.key = this.flowParseObjectPropertyKey(); node.static = isStatic; - node.proto = protoStart != null; + node.proto = protoStartLoc != null; node.kind = kind; let optional = false; @@ -1173,11 +1185,11 @@ export default (superClass: Class): Class => // This is a method property node.method = true; - if (protoStart != null) { - this.unexpected(protoStart); + if (protoStartLoc != null) { + this.unexpected(protoStartLoc); } if (variance) { - this.unexpected(variance.start); + this.unexpected(variance.loc.start); } node.value = this.flowParseObjectTypeMethodish( @@ -1192,10 +1204,9 @@ export default (superClass: Class): Class => node.key.name === "constructor" && node.value.this ) { - this.raise( - node.value.this.start, - FlowErrors.ThisParamBannedInConstructor, - ); + this.raise(FlowErrors.ThisParamBannedInConstructor, { + node: node.value.this, + }); } } else { if (kind !== "init") this.unexpected(); @@ -1221,29 +1232,29 @@ export default (superClass: Class): Class => property: N.FlowObjectTypeProperty | N.FlowObjectTypeSpreadProperty, ): void { const paramCount = property.kind === "get" ? 0 : 1; - const start = property.start; const length = property.value.params.length + (property.value.rest ? 1 : 0); if (property.value.this) { this.raise( - property.value.this.start, property.kind === "get" ? FlowErrors.GetterMayNotHaveThisParam : FlowErrors.SetterMayNotHaveThisParam, + { node: property.value.this }, ); } if (length !== paramCount) { - if (property.kind === "get") { - this.raise(start, Errors.BadGetterArity); - } else { - this.raise(start, Errors.BadSetterArity); - } + this.raise( + property.kind === "get" + ? Errors.BadGetterArity + : Errors.BadSetterArity, + { node: property }, + ); } if (property.kind === "set" && property.value.rest) { - this.raise(start, Errors.BadSetterRestParameter); + this.raise(Errors.BadSetterRestParameter, { node: property }); } } @@ -1325,13 +1336,13 @@ export default (superClass: Class): Class => if (lh.type === tt.colon || lh.type === tt.question) { if (isThis && !first) { - this.raise(node.start, FlowErrors.ThisParamMustBeFirst); + this.raise(FlowErrors.ThisParamMustBeFirst, { node }); } name = this.parseIdentifier(isThis); if (this.eat(tt.question)) { optional = true; if (isThis) { - this.raise(node.start, FlowErrors.ThisParamMayNotBeOptional); + this.raise(FlowErrors.ThisParamMayNotBeOptional, { node }); } } typeAnnotation = this.flowParseTypeInitialiser(); @@ -1558,10 +1569,9 @@ export default (superClass: Class): Class => ); } - throw this.raise( - this.state.start, - FlowErrors.UnexpectedSubtractionOperand, - ); + throw this.raise(FlowErrors.UnexpectedSubtractionOperand, { + at: this.state.startLoc, + }); } throw this.unexpected(); @@ -1748,11 +1758,7 @@ export default (superClass: Class): Class => typeCastToParameter(node: N.Node): N.Node { node.expression.typeAnnotation = node.typeAnnotation; - this.resetEndLocation( - node.expression, - node.typeAnnotation.end, - node.typeAnnotation.loc.end, - ); + this.resetEndLocation(node.expression, node.typeAnnotation.loc.end); return node.expression; } @@ -1952,7 +1958,9 @@ export default (superClass: Class): Class => // e.g. Source: a ? (b): c => (d): e => f // Result 1: a ? b : (c => ((d): e => f)) // Result 2: a ? ((b): c => d) : (e => f) - this.raise(state.start, FlowErrors.AmbiguousConditionalArrow); + this.raise(FlowErrors.AmbiguousConditionalArrow, { + at: state.startLoc, + }); } if (failed && valid.length === 1) { @@ -2036,7 +2044,7 @@ export default (superClass: Class): Class => // node.params is Expression[] instead of $ReadOnlyArray because it // has not been converted yet. ((node.params: any): N.Expression[]), - node.extra?.trailingComma, + node.extra?.trailingCommaLoc, /* isLHS */ false, ); // Enter scope, as checkParams defines bindings @@ -2165,10 +2173,10 @@ export default (superClass: Class): Class => } maybeParseExportNamespaceSpecifier(node: N.Node): boolean { - const pos = this.state.start; + const { startLoc } = this.state; const hasNamespace = super.maybeParseExportNamespaceSpecifier(node); if (hasNamespace && node.exportKind === "type") { - this.unexpected(pos); + this.unexpected(startLoc); } return hasNamespace; } @@ -2185,7 +2193,7 @@ export default (superClass: Class): Class => member: any, state: N.ParseClassMemberState, ): void { - const pos = this.state.start; + const { startLoc } = this.state; if (this.isContextual(tt._declare)) { if (this.parseClassMemberFromModifier(classBody, member)) { // 'declare' is a class element name @@ -2203,12 +2211,11 @@ export default (superClass: Class): Class => member.type !== "ClassPrivateProperty" && member.type !== "PropertyDefinition" // Used by estree plugin ) { - this.raise(pos, FlowErrors.DeclareClassElement); + this.raise(FlowErrors.DeclareClassElement, { at: startLoc }); } else if (member.value) { - this.raise( - member.value.start, - FlowErrors.DeclareClassFieldInitializer, - ); + this.raise(FlowErrors.DeclareClassFieldInitializer, { + node: member.value, + }); } } } @@ -2223,7 +2230,11 @@ export default (superClass: Class): Class => // Allow @@iterator and @@asyncIterator as a identifier only inside type if (!this.isIterator(word) || !this.state.inType) { - this.raise(this.state.pos, Errors.InvalidIdentifier, fullWord); + this.raise( + Errors.InvalidIdentifier, + { at: this.state.curPosition() }, + fullWord, + ); } this.finishToken(tt.name, fullWord); @@ -2272,7 +2283,7 @@ export default (superClass: Class): Class => // turn type casts that we found in function parameter head into type annotated params toAssignableList( exprList: N.Expression[], - trailingCommaPos?: ?number, + trailingCommaLoc?: ?Position, isLHS: boolean, ): $ReadOnlyArray { for (let i = 0; i < exprList.length; i++) { @@ -2281,7 +2292,7 @@ export default (superClass: Class): Class => exprList[i] = this.typeCastToParameter(expr); } } - return super.toAssignableList(exprList, trailingCommaPos, isLHS); + return super.toAssignableList(exprList, trailingCommaLoc, isLHS); } // this is a list of nodes, from something like a call expression, we need to filter the @@ -2298,7 +2309,9 @@ export default (superClass: Class): Class => !expr.extra?.parenthesized && (exprList.length > 1 || !isParenthesizedExpr) ) { - this.raise(expr.typeAnnotation.start, FlowErrors.TypeCastInPattern); + this.raise(FlowErrors.TypeCastInPattern, { + node: expr.typeAnnotation, + }); } } @@ -2388,7 +2401,7 @@ export default (superClass: Class): Class => allowsDirectSuper: boolean, ): void { if ((method: $FlowFixMe).variance) { - this.unexpected((method: $FlowFixMe).variance.start); + this.unexpected((method: $FlowFixMe).variance.loc.start); } delete (method: $FlowFixMe).variance; if (this.match(tt.lt)) { @@ -2407,7 +2420,7 @@ export default (superClass: Class): Class => if (method.params && isConstructor) { const params = method.params; if (params.length > 0 && this.isThisParam(params[0])) { - this.raise(method.start, FlowErrors.ThisParamBannedInConstructor); + this.raise(FlowErrors.ThisParamBannedInConstructor, { node: method }); } // estree support } else if ( @@ -2418,7 +2431,7 @@ export default (superClass: Class): Class => ) { const params = method.value.params; if (params.length > 0 && this.isThisParam(params[0])) { - this.raise(method.start, FlowErrors.ThisParamBannedInConstructor); + this.raise(FlowErrors.ThisParamBannedInConstructor, { node: method }); } } } @@ -2430,7 +2443,7 @@ export default (superClass: Class): Class => isAsync: boolean, ): void { if ((method: $FlowFixMe).variance) { - this.unexpected((method: $FlowFixMe).variance.start); + this.unexpected((method: $FlowFixMe).variance.loc.start); } delete (method: $FlowFixMe).variance; if (this.match(tt.lt)) { @@ -2468,9 +2481,9 @@ export default (superClass: Class): Class => if (params.length > 0) { const param = params[0]; if (this.isThisParam(param) && method.kind === "get") { - this.raise(param.start, FlowErrors.GetterMayNotHaveThisParam); + this.raise(FlowErrors.GetterMayNotHaveThisParam, { node: param }); } else if (this.isThisParam(param)) { - this.raise(param.start, FlowErrors.SetterMayNotHaveThisParam); + this.raise(FlowErrors.SetterMayNotHaveThisParam, { node: param }); } } } @@ -2493,7 +2506,7 @@ export default (superClass: Class): Class => refExpressionErrors: ?ExpressionErrors, ): void { if ((prop: $FlowFixMe).variance) { - this.unexpected((prop: $FlowFixMe).variance.start); + this.unexpected((prop: $FlowFixMe).variance.loc.start); } delete (prop: $FlowFixMe).variance; @@ -2525,10 +2538,10 @@ export default (superClass: Class): Class => parseAssignableListItemTypes(param: N.Pattern): N.Pattern { if (this.eat(tt.question)) { if (param.type !== "Identifier") { - this.raise(param.start, FlowErrors.PatternIsOptional); + this.raise(FlowErrors.PatternIsOptional, { node: param }); } if (this.isThisParam(param)) { - this.raise(param.start, FlowErrors.ThisParamMayNotBeOptional); + this.raise(FlowErrors.ThisParamMayNotBeOptional, { node: param }); } ((param: any): N.Identifier).optional = true; @@ -2536,11 +2549,11 @@ export default (superClass: Class): Class => if (this.match(tt.colon)) { param.typeAnnotation = this.flowParseTypeAnnotation(); } else if (this.isThisParam(param)) { - this.raise(param.start, FlowErrors.ThisParamAnnotationRequired); + this.raise(FlowErrors.ThisParamAnnotationRequired, { node: param }); } if (this.match(tt.eq) && this.isThisParam(param)) { - this.raise(param.start, FlowErrors.ThisParamNoDefault); + this.raise(FlowErrors.ThisParamNoDefault, { node: param }); } this.resetEndLocation(param); @@ -2559,7 +2572,9 @@ export default (superClass: Class): Class => node.typeAnnotation && node.right.start < node.typeAnnotation.start ) { - this.raise(node.typeAnnotation.start, FlowErrors.TypeBeforeInitializer); + this.raise(FlowErrors.TypeBeforeInitializer, { + node: node.typeAnnotation, + }); } return node; @@ -2606,7 +2621,8 @@ export default (superClass: Class): Class => // import type * is not allowed if (kind === "type" && type === tt.star) { - this.unexpected(lh.start); + // FIXME: lh.start? + this.unexpected(null, lh.type); } if ( @@ -2670,8 +2686,8 @@ export default (superClass: Class): Class => if (importedIsString) { /*:: invariant(firstIdent instanceof N.StringLiteral) */ throw this.raise( - specifier.start, Errors.ImportBindingIsString, + { node: specifier }, firstIdent.value, ); } @@ -2691,16 +2707,15 @@ export default (superClass: Class): Class => const specifierIsTypeImport = hasTypeImportKind(specifier); if (isInTypeOnlyImport && specifierIsTypeImport) { - this.raise( - specifier.start, - FlowErrors.ImportTypeShorthandOnlyInPureImport, - ); + this.raise(FlowErrors.ImportTypeShorthandOnlyInPureImport, { + node: specifier, + }); } if (isInTypeOnlyImport || specifierIsTypeImport) { this.checkReservedType( specifier.local.name, - specifier.local.start, + specifier.local.loc.start, /* declaration */ true, ); } @@ -2708,7 +2723,7 @@ export default (superClass: Class): Class => if (isBinding && !isInTypeOnlyImport && !specifierIsTypeImport) { this.checkReservedWord( specifier.local.name, - specifier.start, + specifier.loc.start, true, true, ); @@ -2867,8 +2882,8 @@ export default (superClass: Class): Class => if (arrow.node.async) { /*:: invariant(typeParameters) */ this.raise( - typeParameters.start, FlowErrors.UnexpectedTypeParameterBeforeAsyncArrowFunction, + { node: typeParameters }, ); } @@ -2900,10 +2915,9 @@ export default (superClass: Class): Class => if (arrow.thrown) throw arrow.error; /*:: invariant(typeParameters) */ - throw this.raise( - typeParameters.start, - FlowErrors.UnexpectedTokenAfterTypeParameter, - ); + throw this.raise(FlowErrors.UnexpectedTokenAfterTypeParameter, { + node: typeParameters, + }); } return super.parseMaybeAssign(refExpressionErrors, afterLeftParse); @@ -2977,7 +2991,7 @@ export default (superClass: Class): Class => // ensure the `this` param is first, if it exists for (let i = 0; i < node.params.length; i++) { if (this.isThisParam(node.params[i]) && i > 0) { - this.raise(node.params[i].start, FlowErrors.ThisParamMustBeFirst); + this.raise(FlowErrors.ThisParamMustBeFirst, { node: node.params[i] }); } } @@ -3161,7 +3175,9 @@ export default (superClass: Class): Class => parseTopLevel(file: N.File, program: N.Program): N.File { const fileNode = super.parseTopLevel(file, program); if (this.state.hasFlowComment) { - this.raise(this.state.pos, FlowErrors.UnterminatedFlowComment); + this.raise(FlowErrors.UnterminatedFlowComment, { + at: this.state.curPosition(), + }); } return fileNode; } @@ -3169,7 +3185,9 @@ export default (superClass: Class): Class => skipBlockComment(): N.CommentBlock | void { if (this.hasPlugin("flowComments") && this.skipFlowComment()) { if (this.state.hasFlowComment) { - this.unexpected(null, FlowErrors.NestedFlowComment); + throw this.raise(FlowErrors.NestedFlowComment, { + at: this.state.startLoc, + }); } this.hasFlowCommentCompletion(); this.state.pos += this.skipFlowComment(); @@ -3178,11 +3196,13 @@ export default (superClass: Class): Class => } if (this.state.hasFlowComment) { - const end = this.input.indexOf("*-/", (this.state.pos += 2)); + const end = this.input.indexOf("*-/", this.state.pos + 2); if (end === -1) { - throw this.raise(this.state.pos - 2, Errors.UnterminatedComment); + throw this.raise(Errors.UnterminatedComment, { + at: this.state.curPosition(), + }); } - this.state.pos = end + 3; + this.state.pos = end + 2 + 3; return; } @@ -3223,130 +3243,102 @@ export default (superClass: Class): Class => hasFlowCommentCompletion(): void { const end = this.input.indexOf("*/", this.state.pos); if (end === -1) { - throw this.raise(this.state.pos, Errors.UnterminatedComment); + throw this.raise(Errors.UnterminatedComment, { + at: this.state.curPosition(), + }); } } // Flow enum parsing flowEnumErrorBooleanMemberNotInitialized( - pos: number, + loc: Position, { enumName, memberName }: { enumName: string, memberName: string }, ): void { this.raise( - pos, FlowErrors.EnumBooleanMemberNotInitialized, + { at: loc }, memberName, enumName, ); } - flowEnumErrorInvalidMemberName( - pos: number, - { enumName, memberName }: { enumName: string, memberName: string }, - ): void { - const suggestion = memberName[0].toUpperCase() + memberName.slice(1); - this.raise( - pos, - FlowErrors.EnumInvalidMemberName, - memberName, - suggestion, - enumName, - ); - } - - flowEnumErrorDuplicateMemberName( - pos: number, - { enumName, memberName }: { enumName: string, memberName: string }, - ): void { - this.raise(pos, FlowErrors.EnumDuplicateMemberName, memberName, enumName); - } - - flowEnumErrorInconsistentMemberValues( - pos: number, - { enumName }: { enumName: string }, - ): void { - this.raise(pos, FlowErrors.EnumInconsistentMemberValues, enumName); - } - flowEnumErrorInvalidExplicitType( - pos: number, + loc: Position, { enumName, suppliedType, }: { enumName: string, suppliedType: null | string }, ) { return this.raise( - pos, suppliedType === null ? FlowErrors.EnumInvalidExplicitTypeUnknownSupplied : FlowErrors.EnumInvalidExplicitType, + { at: loc }, enumName, suppliedType, ); } flowEnumErrorInvalidMemberInitializer( - pos: number, + loc: Position, { enumName, explicitType, memberName }: EnumContext, ) { - let message = null; - switch (explicitType) { - case "boolean": - case "number": - case "string": - message = FlowErrors.EnumInvalidMemberInitializerPrimaryType; - break; - case "symbol": - message = FlowErrors.EnumInvalidMemberInitializerSymbolType; - break; - default: - // null - message = FlowErrors.EnumInvalidMemberInitializerUnknownType; - } - return this.raise(pos, message, enumName, memberName, explicitType); + return this.raise( + explicitType === "boolean" || + explicitType === "number" || + explicitType === "string" + ? FlowErrors.EnumInvalidMemberInitializerPrimaryType + : explicitType === "symbol" + ? FlowErrors.EnumInvalidMemberInitializerSymbolType + : FlowErrors.EnumInvalidMemberInitializerUnknownType, + { at: loc }, + enumName, + memberName, + explicitType, + ); } flowEnumErrorNumberMemberNotInitialized( - pos: number, + loc: Position, { enumName, memberName }: { enumName: string, memberName: string }, ): void { this.raise( - pos, FlowErrors.EnumNumberMemberNotInitialized, + { at: loc }, enumName, memberName, ); } flowEnumErrorStringMemberInconsistentlyInitailized( - pos: number, + node: N.Node, { enumName }: { enumName: string }, ): void { this.raise( - pos, FlowErrors.EnumStringMemberInconsistentlyInitailized, + { node }, enumName, ); } flowEnumMemberInit(): EnumMemberInit { - const startPos = this.state.start; + const startLoc = this.state.startLoc; const endOfInit = () => this.match(tt.comma) || this.match(tt.braceR); switch (this.state.type) { case tt.num: { const literal = this.parseNumericLiteral(this.state.value); if (endOfInit()) { - return { type: "number", pos: literal.start, value: literal }; + return { type: "number", loc: literal.loc.start, value: literal }; } - return { type: "invalid", pos: startPos }; + return { type: "invalid", loc: startLoc }; } case tt.string: { const literal = this.parseStringLiteral(this.state.value); if (endOfInit()) { - return { type: "string", pos: literal.start, value: literal }; + return { type: "string", loc: literal.loc.start, value: literal }; } - return { type: "invalid", pos: startPos }; + return { type: "invalid", loc: startLoc }; } case tt._true: case tt._false: { @@ -3354,28 +3346,28 @@ export default (superClass: Class): Class => if (endOfInit()) { return { type: "boolean", - pos: literal.start, + loc: literal.loc.start, value: literal, }; } - return { type: "invalid", pos: startPos }; + return { type: "invalid", loc: startLoc }; } default: - return { type: "invalid", pos: startPos }; + return { type: "invalid", loc: startLoc }; } } flowEnumMemberRaw(): { id: N.Node, init: EnumMemberInit } { - const pos = this.state.start; + const loc = this.state.startLoc; const id = this.parseIdentifier(true); const init = this.eat(tt.eq) ? this.flowEnumMemberInit() - : { type: "none", pos }; + : { type: "none", loc }; return { id, init }; } flowEnumCheckExplicitTypeMismatch( - pos: number, + loc: Position, context: EnumContext, expectedType: EnumExplicitType, ): void { @@ -3384,7 +3376,7 @@ export default (superClass: Class): Class => return; } if (explicitType !== expectedType) { - this.flowEnumErrorInvalidMemberInitializer(pos, context); + this.flowEnumErrorInvalidMemberInitializer(loc, context); } } @@ -3423,16 +3415,22 @@ export default (superClass: Class): Class => continue; } if (/^[a-z]/.test(memberName)) { - this.flowEnumErrorInvalidMemberName(id.start, { - enumName, + this.raise( + FlowErrors.EnumInvalidMemberName, + { node: id }, memberName, - }); + // suggestion + memberName[0].toUpperCase() + memberName.slice(1), + enumName, + ); } if (seenNames.has(memberName)) { - this.flowEnumErrorDuplicateMemberName(id.start, { - enumName, + this.raise( + FlowErrors.EnumDuplicateMemberName, + { node: id }, memberName, - }); + enumName, + ); } seenNames.add(memberName); const context = { enumName, explicitType, memberName }; @@ -3440,7 +3438,7 @@ export default (superClass: Class): Class => switch (init.type) { case "boolean": { this.flowEnumCheckExplicitTypeMismatch( - init.pos, + init.loc, context, "boolean", ); @@ -3451,7 +3449,7 @@ export default (superClass: Class): Class => break; } case "number": { - this.flowEnumCheckExplicitTypeMismatch(init.pos, context, "number"); + this.flowEnumCheckExplicitTypeMismatch(init.loc, context, "number"); memberNode.init = init.value; members.numberMembers.push( this.finishNode(memberNode, "EnumNumberMember"), @@ -3459,7 +3457,7 @@ export default (superClass: Class): Class => break; } case "string": { - this.flowEnumCheckExplicitTypeMismatch(init.pos, context, "string"); + this.flowEnumCheckExplicitTypeMismatch(init.loc, context, "string"); memberNode.init = init.value; members.stringMembers.push( this.finishNode(memberNode, "EnumStringMember"), @@ -3467,18 +3465,18 @@ export default (superClass: Class): Class => break; } case "invalid": { - throw this.flowEnumErrorInvalidMemberInitializer(init.pos, context); + throw this.flowEnumErrorInvalidMemberInitializer(init.loc, context); } case "none": { switch (explicitType) { case "boolean": this.flowEnumErrorBooleanMemberNotInitialized( - init.pos, + init.loc, context, ); break; case "number": - this.flowEnumErrorNumberMemberNotInitialized(init.pos, context); + this.flowEnumErrorNumberMemberNotInitialized(init.loc, context); break; default: members.defaultedMembers.push( @@ -3506,18 +3504,16 @@ export default (superClass: Class): Class => return initializedMembers; } else if (defaultedMembers.length > initializedMembers.length) { for (const member of initializedMembers) { - this.flowEnumErrorStringMemberInconsistentlyInitailized( - member.start, - { enumName }, - ); + this.flowEnumErrorStringMemberInconsistentlyInitailized(member, { + enumName, + }); } return defaultedMembers; } else { for (const member of defaultedMembers) { - this.flowEnumErrorStringMemberInconsistentlyInitailized( - member.start, - { enumName }, - ); + this.flowEnumErrorStringMemberInconsistentlyInitailized(member, { + enumName, + }); } return initializedMembers; } @@ -3530,7 +3526,7 @@ export default (superClass: Class): Class => }): EnumExplicitType { if (this.eatContextual(tt._of)) { if (!tokenIsIdentifier(this.state.type)) { - throw this.flowEnumErrorInvalidExplicitType(this.state.start, { + throw this.flowEnumErrorInvalidExplicitType(this.state.startLoc, { enumName, suppliedType: null, }); @@ -3545,7 +3541,7 @@ export default (superClass: Class): Class => value !== "string" && value !== "symbol" ) { - this.flowEnumErrorInvalidExplicitType(this.state.start, { + this.flowEnumErrorInvalidExplicitType(this.state.startLoc, { enumName, suppliedType: value, }); @@ -3556,7 +3552,9 @@ export default (superClass: Class): Class => return null; } - flowEnumBody(node: N.Node, { enumName, nameLoc }): N.Node { + flowEnumBody(node: N.Node, id: N.Node): N.Node { + const enumName = id.name; + const nameLoc = id.loc.start; const explicitType = this.flowEnumParseExplicitType({ enumName }); this.expect(tt.braceL); const { members, hasUnknownMembers } = this.flowEnumMembers({ @@ -3615,7 +3613,7 @@ export default (superClass: Class): Class => return this.finishNode(node, "EnumStringBody"); } else if (!numsLen && !strsLen && boolsLen >= defaultedLen) { for (const member of members.defaultedMembers) { - this.flowEnumErrorBooleanMemberNotInitialized(member.start, { + this.flowEnumErrorBooleanMemberNotInitialized(member.loc.start, { enumName, memberName: member.id.name, }); @@ -3625,7 +3623,7 @@ export default (superClass: Class): Class => return this.finishNode(node, "EnumBooleanBody"); } else if (!boolsLen && !strsLen && numsLen >= defaultedLen) { for (const member of members.defaultedMembers) { - this.flowEnumErrorNumberMemberNotInitialized(member.start, { + this.flowEnumErrorNumberMemberNotInitialized(member.loc.start, { enumName, memberName: member.id.name, }); @@ -3634,7 +3632,11 @@ export default (superClass: Class): Class => this.expect(tt.braceR); return this.finishNode(node, "EnumNumberBody"); } else { - this.flowEnumErrorInconsistentMemberValues(nameLoc, { enumName }); + this.raise( + FlowErrors.EnumInconsistentMemberValues, + { at: nameLoc }, + enumName, + ); return empty(); } } @@ -3644,10 +3646,7 @@ export default (superClass: Class): Class => flowParseEnumDeclaration(node: N.Node): N.Node { const id = this.parseIdentifier(); node.id = id; - node.body = this.flowEnumBody(this.startNode(), { - enumName: id.name, - nameLoc: id.start, - }); + node.body = this.flowEnumBody(this.startNode(), id); return this.finishNode(node, "EnumDeclaration"); } diff --git a/packages/babel-parser/src/plugins/flow/scope.js b/packages/babel-parser/src/plugins/flow/scope.js index f849c29c00c9..418276b59df0 100644 --- a/packages/babel-parser/src/plugins/flow/scope.js +++ b/packages/babel-parser/src/plugins/flow/scope.js @@ -1,5 +1,6 @@ // @flow +import { Position } from "../../util/location"; import ScopeHandler, { Scope } from "../../util/scope"; import { BIND_FLAGS_FLOW_DECLARE_FN, @@ -19,10 +20,10 @@ export default class FlowScopeHandler extends ScopeHandler { return new FlowScope(flags); } - declareName(name: string, bindingType: BindingTypes, pos: number) { + declareName(name: string, bindingType: BindingTypes, loc: Position) { const scope = this.currentScope(); if (bindingType & BIND_FLAGS_FLOW_DECLARE_FN) { - this.checkRedeclarationInScope(scope, name, bindingType, pos); + this.checkRedeclarationInScope(scope, name, bindingType, loc); this.maybeExportDefined(scope, name); scope.declareFunctions.add(name); return; diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index d2ecb7306e88..257fb76ed5a8 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -87,7 +87,9 @@ export default (superClass: Class): Class => let chunkStart = this.state.pos; for (;;) { if (this.state.pos >= this.length) { - throw this.raise(this.state.start, JsxErrors.UnterminatedJsxContent); + throw this.raise(JsxErrors.UnterminatedJsxContent, { + at: this.state.startLoc, + }); } const ch = this.input.charCodeAt(this.state.pos); @@ -117,11 +119,14 @@ export default (superClass: Class): Class => const htmlEntity = ch === charCodes.rightCurlyBrace ? "}" : ">"; const char = this.input[this.state.pos]; - this.raise(this.state.pos, { - code: ErrorCodes.SyntaxError, - reasonCode: "UnexpectedToken", - template: `Unexpected token \`${char}\`. Did you mean \`${htmlEntity}\` or \`{'${char}'}\`?`, - }); + this.raise( + { + code: ErrorCodes.SyntaxError, + reasonCode: "UnexpectedToken", + template: `Unexpected token \`${char}\`. Did you mean \`${htmlEntity}\` or \`{'${char}'}\`?`, + }, + { at: this.state.curPosition() }, + ); } /* falls through */ @@ -161,7 +166,9 @@ export default (superClass: Class): Class => let chunkStart = ++this.state.pos; for (;;) { if (this.state.pos >= this.length) { - throw this.raise(this.state.start, Errors.UnterminatedString); + throw this.raise(Errors.UnterminatedString, { + at: this.state.startLoc, + }); } const ch = this.input.charCodeAt(this.state.pos); @@ -299,7 +306,7 @@ export default (superClass: Class): Class => this.next(); node = this.jsxParseExpressionContainer(node, tc.j_oTag); if (node.expression.type === "JSXEmptyExpression") { - this.raise(node.start, JsxErrors.AttributeIsEmpty); + this.raise(JsxErrors.AttributeIsEmpty, { node }); } return node; @@ -308,7 +315,9 @@ export default (superClass: Class): Class => return this.parseExprAtom(); default: - throw this.raise(this.state.start, JsxErrors.UnsupportedJsxValue); + throw this.raise(JsxErrors.UnsupportedJsxValue, { + at: this.state.startLoc, + }); } } @@ -318,15 +327,10 @@ export default (superClass: Class): Class => jsxParseEmptyExpression(): N.JSXEmptyExpression { const node = this.startNodeAt( - this.state.lastTokEnd, + this.state.lastTokEndLoc.index, this.state.lastTokEndLoc, ); - return this.finishNodeAt( - node, - "JSXEmptyExpression", - this.state.start, - this.state.startLoc, - ); + return this.finishNodeAt(node, "JSXEmptyExpression", this.state.startLoc); } // Parse JSX spread child @@ -356,10 +360,9 @@ export default (superClass: Class): Class => expression.type === "SequenceExpression" && !expression.extra?.parenthesized ) { - this.raise( - expression.expressions[1].start, - JsxErrors.UnexpectedSequenceExpression, - ); + this.raise(JsxErrors.UnexpectedSequenceExpression, { + node: expression.expressions[1], + }); } } @@ -483,17 +486,19 @@ export default (superClass: Class): Class => } } - if (isFragment(openingElement) && !isFragment(closingElement)) { - this.raise( - // $FlowIgnore - closingElement.start, - JsxErrors.MissingClosingTagFragment, - ); + if ( + isFragment(openingElement) && + !isFragment(closingElement) && + closingElement !== null + ) { + this.raise(JsxErrors.MissingClosingTagFragment, { + node: closingElement, + }); } else if (!isFragment(openingElement) && isFragment(closingElement)) { this.raise( - // $FlowIgnore - closingElement.start, JsxErrors.MissingClosingTagElement, + // $FlowIgnore + { node: closingElement }, getQualifiedJSXName(openingElement.name), ); } else if (!isFragment(openingElement) && !isFragment(closingElement)) { @@ -503,9 +508,9 @@ export default (superClass: Class): Class => getQualifiedJSXName(openingElement.name) ) { this.raise( - // $FlowIgnore - closingElement.start, JsxErrors.MissingClosingTagElement, + // $FlowIgnore + { node: closingElement }, getQualifiedJSXName(openingElement.name), ); } @@ -521,10 +526,9 @@ export default (superClass: Class): Class => } node.children = children; if (this.match(tt.lt)) { - throw this.raise( - this.state.start, - JsxErrors.UnwrappedAdjacentJSXElements, - ); + throw this.raise(JsxErrors.UnwrappedAdjacentJSXElements, { + at: this.state.startLoc, + }); } return isFragment(openingElement) diff --git a/packages/babel-parser/src/plugins/placeholders.js b/packages/babel-parser/src/plugins/placeholders.js index 16f10782a6f4..c89f395f3345 100644 --- a/packages/babel-parser/src/plugins/placeholders.js +++ b/packages/babel-parser/src/plugins/placeholders.js @@ -1,5 +1,8 @@ // @flow +// Error messages are colocated with the plugin. +/* eslint-disable @babel/development-internal/dry-error-messages */ + import * as charCodes from "charcodes"; import { tokenLabelName, tt } from "../tokenizer/types"; @@ -46,12 +49,15 @@ type NodeOf = $Switch< // the substituted nodes. type MaybePlaceholder = NodeOf; // | Placeholder -const PlaceHolderErrors = makeErrorTemplates( +/* eslint sort-keys: "error" */ +const PlaceholderErrors = makeErrorTemplates( { ClassNameIsRequired: "A class name is required.", }, /* code */ ErrorCodes.SyntaxError, + /* syntaxPlugin */ "placeholders", ); +/* eslint-disable sort-keys */ export default (superClass: Class): Class => class extends superClass { @@ -246,7 +252,9 @@ export default (superClass: Class): Class => node.body = this.finishPlaceholder(placeholder, "ClassBody"); return this.finishNode(node, type); } else { - this.unexpected(null, PlaceHolderErrors.ClassNameIsRequired); + throw this.raise(PlaceholderErrors.ClassNameIsRequired, { + at: this.state.startLoc, + }); } } else { this.parseClassId(node, isStatement, optionalId); diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 835d24c6c058..1673c1e3e27f 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -283,22 +283,27 @@ export default (superClass: Class): Class => errorTemplate?: ErrorTemplate, stopOnStartOfClassStaticBlock?: boolean, ): void { - const enforceOrder = (pos, modifier, before, after) => { + const enforceOrder = (loc, modifier, before, after) => { if (modifier === before && modified[after]) { - this.raise(pos, TSErrors.InvalidModifiersOrder, before, after); + this.raise( + TSErrors.InvalidModifiersOrder, + { at: loc }, + before, + after, + ); } }; - const incompatible = (pos, modifier, mod1, mod2) => { + const incompatible = (loc, modifier, mod1, mod2) => { if ( (modified[mod1] && modifier === mod2) || (modified[mod2] && modifier === mod1) ) { - this.raise(pos, TSErrors.IncompatibleModifiers, mod1, mod2); + this.raise(TSErrors.IncompatibleModifiers, { at: loc }, mod1, mod2); } }; for (;;) { - const startPos = this.state.start; + const { startLoc } = this.state; const modifier: ?TsModifier = this.tsParseModifier( allowedModifiers.concat(disallowedModifiers ?? []), stopOnStartOfClassStaticBlock, @@ -308,36 +313,34 @@ export default (superClass: Class): Class => if (tsIsAccessModifier(modifier)) { if (modified.accessibility) { - this.raise(startPos, TSErrors.DuplicateAccessibilityModifier); + this.raise(TSErrors.DuplicateAccessibilityModifier, { + at: startLoc, + }); } else { - enforceOrder(startPos, modifier, modifier, "override"); - enforceOrder(startPos, modifier, modifier, "static"); - enforceOrder(startPos, modifier, modifier, "readonly"); + enforceOrder(startLoc, modifier, modifier, "override"); + enforceOrder(startLoc, modifier, modifier, "static"); + enforceOrder(startLoc, modifier, modifier, "readonly"); modified.accessibility = modifier; } } else { if (Object.hasOwnProperty.call(modified, modifier)) { - this.raise(startPos, TSErrors.DuplicateModifier, modifier); + this.raise(TSErrors.DuplicateModifier, { at: startLoc }, modifier); } else { - enforceOrder(startPos, modifier, "static", "readonly"); - enforceOrder(startPos, modifier, "static", "override"); - enforceOrder(startPos, modifier, "override", "readonly"); - enforceOrder(startPos, modifier, "abstract", "override"); + enforceOrder(startLoc, modifier, "static", "readonly"); + enforceOrder(startLoc, modifier, "static", "override"); + enforceOrder(startLoc, modifier, "override", "readonly"); + enforceOrder(startLoc, modifier, "abstract", "override"); - incompatible(startPos, modifier, "declare", "override"); - incompatible(startPos, modifier, "static", "abstract"); + incompatible(startLoc, modifier, "declare", "override"); + incompatible(startLoc, modifier, "static", "abstract"); } modified[modifier] = true; } if (disallowedModifiers?.includes(modifier)) { - this.raise( - startPos, - // $FlowIgnore - errorTemplate, - modifier, - ); + // $FlowIgnore + this.raise(errorTemplate, { at: startLoc }, modifier); } } } @@ -465,7 +468,9 @@ export default (superClass: Class): Class => this.expect(tt._import); this.expect(tt.parenL); if (!this.match(tt.string)) { - this.raise(this.state.start, TSErrors.UnsupportedImportTypeArgument); + this.raise(TSErrors.UnsupportedImportTypeArgument, { + at: this.state.startLoc, + }); } // For compatibility to estree we cannot call parseLiteral directly here @@ -560,7 +565,7 @@ export default (superClass: Class): Class => refTrailingCommaPos, ); if (node.params.length === 0) { - this.raise(node.start, TSErrors.EmptyTypeParameters); + this.raise(TSErrors.EmptyTypeParameters, { node }); } if (refTrailingCommaPos.value !== -1) { this.addExtra(node, "trailingComma", refTrailingCommaPos.value); @@ -615,8 +620,8 @@ export default (superClass: Class): Class => pattern.type !== "ArrayPattern" ) { this.raise( - pattern.start, TSErrors.UnsupportedSignatureParameterKind, + { node: pattern }, pattern.type, ); } @@ -682,11 +687,13 @@ export default (superClass: Class): Class => if (this.match(tt.parenL) || this.match(tt.lt)) { if (readonly) { - this.raise(node.start, TSErrors.ReadonlyForMethodSignature); + this.raise(TSErrors.ReadonlyForMethodSignature, { node }); } const method: N.TsMethodSignature = nodeAny; if (method.kind && this.match(tt.lt)) { - this.raise(this.state.pos, TSErrors.AccesorCannotHaveTypeParameters); + this.raise(TSErrors.AccesorCannotHaveTypeParameters, { + at: this.state.curPosition(), + }); } this.tsFillSignature(tt.colon, method); this.tsParseTypeMemberSemicolon(); @@ -698,46 +705,41 @@ export default (superClass: Class): Class => : "typeAnnotation"; if (method.kind === "get") { if (method[paramsKey].length > 0) { - this.raise(this.state.pos, Errors.BadGetterArity); + this.raise(Errors.BadGetterArity, { at: this.state.curPosition() }); if (this.isThisParam(method[paramsKey][0])) { - this.raise( - this.state.pos, - TSErrors.AccesorCannotDeclareThisParameter, - ); + this.raise(TSErrors.AccesorCannotDeclareThisParameter, { + at: this.state.curPosition(), + }); } } } else if (method.kind === "set") { if (method[paramsKey].length !== 1) { - this.raise(this.state.pos, Errors.BadSetterArity); + this.raise(Errors.BadSetterArity, { at: this.state.curPosition() }); } else { const firstParameter = method[paramsKey][0]; if (this.isThisParam(firstParameter)) { - this.raise( - this.state.pos, - TSErrors.AccesorCannotDeclareThisParameter, - ); + this.raise(TSErrors.AccesorCannotDeclareThisParameter, { + at: this.state.curPosition(), + }); } if ( firstParameter.type === "Identifier" && firstParameter.optional ) { - this.raise( - this.state.pos, - TSErrors.SetAccesorCannotHaveOptionalParameter, - ); + this.raise(TSErrors.SetAccesorCannotHaveOptionalParameter, { + at: this.state.curPosition(), + }); } if (firstParameter.type === "RestElement") { - this.raise( - this.state.pos, - TSErrors.SetAccesorCannotHaveRestParameter, - ); + this.raise(TSErrors.SetAccesorCannotHaveRestParameter, { + at: this.state.curPosition(), + }); } } if (method[returnTypeKey]) { - this.raise( - method[returnTypeKey].start, - TSErrors.SetAccesorCannotHaveReturnType, - ); + this.raise(TSErrors.SetAccesorCannotHaveReturnType, { + node: method[returnTypeKey], + }); } } else { method.kind = "method"; @@ -905,7 +907,9 @@ export default (superClass: Class): Class => type !== "TSOptionalType" && !(type === "TSNamedTupleMember" && elementNode.optional) ) { - this.raise(elementNode.start, TSErrors.OptionalTypeBeforeRequired); + this.raise(TSErrors.OptionalTypeBeforeRequired, { + node: elementNode, + }); } // Flow doesn't support ||= @@ -924,10 +928,9 @@ export default (superClass: Class): Class => // Flow doesn't support ??= labeledElements = labeledElements ?? isLabeled; if (labeledElements !== isLabeled) { - this.raise( - elementNode.start, - TSErrors.MixedLabeledAndUnlabeledElements, - ); + this.raise(TSErrors.MixedLabeledAndUnlabeledElements, { + node: elementNode, + }); } }); @@ -955,7 +958,7 @@ export default (superClass: Class): Class => ) { labeledNode.label = (type.typeName: N.Identifier); } else { - this.raise(type.start, TSErrors.InvalidTupleMemberLabel); + this.raise(TSErrors.InvalidTupleMemberLabel, { node: type }); // This produces an invalid AST, but at least we don't drop // nodes representing the invalid source. // $FlowIgnore @@ -1156,7 +1159,7 @@ export default (superClass: Class): Class => case "TSArrayType": return; default: - this.raise(node.start, TSErrors.UnexpectedReadonly); + this.raise(TSErrors.UnexpectedReadonly, { node }); } } @@ -1387,8 +1390,8 @@ export default (superClass: Class): Class => if (containsEsc) { this.raise( - this.state.lastTokStart, Errors.InvalidEscapedReservedWord, + { at: this.state.lastTokStartLoc }, "asserts", ); } @@ -1450,7 +1453,7 @@ export default (superClass: Class): Class => tsParseTypeAssertion(): N.TsTypeAssertion { if (this.getPluginOption("typescript", "disallowAmbiguousJSXLike")) { - this.raise(this.state.start, TSErrors.ReservedTypeAssertion); + this.raise(TSErrors.ReservedTypeAssertion, { at: this.state.startLoc }); } const node: N.TsTypeAssertion = this.startNode(); @@ -1464,7 +1467,7 @@ export default (superClass: Class): Class => tsParseHeritageClause( descriptor: string, ): $ReadOnlyArray { - const originalStart = this.state.start; + const originalStartLoc = this.state.startLoc; const delimitedList = this.tsParseDelimitedList( "HeritageClauseElement", @@ -1472,7 +1475,11 @@ export default (superClass: Class): Class => ); if (!delimitedList.length) { - this.raise(originalStart, TSErrors.EmptyHeritageClauseType, descriptor); + this.raise( + TSErrors.EmptyHeritageClauseType, + { at: originalStartLoc }, + descriptor, + ); } return delimitedList; @@ -1502,7 +1509,7 @@ export default (superClass: Class): Class => ); } else { node.id = null; - this.raise(this.state.start, TSErrors.MissingInterfaceName); + this.raise(TSErrors.MissingInterfaceName, { at: this.state.startLoc }); } node.typeParameters = this.tsTryParseTypeParameters(); @@ -1699,7 +1706,9 @@ export default (superClass: Class): Class => node.importKind === "type" && moduleReference.type !== "TSExternalModuleReference" ) { - this.raise(moduleReference.start, TSErrors.ImportAliasHasImportType); + this.raise(TSErrors.ImportAliasHasImportType, { + node: moduleReference, + }); } node.moduleReference = moduleReference; this.semicolon(); @@ -1978,7 +1987,7 @@ export default (superClass: Class): Class => }), ); if (node.params.length === 0) { - this.raise(node.start, TSErrors.EmptyTypeArguments); + this.raise(TSErrors.EmptyTypeArguments, { node }); } this.expect(tt.gt); return this.finishNode(node, "TSTypeParameterInstantiation"); @@ -2024,7 +2033,7 @@ export default (superClass: Class): Class => allowModifiers === false && (accessibility || readonly || override) ) { - this.raise(startPos, TSErrors.UnexpectedParameterModifier); + this.raise(TSErrors.UnexpectedParameterModifier, { at: startLoc }); } } @@ -2040,7 +2049,7 @@ export default (superClass: Class): Class => if (readonly) pp.readonly = readonly; if (override) pp.override = override; if (elt.type !== "Identifier" && elt.type !== "AssignmentPattern") { - this.raise(pp.start, TSErrors.UnsupportedParameterPropertyKind); + this.raise(TSErrors.UnsupportedParameterPropertyKind, { node: pp }); } pp.parameter = ((elt: any): N.Identifier | N.AssignmentPattern); return this.finishNode(pp, "TSParameterProperty"); @@ -2073,7 +2082,7 @@ export default (superClass: Class): Class => return; } if (bodilessType === "TSDeclareFunction" && this.state.isAmbientContext) { - this.raise(node.start, TSErrors.DeclareFunctionHasImplementation); + this.raise(TSErrors.DeclareFunctionHasImplementation, { node }); if ( // $FlowIgnore node.declare @@ -2099,10 +2108,9 @@ export default (superClass: Class): Class => tsCheckForInvalidTypeCasts(items: $ReadOnlyArray) { items.forEach(node => { if (node?.type === "TSTypeCastExpression") { - this.raise( - node.typeAnnotation.start, - TSErrors.UnexpectedTypeAnnotation, - ); + this.raise(TSErrors.UnexpectedTypeAnnotation, { + node: node.typeAnnotation, + }); } }); } @@ -2165,7 +2173,7 @@ export default (superClass: Class): Class => } if (this.match(tt.lt)) { - let missingParenErrorPos; + let missingParenErrorLoc; // tsTryParseAndCatch is expensive, so avoid if not necessary. // There are number of things we are going to "maybe" parse, like type arguments on // tagged template expressions. If any of them fail, walk it back and continue. @@ -2189,7 +2197,7 @@ export default (superClass: Class): Class => if (typeArguments) { if (isOptionalCall && !this.match(tt.parenL)) { - missingParenErrorPos = this.state.pos; + missingParenErrorLoc = this.state.curPosition(); this.unexpected(); } @@ -2226,8 +2234,8 @@ export default (superClass: Class): Class => this.unexpected(); }); - if (missingParenErrorPos) { - this.unexpected(missingParenErrorPos, tt.parenL); + if (missingParenErrorLoc) { + this.unexpected(missingParenErrorLoc, tt.parenL); } if (result) return result; @@ -2286,7 +2294,7 @@ export default (superClass: Class): Class => checkReservedWord( word: string, // eslint-disable-line no-unused-vars - startLoc: number, // eslint-disable-line no-unused-vars + startLoc: Position, // eslint-disable-line no-unused-vars checkKeywords: boolean, // eslint-disable-line no-unused-vars // eslint-disable-next-line no-unused-vars isBinding: boolean, @@ -2345,10 +2353,9 @@ export default (superClass: Class): Class => importNode.specifiers.length > 1 && importNode.specifiers[0].type === "ImportDefaultSpecifier" ) { - this.raise( - importNode.start, - TSErrors.TypeImportCannotSpecifyDefaultAndNamed, - ); + this.raise(TSErrors.TypeImportCannotSpecifyDefaultAndNamed, { + node: importNode, + }); } return importNode; @@ -2485,7 +2492,9 @@ export default (superClass: Class): Class => this.next(); // eat "static" this.next(); // eat "{" if (this.tsHasSomeModifiers(member, modifiers)) { - this.raise(this.state.pos, TSErrors.StaticBlockCannotHaveModifier); + this.raise(TSErrors.StaticBlockCannotHaveModifier, { + at: this.state.curPosition(), + }); } this.parseClassStaticBlock(classBody, ((member: any): N.StaticBlock)); } else { @@ -2515,32 +2524,34 @@ export default (superClass: Class): Class => classBody.body.push(idx); if ((member: any).abstract) { - this.raise(member.start, TSErrors.IndexSignatureHasAbstract); + this.raise(TSErrors.IndexSignatureHasAbstract, { node: member }); } if ((member: any).accessibility) { this.raise( - member.start, TSErrors.IndexSignatureHasAccessibility, + { node: member }, (member: any).accessibility, ); } if ((member: any).declare) { - this.raise(member.start, TSErrors.IndexSignatureHasDeclare); + this.raise(TSErrors.IndexSignatureHasDeclare, { node: member }); } if ((member: any).override) { - this.raise(member.start, TSErrors.IndexSignatureHasOverride); + this.raise(TSErrors.IndexSignatureHasOverride, { node: member }); } return; } if (!this.state.inAbstractClass && (member: any).abstract) { - this.raise(member.start, TSErrors.NonAbstractClassHasAbstractMethod); + this.raise(TSErrors.NonAbstractClassHasAbstractMethod, { + node: member, + }); } if ((member: any).override) { if (!state.hadSuperClass) { - this.raise(member.start, TSErrors.OverrideNotInSubClass); + this.raise(TSErrors.OverrideNotInSubClass, { node: member }); } } @@ -2556,11 +2567,11 @@ export default (superClass: Class): Class => if (optional) methodOrProp.optional = true; if ((methodOrProp: any).readonly && this.match(tt.parenL)) { - this.raise(methodOrProp.start, TSErrors.ClassMethodHasReadonly); + this.raise(TSErrors.ClassMethodHasReadonly, { node: methodOrProp }); } if ((methodOrProp: any).declare && this.match(tt.parenL)) { - this.raise(methodOrProp.start, TSErrors.ClassMethodHasDeclare); + this.raise(TSErrors.ClassMethodHasDeclare, { node: methodOrProp }); } } @@ -2662,10 +2673,9 @@ export default (superClass: Class): Class => isDeclare && (this.isContextual(tt._declare) || !this.shouldParseExportDeclaration()) ) { - throw this.raise( - this.state.start, - TSErrors.ExpectedAmbientAfterExportDeclare, - ); + throw this.raise(TSErrors.ExpectedAmbientAfterExportDeclare, { + at: this.state.startLoc, + }); } let declaration: ?N.Declaration; @@ -2729,13 +2739,15 @@ export default (superClass: Class): Class => this.parseClassPropertyAnnotation(node); if (this.state.isAmbientContext && this.match(tt.eq)) { - this.raise(this.state.start, TSErrors.DeclareClassFieldHasInitializer); + this.raise(TSErrors.DeclareClassFieldHasInitializer, { + at: this.state.startLoc, + }); } if (node.abstract && this.match(tt.eq)) { const { key } = node; this.raise( - this.state.start, TSErrors.AbstractPropertyHasInitializer, + { at: this.state.startLoc }, key.type === "Identifier" && !node.computed ? key.name : `[${this.input.slice(key.start, key.end)}]`, @@ -2750,14 +2762,14 @@ export default (superClass: Class): Class => ): N.ClassPrivateProperty { // $FlowIgnore if (node.abstract) { - this.raise(node.start, TSErrors.PrivateElementHasAbstract); + this.raise(TSErrors.PrivateElementHasAbstract, { node }); } // $FlowIgnore if (node.accessibility) { this.raise( - node.start, TSErrors.PrivateElementHasAccessibility, + { node }, node.accessibility, ); } @@ -2776,12 +2788,14 @@ export default (superClass: Class): Class => ): void { const typeParameters = this.tsTryParseTypeParameters(); if (typeParameters && isConstructor) { - this.raise(typeParameters.start, TSErrors.ConstructorHasTypeParameters); + this.raise(TSErrors.ConstructorHasTypeParameters, { + node: typeParameters, + }); } // $FlowIgnore if (method.declare && (method.kind === "get" || method.kind === "set")) { - this.raise(method.start, TSErrors.DeclareAccessor, method.kind); + this.raise(TSErrors.DeclareAccessor, { node: method }, method.kind); } if (typeParameters) method.typeParameters = typeParameters; super.pushClassMethod( @@ -2985,7 +2999,7 @@ export default (superClass: Class): Class => !node.extra?.trailingComma && this.getPluginOption("typescript", "disallowAmbiguousJSXLike") ) { - this.raise(node.start, TSErrors.ReservedArrowTypeParam); + this.raise(TSErrors.ReservedArrowTypeParam, { node }); } } @@ -3030,7 +3044,7 @@ export default (superClass: Class): Class => !this.state.isAmbientContext && !this.state.inType ) { - this.raise(param.start, TSErrors.PatternIsOptional); + this.raise(TSErrors.PatternIsOptional, { node: param }); } ((param: any): N.Identifier).optional = true; @@ -3107,7 +3121,7 @@ export default (superClass: Class): Class => contextDescription !== "parenthesized expression" && !expr.extra?.parenthesized ) { - this.raise(expr.start, Errors.InvalidLhs, contextDescription); + this.raise(Errors.InvalidLhs, { node: expr }, contextDescription); break; } this.checkLVal(expr.expression, "parenthesized expression", ...args); @@ -3141,7 +3155,7 @@ export default (superClass: Class): Class => return call; } - this.unexpected(this.state.start, tt.parenL); + this.unexpected(null, tt.parenL); } return super.parseMaybeDecoratorArguments(expr); @@ -3182,10 +3196,9 @@ export default (superClass: Class): Class => node.typeAnnotation && node.right.start < node.typeAnnotation.start ) { - this.raise( - node.typeAnnotation.start, - TSErrors.TypeAnnotationAfterAssign, - ); + this.raise(TSErrors.TypeAnnotationAfterAssign, { + node: node.typeAnnotation, + }); } return node; @@ -3229,7 +3242,9 @@ export default (superClass: Class): Class => if (!this.state.maybeInArrowParameters) { exprList[i] = this.typeCastToParameter(expr); } else { - this.raise(expr.start, TSErrors.UnexpectedTypeCastInParameter); + this.raise(TSErrors.UnexpectedTypeCastInParameter, { + node: expr, + }); } break; } @@ -3240,11 +3255,7 @@ export default (superClass: Class): Class => typeCastToParameter(node: N.TsTypeCastExpression): N.Node { node.expression.typeAnnotation = node.typeAnnotation; - this.resetEndLocation( - node.expression, - node.typeAnnotation.end, - node.typeAnnotation.loc.end, - ); + this.resetEndLocation(node.expression, node.typeAnnotation.loc.end); return node.expression; } @@ -3338,10 +3349,9 @@ export default (superClass: Class): Class => // Foo {} if (!this.hasFollowingLineBreak()) { node.abstract = true; - this.raise( - node.start, - TSErrors.NonClassMethodPropertyHasAbstractModifer, - ); + this.raise(TSErrors.NonClassMethodPropertyHasAbstractModifer, { + node, + }); this.next(); return this.tsParseInterfaceDeclaration( (node: N.TsInterfaceDeclaration), @@ -3361,8 +3371,8 @@ export default (superClass: Class): Class => if (hasBody) { const { key } = method; this.raise( - method.start, TSErrors.AbstractMethodHasImplementation, + { node: method }, key.type === "Identifier" && !method.computed ? key.name : `[${this.input.slice(key.start, key.end)}]`, @@ -3455,7 +3465,7 @@ export default (superClass: Class): Class => let hasTypeSpecifier = false; let canParseAsKeyword = true; - const pos = leftOfAs.start; + const loc = leftOfAs.loc.start; // https://github.com/microsoft/TypeScript/blob/fc4f9d83d5939047aa6bb2a43965c6e9bbfbc35b/src/compiler/parser.ts#L7411-L7456 // import { type } from "mod"; - hasTypeSpecifier: false, leftOfAs: type @@ -3495,10 +3505,10 @@ export default (superClass: Class): Class => } if (hasTypeSpecifier && isInTypeOnlyImportExport) { this.raise( - pos, isImport ? TSErrors.TypeModifierIsUsedInTypeImports : TSErrors.TypeModifierIsUsedInTypeExports, + { at: loc }, ); } diff --git a/packages/babel-parser/src/plugins/typescript/scope.js b/packages/babel-parser/src/plugins/typescript/scope.js index 657a3109af20..0019ac9cdf1c 100644 --- a/packages/babel-parser/src/plugins/typescript/scope.js +++ b/packages/babel-parser/src/plugins/typescript/scope.js @@ -1,5 +1,6 @@ // @flow +import { Position } from "../../util/location"; import ScopeHandler, { Scope } from "../../util/scope"; import { BIND_KIND_TYPE, @@ -40,7 +41,7 @@ export default class TypeScriptScopeHandler extends ScopeHandler): Class => class extends superClass { parseV8Intrinsic(): N.Expression { if (this.match(tt.modulo)) { - const v8IntrinsicStart = this.state.start; + const v8IntrinsicStartLoc = this.state.startLoc; // let the `loc` of Identifier starts from `%` const node = this.startNode(); this.next(); // eat '%' @@ -18,7 +18,7 @@ export default (superClass: Class): Class => return identifier; } } - this.unexpected(v8IntrinsicStart); + this.unexpected(v8IntrinsicStartLoc); } } diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 9a647a68775d..1e198600f865 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -3,6 +3,7 @@ /*:: declare var invariant; */ import type { Options } from "../options"; +import { Position, createPositionWithColumnOffset } from "../util/location"; import * as N from "../types"; import * as charCodes from "charcodes"; import { isIdentifierStart, isIdentifierChar } from "../util/identifier"; @@ -128,8 +129,8 @@ export default class Tokenizer extends ParserErrors { // parser/util.js /*:: +hasPrecedingLineBreak: () => boolean; - +unexpected: (pos?: ?number, messageOrType?: ErrorTemplate | TokenType) => empty; - +expectPlugin: (name: string, pos?: ?number) => true; + +unexpected: (loc?: ?Position, type?: TokenType) => empty; + +expectPlugin: (name: string, loc?: Position) => true; */ isLookahead: boolean; @@ -162,7 +163,6 @@ export default class Tokenizer extends ParserErrors { this.pushToken(new Token(this.state)); } - this.state.lastTokEnd = this.state.end; this.state.lastTokStart = this.state.start; this.state.lastTokEndLoc = this.state.endLoc; this.state.lastTokStartLoc = this.state.startLoc; @@ -205,9 +205,13 @@ export default class Tokenizer extends ParserErrors { type: state.type, start: state.start, end: state.end, - lastTokEnd: state.end, context: [this.curContext()], inType: state.inType, + startLoc: state.startLoc, + lastTokEndLoc: state.lastTokEndLoc, + curLine: state.curLine, + lineStart: state.lineStart, + curPosition: state.curPosition, }; } @@ -280,9 +284,9 @@ export default class Tokenizer extends ParserErrors { // after a "use strict" directive. Strict mode will be set at parse // time for any literals that occur after the next node of the strict // directive. - this.state.strictErrors.forEach((message, pos) => + this.state.strictErrors.forEach(({ message, loc }) => /* eslint-disable @babel/development-internal/dry-error-messages */ - this.raise(pos, message), + this.raise(message, { at: loc }), ); this.state.strictErrors.clear(); } @@ -312,7 +316,14 @@ export default class Tokenizer extends ParserErrors { if (!this.isLookahead) startLoc = this.state.curPosition(); const start = this.state.pos; const end = this.input.indexOf("*/", start + 2); - if (end === -1) throw this.raise(start, Errors.UnterminatedComment); + if (end === -1) { + // We have to call this again here because startLoc may not be set... + // This seems to be for performance reasons: + // https://github.com/babel/babel/commit/acf2a10899f696a8aaf34df78bf9725b5ea7f2da + throw this.raise(Errors.UnterminatedComment, { + at: this.state.curPosition(), + }); + } this.state.pos = end + 2; lineBreakG.lastIndex = start + 2; @@ -483,12 +494,12 @@ export default class Tokenizer extends ParserErrors { finishToken(type: TokenType, val: any): void { this.state.end = this.state.pos; + this.state.endLoc = this.state.curPosition(); const prevType = this.state.type; this.state.type = type; this.state.value = val; if (!this.isLookahead) { - this.state.endLoc = this.state.curPosition(); this.updateContext(prevType); } } @@ -519,7 +530,9 @@ export default class Tokenizer extends ParserErrors { const nextPos = this.state.pos + 1; const next = this.codePointAtPos(nextPos); if (next >= charCodes.digit0 && next <= charCodes.digit9) { - throw this.raise(this.state.pos, Errors.UnexpectedDigitAfterHash); + throw this.raise(Errors.UnexpectedDigitAfterHash, { + at: this.state.curPosition(), + }); } if ( @@ -533,10 +546,10 @@ export default class Tokenizer extends ParserErrors { this.expectPlugin("recordAndTuple"); if (this.getPluginOption("recordAndTuple", "syntaxType") !== "hash") { throw this.raise( - this.state.pos, next === charCodes.leftCurlyBrace ? Errors.RecordExpressionHashIncorrectStartSyntaxType : Errors.TupleExpressionHashIncorrectStartSyntaxType, + { at: this.state.curPosition() }, ); } @@ -660,10 +673,9 @@ export default class Tokenizer extends ParserErrors { next === charCodes.rightCurlyBrace ) { if (this.getPluginOption("recordAndTuple", "syntaxType") !== "bar") { - throw this.raise( - this.state.pos, - Errors.RecordExpressionBarIncorrectEndSyntaxType, - ); + throw this.raise(Errors.RecordExpressionBarIncorrectEndSyntaxType, { + at: this.state.curPosition(), + }); } this.state.pos += 2; this.finishToken(tt.braceBarR); @@ -676,10 +688,9 @@ export default class Tokenizer extends ParserErrors { next === charCodes.rightSquareBracket ) { if (this.getPluginOption("recordAndTuple", "syntaxType") !== "bar") { - throw this.raise( - this.state.pos, - Errors.TupleExpressionBarIncorrectEndSyntaxType, - ); + throw this.raise(Errors.TupleExpressionBarIncorrectEndSyntaxType, { + at: this.state.curPosition(), + }); } this.state.pos += 2; this.finishToken(tt.bracketBarR); @@ -857,8 +868,8 @@ export default class Tokenizer extends ParserErrors { ) { if (this.getPluginOption("recordAndTuple", "syntaxType") !== "bar") { throw this.raise( - this.state.pos, Errors.TupleExpressionBarIncorrectStartSyntaxType, + { at: this.state.curPosition() }, ); } @@ -881,8 +892,8 @@ export default class Tokenizer extends ParserErrors { ) { if (this.getPluginOption("recordAndTuple", "syntaxType") !== "bar") { throw this.raise( - this.state.pos, Errors.RecordExpressionBarIncorrectStartSyntaxType, + { at: this.state.curPosition() }, ); } @@ -1023,8 +1034,8 @@ export default class Tokenizer extends ParserErrors { } throw this.raise( - this.state.pos, Errors.InvalidOrUnexpectedToken, + { at: this.state.curPosition() }, String.fromCodePoint(code), ); } @@ -1036,16 +1047,22 @@ export default class Tokenizer extends ParserErrors { } readRegexp(): void { + const startLoc = this.state.startLoc; const start = this.state.start + 1; let escaped, inClass; let { pos } = this.state; for (; ; ++pos) { if (pos >= this.length) { - throw this.raise(start, Errors.UnterminatedRegExp); + // FIXME: explain + throw this.raise(Errors.UnterminatedRegExp, { + at: createPositionWithColumnOffset(startLoc, 1), + }); } const ch = this.input.charCodeAt(pos); if (isNewLine(ch)) { - throw this.raise(start, Errors.UnterminatedRegExp); + throw this.raise(Errors.UnterminatedRegExp, { + at: createPositionWithColumnOffset(startLoc, 1), + }); } if (escaped) { escaped = false; @@ -1072,10 +1089,15 @@ export default class Tokenizer extends ParserErrors { if (VALID_REGEX_FLAGS.has(cp)) { if (mods.includes(char)) { - this.raise(pos + 1, Errors.DuplicateRegExpFlags); + // (pos + 1) + 1 - start + this.raise(Errors.DuplicateRegExpFlags, { + at: createPositionWithColumnOffset(startLoc, pos + 2 - start), + }); } } else if (isIdentifierChar(cp) || cp === charCodes.backslash) { - this.raise(pos + 1, Errors.MalformedRegExpFlags); + this.raise(Errors.MalformedRegExpFlags, { + at: createPositionWithColumnOffset(startLoc, pos + 2 - start), + }); } else { break; } @@ -1131,17 +1153,23 @@ export default class Tokenizer extends ParserErrors { const prev = this.input.charCodeAt(this.state.pos - 1); const next = this.input.charCodeAt(this.state.pos + 1); if (allowedSiblings.indexOf(next) === -1) { - this.raise(this.state.pos, Errors.UnexpectedNumericSeparator); + this.raise(Errors.UnexpectedNumericSeparator, { + at: this.state.curPosition(), + }); } else if ( forbiddenSiblings.indexOf(prev) > -1 || forbiddenSiblings.indexOf(next) > -1 || Number.isNaN(next) ) { - this.raise(this.state.pos, Errors.UnexpectedNumericSeparator); + this.raise(Errors.UnexpectedNumericSeparator, { + at: this.state.curPosition(), + }); } if (!allowNumSeparator) { - this.raise(this.state.pos, Errors.NumericSeparatorInEscapeSequence); + this.raise(Errors.NumericSeparatorInEscapeSequence, { + at: this.state.curPosition(), + }); } // Ignore this _ character @@ -1164,7 +1192,11 @@ export default class Tokenizer extends ParserErrors { if (this.options.errorRecovery && val <= 9) { val = 0; - this.raise(this.state.start + i + 2, Errors.InvalidDigit, radix); + this.raise( + Errors.InvalidDigit, + { at: this.state.curPosition() }, + radix, + ); } else if (forceLen) { val = 0; invalid = true; @@ -1187,13 +1219,18 @@ export default class Tokenizer extends ParserErrors { } readRadixNumber(radix: number): void { - const start = this.state.pos; + const startLoc = this.state.curPosition(); let isBigInt = false; this.state.pos += 2; // 0x const val = this.readInt(radix); if (val == null) { - this.raise(this.state.start + 2, Errors.InvalidDigit, radix); + this.raise( + Errors.InvalidDigit, + // Numeric literals can't have newlines, so this is safe to do. + { at: createPositionWithColumnOffset(startLoc, 2) }, + radix, + ); } const next = this.input.charCodeAt(this.state.pos); @@ -1201,15 +1238,19 @@ export default class Tokenizer extends ParserErrors { ++this.state.pos; isBigInt = true; } else if (next === charCodes.lowercaseM) { - throw this.raise(start, Errors.InvalidDecimal); + throw this.raise(Errors.InvalidDecimal, { at: startLoc }); } if (isIdentifierStart(this.codePointAtPos(this.state.pos))) { - throw this.raise(this.state.pos, Errors.NumberIdentifier); + throw this.raise(Errors.NumberIdentifier, { + at: this.state.curPosition(), + }); } if (isBigInt) { - const str = this.input.slice(start, this.state.pos).replace(/[_n]/g, ""); + const str = this.input + .slice(startLoc.index, this.state.pos) + .replace(/[_n]/g, ""); this.finishToken(tt.bigint, str); return; } @@ -1221,6 +1262,7 @@ export default class Tokenizer extends ParserErrors { readNumber(startsWithDot: boolean): void { const start = this.state.pos; + const startLoc = this.state.curPosition(); let isFloat = false; let isBigInt = false; let isDecimal = false; @@ -1228,7 +1270,7 @@ export default class Tokenizer extends ParserErrors { let isOctal = false; if (!startsWithDot && this.readInt(10) === null) { - this.raise(start, Errors.InvalidNumber); + this.raise(Errors.InvalidNumber, { at: this.state.curPosition() }); } const hasLeadingZero = this.state.pos - start >= 2 && @@ -1236,12 +1278,15 @@ export default class Tokenizer extends ParserErrors { if (hasLeadingZero) { const integer = this.input.slice(start, this.state.pos); - this.recordStrictModeErrors(start, Errors.StrictOctalLiteral); + this.recordStrictModeErrors(Errors.StrictOctalLiteral, startLoc); if (!this.state.strict) { // disallow numeric separators in non octal decimals and legacy octal likes const underscorePos = integer.indexOf("_"); if (underscorePos > 0) { - this.raise(underscorePos + start, Errors.ZeroDigitNumericSeparator); + // Numeric literals can't have newlines, so this is safe to do. + this.raise(Errors.ZeroDigitNumericSeparator, { + at: createPositionWithColumnOffset(startLoc, underscorePos), + }); } } isOctal = hasLeadingZero && !/[89]/.test(integer); @@ -1264,7 +1309,7 @@ export default class Tokenizer extends ParserErrors { ++this.state.pos; } if (this.readInt(10) === null) { - this.raise(start, Errors.InvalidOrMissingExponent); + this.raise(Errors.InvalidOrMissingExponent, { at: startLoc }); } isFloat = true; hasExponent = true; @@ -1275,23 +1320,25 @@ export default class Tokenizer extends ParserErrors { // disallow floats, legacy octal syntax and non octal decimals // new style octal ("0o") is handled in this.readRadixNumber if (isFloat || hasLeadingZero) { - this.raise(start, Errors.InvalidBigIntLiteral); + this.raise(Errors.InvalidBigIntLiteral, { at: startLoc }); } ++this.state.pos; isBigInt = true; } if (next === charCodes.lowercaseM) { - this.expectPlugin("decimal", this.state.pos); + this.expectPlugin("decimal", this.state.curPosition()); if (hasExponent || hasLeadingZero) { - this.raise(start, Errors.InvalidDecimal); + this.raise(Errors.InvalidDecimal, { at: startLoc }); } ++this.state.pos; isDecimal = true; } if (isIdentifierStart(this.codePointAtPos(this.state.pos))) { - throw this.raise(this.state.pos, Errors.NumberIdentifier); + throw this.raise(Errors.NumberIdentifier, { + at: this.state.curPosition(), + }); } // remove "_" for numeric literal separator, and trailing `m` or `n` @@ -1318,7 +1365,7 @@ export default class Tokenizer extends ParserErrors { let code; if (ch === charCodes.leftCurlyBrace) { - const codePos = ++this.state.pos; + ++this.state.pos; code = this.readHexChar( this.input.indexOf("}", this.state.pos) - this.state.pos, true, @@ -1327,7 +1374,7 @@ export default class Tokenizer extends ParserErrors { ++this.state.pos; if (code !== null && code > 0x10ffff) { if (throwOnInvalid) { - this.raise(codePos, Errors.InvalidCodePoint); + this.raise(Errors.InvalidCodePoint, { at: this.state.curPosition() }); } else { return null; } @@ -1343,7 +1390,9 @@ export default class Tokenizer extends ParserErrors { chunkStart = ++this.state.pos; for (;;) { if (this.state.pos >= this.length) { - throw this.raise(this.state.start, Errors.UnterminatedString); + throw this.raise(Errors.UnterminatedString, { + at: this.state.startLoc, + }); } const ch = this.input.charCodeAt(this.state.pos); if (ch === quote) break; @@ -1360,7 +1409,9 @@ export default class Tokenizer extends ParserErrors { ++this.state.curLine; this.state.lineStart = this.state.pos; } else if (isNewLine(ch)) { - throw this.raise(this.state.start, Errors.UnterminatedString); + throw this.raise(Errors.UnterminatedString, { + at: this.state.startLoc, + }); } else { ++this.state.pos; } @@ -1372,7 +1423,7 @@ export default class Tokenizer extends ParserErrors { // Reads tempalte continuation `}...` readTemplateContinuation(): void { if (!this.match(tt.braceR)) { - this.unexpected(this.state.start, tt.braceR); + this.unexpected(null, tt.braceR); } // rewind pos to `}` this.state.pos--; @@ -1387,7 +1438,10 @@ export default class Tokenizer extends ParserErrors { ++this.state.pos; // eat '`' or `}` for (;;) { if (this.state.pos >= this.length) { - throw this.raise(this.state.start + 1, Errors.UnterminatedTemplate); + // FIXME: explain + throw this.raise(Errors.UnterminatedTemplate, { + at: createPositionWithColumnOffset(this.state.startLoc, 1), + }); } const ch = this.input.charCodeAt(this.state.pos); if (ch === charCodes.graveAccent) { @@ -1439,11 +1493,11 @@ export default class Tokenizer extends ParserErrors { } } - recordStrictModeErrors(pos: number, message: ErrorTemplate) { - if (this.state.strict && !this.state.strictErrors.has(pos)) { - this.raise(pos, message); + recordStrictModeErrors(message: ErrorTemplate, loc: Position) { + if (this.state.strict && !this.state.strictErrors.has(loc.index)) { + this.raise(message, { at: loc }); } else { - this.state.strictErrors.set(pos, message); + this.state.strictErrors.set(loc.index, { loc, message }); } } @@ -1491,14 +1545,21 @@ export default class Tokenizer extends ParserErrors { return null; } else { this.recordStrictModeErrors( - this.state.pos - 1, Errors.StrictNumericEscape, + // We immediately follow a "\\", and we're an 8 or a 9, so we must + // be on the same line. + createPositionWithColumnOffset(this.state.curPosition(), -1), ); } // fall through default: if (ch >= charCodes.digit0 && ch <= charCodes.digit7) { - const codePos = this.state.pos - 1; + // We immediately follow a "\\", and we're something between 0 and 7, + // so we must be on the same line. + const codePos = createPositionWithColumnOffset( + this.state.curPosition(), + -1, + ); const match = this.input .substr(this.state.pos - 1, 3) .match(/^[0-7]+/); @@ -1522,7 +1583,7 @@ export default class Tokenizer extends ParserErrors { if (inTemplate) { return null; } else { - this.recordStrictModeErrors(codePos, Errors.StrictNumericEscape); + this.recordStrictModeErrors(Errors.StrictNumericEscape, codePos); } } @@ -1540,13 +1601,13 @@ export default class Tokenizer extends ParserErrors { forceLen: boolean, throwOnInvalid: boolean, ): number | null { - const codePos = this.state.pos; + const codeLoc = this.state.curPosition(); const n = this.readInt(16, len, forceLen, false); if (n === null) { if (throwOnInvalid) { - this.raise(codePos, Errors.InvalidEscapeSequence); + this.raise(Errors.InvalidEscapeSequence, { at: codeLoc }); } else { - this.state.pos = codePos - 1; + this.state.pos = codeLoc.index - 1; } } return n; @@ -1578,12 +1639,14 @@ export default class Tokenizer extends ParserErrors { this.state.containsEsc = true; word += this.input.slice(chunkStart, this.state.pos); - const escStart = this.state.pos; + const escStart = this.state.curPosition(); const identifierCheck = this.state.pos === start ? isIdentifierStart : isIdentifierChar; if (this.input.charCodeAt(++this.state.pos) !== charCodes.lowercaseU) { - this.raise(this.state.pos, Errors.MissingUnicodeEscape); + this.raise(Errors.MissingUnicodeEscape, { + at: this.state.curPosition(), + }); chunkStart = this.state.pos - 1; continue; } @@ -1592,7 +1655,7 @@ export default class Tokenizer extends ParserErrors { const esc = this.readCodePoint(true); if (esc !== null) { if (!identifierCheck(esc)) { - this.raise(escStart, Errors.EscapedCharNotAnIdentifier); + this.raise(Errors.EscapedCharNotAnIdentifier, { at: escStart }); } word += String.fromCodePoint(esc); @@ -1624,8 +1687,8 @@ export default class Tokenizer extends ParserErrors { const { type } = this.state; if (tokenIsKeyword(type) && this.state.containsEsc) { this.raise( - this.state.start, Errors.InvalidEscapedReservedWord, + { at: this.state.startLoc }, tokenLabelName(type), ); } diff --git a/packages/babel-parser/src/tokenizer/state.js b/packages/babel-parser/src/tokenizer/state.js index 1502f5cbd933..ac9a04187e7a 100644 --- a/packages/babel-parser/src/tokenizer/state.js +++ b/packages/babel-parser/src/tokenizer/state.js @@ -7,7 +7,7 @@ import { Position } from "../util/location"; import { types as ct, type TokContext } from "./context"; import { tt, type TokenType } from "./types"; -import type { ParsingError, ErrorTemplate } from "../parser/error"; +import type { ErrorData, ParsingError } from "../parser/error"; type TopicContextState = { // When a topic binding has been currently established, @@ -42,7 +42,7 @@ export default class State { this.curLine = startLine; this.lineStart = -startColumn; - this.startLoc = this.endLoc = new Position(startLine, startColumn); + this.startLoc = this.endLoc = new Position(startLine, startColumn, 0); } errors: ParsingError[] = []; @@ -120,7 +120,6 @@ export default class State { // $FlowIgnore this is initialized when generating the second token. lastTokStartLoc: Position = null; lastTokStart: number = 0; - lastTokEnd: number = 0; // The context stack is used to track whether the apostrophe "`" starts // or ends a string template @@ -141,13 +140,13 @@ export default class State { // todo(JLHwung): set strictErrors to null and avoid recording string errors // after a non-directive is parsed - strictErrors: Map = new Map(); + strictErrors: Map = new Map(); // Tokens length in token store tokensLength: number = 0; curPosition(): Position { - return new Position(this.curLine, this.pos - this.lineStart); + return new Position(this.curLine, this.pos - this.lineStart, this.pos); } clone(skipArrays?: boolean): State { diff --git a/packages/babel-parser/src/util/class-scope.js b/packages/babel-parser/src/util/class-scope.js index 70920d1b68ab..db2c95273859 100644 --- a/packages/babel-parser/src/util/class-scope.js +++ b/packages/babel-parser/src/util/class-scope.js @@ -5,6 +5,7 @@ import { CLASS_ELEMENT_FLAG_STATIC, type ClassElementTypes, } from "./scopeflags"; +import { Position } from "./location"; import { Errors, type raiseFunction } from "../parser/error"; export class ClassScope { @@ -16,13 +17,13 @@ export class ClassScope { // A list of private names used before being defined, mapping to // their position. - undefinedPrivateNames: Map = new Map(); + undefinedPrivateNames: Map = new Map(); } export default class ClassScopeHandler { stack: Array = []; declare raise: raiseFunction; - undefinedPrivateNames: Map = new Map(); + undefinedPrivateNames: Map = new Map(); constructor(raise: raiseFunction) { this.raise = raise; @@ -45,13 +46,13 @@ export default class ClassScopeHandler { const current = this.current(); // Array.from is needed because this is compiled to an array-like for loop - for (const [name, pos] of Array.from(oldClassScope.undefinedPrivateNames)) { + for (const [name, loc] of Array.from(oldClassScope.undefinedPrivateNames)) { if (current) { if (!current.undefinedPrivateNames.has(name)) { - current.undefinedPrivateNames.set(name, pos); + current.undefinedPrivateNames.set(name, loc); } } else { - this.raise(pos, Errors.InvalidPrivateFieldResolution, name); + this.raise(Errors.InvalidPrivateFieldResolution, { at: loc }, name); } } } @@ -59,7 +60,7 @@ export default class ClassScopeHandler { declarePrivateName( name: string, elementType: ClassElementTypes, - pos: number, + loc: Position, ) { const { privateNames, loneAccessors, undefinedPrivateNames } = this.current(); @@ -86,24 +87,24 @@ export default class ClassScopeHandler { } if (redefined) { - this.raise(pos, Errors.PrivateNameRedeclaration, name); + this.raise(Errors.PrivateNameRedeclaration, { at: loc }, name); } privateNames.add(name); undefinedPrivateNames.delete(name); } - usePrivateName(name: string, pos: number) { + usePrivateName(name: string, loc: Position) { let classScope; for (classScope of this.stack) { if (classScope.privateNames.has(name)) return; } if (classScope) { - classScope.undefinedPrivateNames.set(name, pos); + classScope.undefinedPrivateNames.set(name, loc); } else { // top-level - this.raise(pos, Errors.InvalidPrivateFieldResolution, name); + this.raise(Errors.InvalidPrivateFieldResolution, { at: loc }, name); } } } diff --git a/packages/babel-parser/src/util/expression-scope.js b/packages/babel-parser/src/util/expression-scope.js index 5d89b8d2afb8..2cc38db8c121 100644 --- a/packages/babel-parser/src/util/expression-scope.js +++ b/packages/babel-parser/src/util/expression-scope.js @@ -1,6 +1,7 @@ // @flow -import type { ErrorTemplate, raiseFunction } from "../parser/error"; +import type { ErrorData, ErrorTemplate, raiseFunction } from "../parser/error"; +import { Position } from "./location"; /*:: declare var invariant; */ /** @@ -74,17 +75,17 @@ class ExpressionScope { } class ArrowHeadParsingScope extends ExpressionScope { - errors: Map = new Map(); + errors: Map = new Map(); constructor(type: 1 | 2) { super(type); } - recordDeclarationError(pos: number, template: ErrorTemplate) { - this.errors.set(pos, template); + recordDeclarationError(message: ErrorTemplate, loc: Position) { + this.errors.set(loc.index, { message, loc }); } - clearDeclarationError(pos: number) { - this.errors.delete(pos); + clearDeclarationError(loc: Position) { + this.errors.delete(loc.index); } - iterateErrors(iterator: (template: ErrorTemplate, pos: number) => void) { + iterateErrors(iterator: (data: ErrorData) => void) { this.errors.forEach(iterator); } } @@ -113,14 +114,17 @@ export default class ExpressionScopeHandler { * @param {ErrorTemplate} template Error template * @memberof ExpressionScopeHandler */ - recordParameterInitializerError(pos: number, template: ErrorTemplate): void { + recordParameterInitializerError( + loc: Position, + template: ErrorTemplate, + ): void { const { stack } = this; let i = stack.length - 1; let scope: ExpressionScope = stack[i]; while (!scope.isCertainlyParameterDeclaration()) { if (scope.canBeArrowParameterDeclaration()) { /*:: invariant(scope instanceof ArrowHeadParsingScope) */ - scope.recordDeclarationError(pos, template); + scope.recordDeclarationError(template, loc); } else { /*:: invariant(scope.type == kExpression) */ // Type-Expression is the boundary where initializer error can populate to @@ -129,7 +133,7 @@ export default class ExpressionScopeHandler { scope = stack[--i]; } /* eslint-disable @babel/development-internal/dry-error-messages */ - this.raise(pos, template); + this.raise(template, { at: loc }); } /** @@ -154,16 +158,16 @@ export default class ExpressionScopeHandler { * @memberof ExpressionScopeHandler */ recordParenthesizedIdentifierError( - pos: number, template: ErrorTemplate, + loc: Position, ): void { const { stack } = this; const scope: ExpressionScope = stack[stack.length - 1]; if (scope.isCertainlyParameterDeclaration()) { - this.raise(pos, template); + this.raise(template, { at: loc }); } else if (scope.canBeArrowParameterDeclaration()) { /*:: invariant(scope instanceof ArrowHeadParsingScope) */ - scope.recordDeclarationError(pos, template); + scope.recordDeclarationError(template, loc); } else { return; } @@ -178,14 +182,17 @@ export default class ExpressionScopeHandler { * @param {ErrorTemplate} template * @memberof ExpressionScopeHandler */ - recordAsyncArrowParametersError(pos: number, template: ErrorTemplate): void { + recordAsyncArrowParametersError( + template: ErrorTemplate, + loc: Position, + ): void { const { stack } = this; let i = stack.length - 1; let scope: ExpressionScope = stack[i]; while (scope.canBeArrowParameterDeclaration()) { if (scope.type === kMaybeAsyncArrowParameterDeclaration) { /*:: invariant(scope instanceof ArrowHeadParsingScope) */ - scope.recordDeclarationError(pos, template); + scope.recordDeclarationError(template, loc); } scope = stack[--i]; } @@ -196,15 +203,15 @@ export default class ExpressionScopeHandler { const currentScope = stack[stack.length - 1]; if (!currentScope.canBeArrowParameterDeclaration()) return; /*:: invariant(currentScope instanceof ArrowHeadParsingScope) */ - currentScope.iterateErrors((template, pos) => { + currentScope.iterateErrors(({ message, loc }) => { /* eslint-disable @babel/development-internal/dry-error-messages */ - this.raise(pos, template); + this.raise(message, { at: loc }); // iterate from parent scope let i = stack.length - 2; let scope = stack[i]; while (scope.canBeArrowParameterDeclaration()) { /*:: invariant(scope instanceof ArrowHeadParsingScope) */ - scope.clearDeclarationError(pos); + scope.clearDeclarationError(loc); scope = stack[--i]; } }); diff --git a/packages/babel-parser/src/util/location.js b/packages/babel-parser/src/util/location.js index c2bfd6f4ba9c..49ace09998d0 100644 --- a/packages/babel-parser/src/util/location.js +++ b/packages/babel-parser/src/util/location.js @@ -1,7 +1,5 @@ // @flow -import { lineBreakG } from "./whitespace"; - export type Pos = { start: number, }; @@ -12,10 +10,14 @@ export type Pos = { export class Position { line: number; column: number; + index: number; - constructor(line: number, col: number) { + constructor(line: number, col: number, index: number) { this.line = line; this.column = col; + + // this.index = index; + Object.defineProperty(this, "index", { enumerable: false, value: index }); } } @@ -32,25 +34,6 @@ export class SourceLocation { } } -// The `getLineInfo` function is mostly useful when the -// `locations` option is off (for performance reasons) and you -// want to find the line/column position for a given character -// offset. `input` should be the code string that the offset refers -// into. - -export function getLineInfo(input: string, offset: number): Position { - let line = 1; - let lineStart = 0; - let match; - lineBreakG.lastIndex = 0; - while ((match = lineBreakG.exec(input)) && match.index < offset) { - line++; - lineStart = lineBreakG.lastIndex; - } - - return new Position(line, offset - lineStart); -} - /** * creates a new position with a non-zero column offset from the given position. * This function should be only be used when we create AST node out of the token @@ -66,6 +49,6 @@ export function createPositionWithColumnOffset( position: Position, columnOffset: number, ) { - const { line, column } = position; - return new Position(line, column + columnOffset); + const { line, column, index } = position; + return new Position(line, column + columnOffset, index + columnOffset); } diff --git a/packages/babel-parser/src/util/scope.js b/packages/babel-parser/src/util/scope.js index d28d4587f91e..573fb50228b2 100644 --- a/packages/babel-parser/src/util/scope.js +++ b/packages/babel-parser/src/util/scope.js @@ -16,6 +16,7 @@ import { type ScopeFlags, type BindingTypes, } from "./scopeflags"; +import { Position } from "./location"; import * as N from "../types"; import { Errors, type raiseFunction } from "../parser/error"; @@ -40,8 +41,7 @@ export default class ScopeHandler { scopeStack: Array = []; declare raise: raiseFunction; declare inModule: boolean; - undefinedExports: Map = new Map(); - undefinedPrivateNames: Map = new Map(); + undefinedExports: Map = new Map(); constructor(raise: raiseFunction, inModule: boolean) { this.raise = raise; @@ -107,10 +107,10 @@ export default class ScopeHandler { ); } - declareName(name: string, bindingType: BindingTypes, pos: number) { + declareName(name: string, bindingType: BindingTypes, loc: Position) { let scope = this.currentScope(); if (bindingType & BIND_SCOPE_LEXICAL || bindingType & BIND_SCOPE_FUNCTION) { - this.checkRedeclarationInScope(scope, name, bindingType, pos); + this.checkRedeclarationInScope(scope, name, bindingType, loc); if (bindingType & BIND_SCOPE_FUNCTION) { scope.functions.add(name); @@ -124,7 +124,7 @@ export default class ScopeHandler { } else if (bindingType & BIND_SCOPE_VAR) { for (let i = this.scopeStack.length - 1; i >= 0; --i) { scope = this.scopeStack[i]; - this.checkRedeclarationInScope(scope, name, bindingType, pos); + this.checkRedeclarationInScope(scope, name, bindingType, loc); scope.var.add(name); this.maybeExportDefined(scope, name); @@ -146,10 +146,10 @@ export default class ScopeHandler { scope: IScope, name: string, bindingType: BindingTypes, - pos: number, + loc: Position, ) { if (this.isRedeclaredInScope(scope, name, bindingType)) { - this.raise(pos, Errors.VarRedeclaration, name); + this.raise(Errors.VarRedeclaration, { at: loc }, name); } } @@ -196,7 +196,7 @@ export default class ScopeHandler { // can overwrite this behavior. !topLevelScope.functions.has(name) ) { - this.undefinedExports.set(name, id.start); + this.undefinedExports.set(name, id.loc.start); } } diff --git a/packages/babel-parser/test/unit/util/location.skip-bundled.js b/packages/babel-parser/test/unit/util/location.skip-bundled.js deleted file mode 100644 index 463463c88921..000000000000 --- a/packages/babel-parser/test/unit/util/location.skip-bundled.js +++ /dev/null @@ -1,38 +0,0 @@ -import { getLineInfo } from "../../../lib/util/location.js"; - -describe("getLineInfo", () => { - const input = "a\nb\nc\nd\ne\nf\ng\nh\ni"; - - it("reports correct position", () => { - expect(getLineInfo(input, 7)).toEqual({ - column: 1, - line: 4, - }); - }); - - it("reports correct position for first line", () => { - expect(getLineInfo(input, 0)).toEqual({ - column: 0, - line: 1, - }); - }); - - const inputArray = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]; - const singleCharLineEndings = ["\n", "\r", "\u2028", "\u2029"]; - - singleCharLineEndings.forEach(ending => { - it(`supports ${escape(ending)} line ending`, () => { - expect(getLineInfo(inputArray.join(ending), 7)).toEqual({ - column: 1, - line: 4, - }); - }); - }); - - it(`supports ${escape("\r\n")} line ending`, () => { - expect(getLineInfo(inputArray.join("\r\n"), 7)).toEqual({ - column: 1, - line: 3, - }); - }); -}); From 65b7aae49e0c7f62d0773af3b861eb6510513e06 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 14 Jan 2022 09:28:22 -0800 Subject: [PATCH 2/6] Update test results to reflect that Errors.ElementAfterRest is now recoverable, and that expectedContextual now throws an error message with the expected token. Also, update `packages/babel-parser/test/fixtures/es2015/uncategorised/277/input.js` since it had a different syntax error that was previously not reached since it would first fail on the fact that there was an element after rest. Reviewed by @tolmasky. --- .../core/uncategorised/396/options.json | 3 - .../core/uncategorised/396/output.json | 49 +++++++++++ .../core/uncategorised/555/options.json | 3 - .../core/uncategorised/555/output.json | 58 +++++++++++++ .../invalid-location/options.json | 3 - .../invalid-location/output.json | 50 +++++++++++ .../comma-after-rest-param/options.json | 3 - .../comma-after-rest-param/output.json | 44 ++++++++++ .../invalid-rest-in-params/options.json | 3 - .../invalid-rest-in-params/output.json | 55 ++++++++++++ .../invalid-escape-import-from/options.json | 4 +- .../es2015/uncategorised/237/options.json | 4 +- .../es2015/uncategorised/238/options.json | 4 +- .../es2015/uncategorised/277/input.js | 2 +- .../es2015/uncategorised/277/options.json | 3 - .../es2015/uncategorised/277/output.json | 54 ++++++++++++ .../es2015/uncategorised/283/options.json | 3 - .../es2015/uncategorised/283/output.json | 50 +++++++++++ .../es2018/object-rest-spread/7/options.json | 3 - .../es2018/object-rest-spread/7/output.json | 87 +++++++++++++++++++ .../es2018/object-rest-spread/9/options.json | 3 - .../es2018/object-rest-spread/9/output.json | 76 ++++++++++++++++ .../comma-after-rest/options.json | 3 - .../comma-after-rest/output.json | 52 +++++++++++ .../comma-after-spread-for-in/options.json | 3 - .../comma-after-spread-for-in/output.json | 47 ++++++++++ .../comma-after-spread-nested/options.json | 3 - .../comma-after-spread-nested/output.json | 54 ++++++++++++ .../options.json | 3 - .../output.json | 70 +++++++++++++++ .../arrow-with-multiple-rest/options.json | 3 - .../arrow-with-multiple-rest/output.json | 57 ++++++++++++ .../options.json | 5 +- .../options.json | 5 +- .../options.json | 5 +- .../invalid-import-missing-comma/options.json | 5 +- .../options.json | 5 +- .../options.json | 5 +- .../options.json | 5 +- .../options.json | 5 +- .../options.json | 5 +- .../options.json | 5 +- .../invalid-syntax/migrated_0258/options.json | 3 - .../invalid-syntax/migrated_0258/output.json | 54 ++++++++++++ .../flow/declare-module/7/options.json | 9 +- .../opaque_invalid1/options.json | 9 +- .../opaque_invalid2/options.json | 9 +- .../opaque_invalid_decl1/options.json | 9 +- .../opaque_invalid_decl3/options.json | 9 +- .../opaque_subtype_invalid1/options.json | 9 +- .../opaque_subtype_invalid2/options.json | 9 +- .../opaque_subtype_invalid3/options.json | 9 +- .../options.json | 9 +- .../import-type-escaped-error/options.json | 2 +- 54 files changed, 958 insertions(+), 91 deletions(-) delete mode 100644 packages/babel-parser/test/fixtures/core/uncategorised/396/options.json create mode 100644 packages/babel-parser/test/fixtures/core/uncategorised/396/output.json delete mode 100644 packages/babel-parser/test/fixtures/core/uncategorised/555/options.json create mode 100644 packages/babel-parser/test/fixtures/core/uncategorised/555/output.json delete mode 100644 packages/babel-parser/test/fixtures/es2015/array-rest-spread/invalid-location/options.json create mode 100644 packages/babel-parser/test/fixtures/es2015/array-rest-spread/invalid-location/output.json delete mode 100644 packages/babel-parser/test/fixtures/es2015/arrow-functions/comma-after-rest-param/options.json create mode 100644 packages/babel-parser/test/fixtures/es2015/arrow-functions/comma-after-rest-param/output.json delete mode 100644 packages/babel-parser/test/fixtures/es2015/arrow-functions/invalid-rest-in-params/options.json create mode 100644 packages/babel-parser/test/fixtures/es2015/arrow-functions/invalid-rest-in-params/output.json delete mode 100644 packages/babel-parser/test/fixtures/es2015/uncategorised/277/options.json create mode 100644 packages/babel-parser/test/fixtures/es2015/uncategorised/277/output.json delete mode 100644 packages/babel-parser/test/fixtures/es2015/uncategorised/283/options.json create mode 100644 packages/babel-parser/test/fixtures/es2015/uncategorised/283/output.json delete mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/7/options.json create mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/7/output.json delete mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/9/options.json create mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/9/output.json delete mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-rest/options.json create mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-rest/output.json delete mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-for-in/options.json create mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-for-in/output.json delete mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-nested/options.json create mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-nested/output.json delete mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/options.json create mode 100644 packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/output.json delete mode 100644 packages/babel-parser/test/fixtures/esprima/es2015-arrow-function/arrow-with-multiple-rest/options.json create mode 100644 packages/babel-parser/test/fixtures/esprima/es2015-arrow-function/arrow-with-multiple-rest/output.json delete mode 100644 packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0258/options.json create mode 100644 packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0258/output.json diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/396/options.json b/packages/babel-parser/test/fixtures/core/uncategorised/396/options.json deleted file mode 100644 index 8c60527068a3..000000000000 --- a/packages/babel-parser/test/fixtures/core/uncategorised/396/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:18)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/396/output.json b/packages/babel-parser/test/fixtures/core/uncategorised/396/output.json new file mode 100644 index 000000000000..eb50e2ac6991 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/uncategorised/396/output.json @@ -0,0 +1,49 @@ +{ + "type": "File", + "start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}}, + "errors": [ + "SyntaxError: Rest element must be last element. (1:18)" + ], + "program": { + "type": "Program", + "start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "FunctionDeclaration", + "start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}}, + "id": { + "type": "Identifier", + "start":9,"end":10,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":10},"identifierName":"t"}, + "name": "t" + }, + "generator": false, + "async": false, + "params": [ + { + "type": "RestElement", + "start":11,"end":18,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":18}}, + "argument": { + "type": "Identifier", + "start":14,"end":18,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":18},"identifierName":"rest"}, + "name": "rest" + } + }, + { + "type": "Identifier", + "start":20,"end":21,"loc":{"start":{"line":1,"column":20},"end":{"line":1,"column":21},"identifierName":"b"}, + "name": "b" + } + ], + "body": { + "type": "BlockStatement", + "start":23,"end":26,"loc":{"start":{"line":1,"column":23},"end":{"line":1,"column":26}}, + "body": [], + "directives": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/555/options.json b/packages/babel-parser/test/fixtures/core/uncategorised/555/options.json deleted file mode 100644 index 2f22ade906d2..000000000000 --- a/packages/babel-parser/test/fixtures/core/uncategorised/555/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (3:13)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/555/output.json b/packages/babel-parser/test/fixtures/core/uncategorised/555/output.json new file mode 100644 index 000000000000..6c71db324d56 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/uncategorised/555/output.json @@ -0,0 +1,58 @@ +{ + "type": "File", + "start":0,"end":59,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":2}}, + "errors": [ + "SyntaxError: Rest element must be last element. (3:13)" + ], + "program": { + "type": "Program", + "start":0,"end":59,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":2}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "FunctionDeclaration", + "start":0,"end":58,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":1}}, + "id": { + "type": "Identifier", + "start":9,"end":12,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":12},"identifierName":"foo"}, + "name": "foo" + }, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":20,"end":25,"loc":{"start":{"line":2,"column":4},"end":{"line":2,"column":9},"identifierName":"first"}, + "name": "first" + }, + { + "type": "RestElement", + "start":31,"end":40,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":13}}, + "argument": { + "type": "Identifier", + "start":34,"end":40,"loc":{"start":{"line":3,"column":7},"end":{"line":3,"column":13},"identifierName":"second"}, + "name": "second" + } + }, + { + "type": "Identifier", + "start":46,"end":51,"loc":{"start":{"line":4,"column":4},"end":{"line":4,"column":9},"identifierName":"third"}, + "name": "third" + } + ], + "body": { + "type": "BlockStatement", + "start":55,"end":58,"loc":{"start":{"line":5,"column":2},"end":{"line":6,"column":1}}, + "body": [], + "directives": [] + } + }, + { + "type": "EmptyStatement", + "start":58,"end":59,"loc":{"start":{"line":6,"column":1},"end":{"line":6,"column":2}} + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/array-rest-spread/invalid-location/options.json b/packages/babel-parser/test/fixtures/es2015/array-rest-spread/invalid-location/options.json deleted file mode 100644 index 032c2a5069b2..000000000000 --- a/packages/babel-parser/test/fixtures/es2015/array-rest-spread/invalid-location/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:1)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/array-rest-spread/invalid-location/output.json b/packages/babel-parser/test/fixtures/es2015/array-rest-spread/invalid-location/output.json new file mode 100644 index 000000000000..1faee153bbbb --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/array-rest-spread/invalid-location/output.json @@ -0,0 +1,50 @@ +{ + "type": "File", + "start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}}, + "errors": [ + "SyntaxError: Unexpected trailing comma after rest element. (1:1)" + ], + "program": { + "type": "Program", + "start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}}, + "expression": { + "type": "AssignmentExpression", + "start":0,"end":13,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":13}}, + "operator": "=", + "left": { + "type": "ArrayPattern", + "start":0,"end":9,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":9}}, + "elements": [ + { + "type": "RestElement", + "start":1,"end":5,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":5}}, + "argument": { + "type": "Identifier", + "start":4,"end":5,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":5},"identifierName":"a"}, + "name": "a" + } + }, + { + "type": "Identifier", + "start":7,"end":8,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":8},"identifierName":"b"}, + "name": "b" + } + ] + }, + "right": { + "type": "Identifier", + "start":12,"end":13,"loc":{"start":{"line":1,"column":12},"end":{"line":1,"column":13},"identifierName":"c"}, + "name": "c" + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/arrow-functions/comma-after-rest-param/options.json b/packages/babel-parser/test/fixtures/es2015/arrow-functions/comma-after-rest-param/options.json deleted file mode 100644 index 31d4d73d6c85..000000000000 --- a/packages/babel-parser/test/fixtures/es2015/arrow-functions/comma-after-rest-param/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Unexpected token, expected \")\" (1:8)" -} diff --git a/packages/babel-parser/test/fixtures/es2015/arrow-functions/comma-after-rest-param/output.json b/packages/babel-parser/test/fixtures/es2015/arrow-functions/comma-after-rest-param/output.json new file mode 100644 index 000000000000..be93852f68a1 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/arrow-functions/comma-after-rest-param/output.json @@ -0,0 +1,44 @@ +{ + "type": "File", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":16}}, + "errors": [ + "SyntaxError: Unexpected trailing comma after rest element. (1:8)" + ], + "program": { + "type": "Program", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":16}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":16}}, + "expression": { + "type": "ArrowFunctionExpression", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":16}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "RestElement", + "start":1,"end":8,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":8}}, + "argument": { + "type": "Identifier", + "start":4,"end":8,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":8},"identifierName":"rest"}, + "name": "rest" + } + } + ], + "body": { + "type": "BlockStatement", + "start":14,"end":16,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":16}}, + "body": [], + "directives": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/arrow-functions/invalid-rest-in-params/options.json b/packages/babel-parser/test/fixtures/es2015/arrow-functions/invalid-rest-in-params/options.json deleted file mode 100644 index 2f22ade906d2..000000000000 --- a/packages/babel-parser/test/fixtures/es2015/arrow-functions/invalid-rest-in-params/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (3:13)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/arrow-functions/invalid-rest-in-params/output.json b/packages/babel-parser/test/fixtures/es2015/arrow-functions/invalid-rest-in-params/output.json new file mode 100644 index 000000000000..9381c05ade5e --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/arrow-functions/invalid-rest-in-params/output.json @@ -0,0 +1,55 @@ +{ + "type": "File", + "start":0,"end":46,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":8}}, + "errors": [ + "SyntaxError: Rest element must be last element. (3:13)", + "SyntaxError: Unexpected trailing comma after rest element. (3:4)" + ], + "program": { + "type": "Program", + "start":0,"end":46,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":8}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":46,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":8}}, + "expression": { + "type": "ArrowFunctionExpression", + "start":0,"end":45,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":7}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":6,"end":11,"loc":{"start":{"line":2,"column":4},"end":{"line":2,"column":9},"identifierName":"first"}, + "name": "first" + }, + { + "type": "RestElement", + "start":17,"end":26,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":13}}, + "argument": { + "type": "Identifier", + "start":20,"end":26,"loc":{"start":{"line":3,"column":7},"end":{"line":3,"column":13},"identifierName":"second"}, + "name": "second" + } + }, + { + "type": "Identifier", + "start":32,"end":37,"loc":{"start":{"line":4,"column":4},"end":{"line":4,"column":9},"identifierName":"third"}, + "name": "third" + } + ], + "body": { + "type": "BlockStatement", + "start":43,"end":45,"loc":{"start":{"line":5,"column":5},"end":{"line":5,"column":7}}, + "body": [], + "directives": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/modules/invalid-escape-import-from/options.json b/packages/babel-parser/test/fixtures/es2015/modules/invalid-escape-import-from/options.json index 5fe96f9ef281..ea8eafb603d1 100644 --- a/packages/babel-parser/test/fixtures/es2015/modules/invalid-escape-import-from/options.json +++ b/packages/babel-parser/test/fixtures/es2015/modules/invalid-escape-import-from/options.json @@ -1,4 +1,4 @@ { "sourceType": "module", - "throws": "Unexpected token (1:9)" -} + "throws": "Unexpected token, expected \"from\" (1:9)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/237/options.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/237/options.json index e1e139e4ac89..402dfc289f1f 100644 --- a/packages/babel-parser/test/fixtures/es2015/uncategorised/237/options.json +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/237/options.json @@ -1,4 +1,4 @@ { "sourceType": "module", - "throws": "Unexpected token (1:10)" -} + "throws": "Unexpected token, expected \"from\" (1:10)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/238/options.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/238/options.json index b5dc7b8e0755..a2c59362af1b 100644 --- a/packages/babel-parser/test/fixtures/es2015/uncategorised/238/options.json +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/238/options.json @@ -1,4 +1,4 @@ { "sourceType": "module", - "throws": "Unexpected token (1:19)" -} + "throws": "Unexpected token, expected \"from\" (1:19)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/277/input.js b/packages/babel-parser/test/fixtures/es2015/uncategorised/277/input.js index 3abec3af4b34..0c8f6a400b4f 100644 --- a/packages/babel-parser/test/fixtures/es2015/uncategorised/277/input.js +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/277/input.js @@ -1 +1 @@ -function f(a, ...b, c) \ No newline at end of file +function f(a, ...b, c) { } diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/277/options.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/277/options.json deleted file mode 100644 index 8c60527068a3..000000000000 --- a/packages/babel-parser/test/fixtures/es2015/uncategorised/277/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:18)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/277/output.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/277/output.json new file mode 100644 index 000000000000..960a3b22be91 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/277/output.json @@ -0,0 +1,54 @@ +{ + "type": "File", + "start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}}, + "errors": [ + "SyntaxError: Rest element must be last element. (1:18)" + ], + "program": { + "type": "Program", + "start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "FunctionDeclaration", + "start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}}, + "id": { + "type": "Identifier", + "start":9,"end":10,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":10},"identifierName":"f"}, + "name": "f" + }, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":11,"end":12,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":12},"identifierName":"a"}, + "name": "a" + }, + { + "type": "RestElement", + "start":14,"end":18,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":18}}, + "argument": { + "type": "Identifier", + "start":17,"end":18,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":18},"identifierName":"b"}, + "name": "b" + } + }, + { + "type": "Identifier", + "start":20,"end":21,"loc":{"start":{"line":1,"column":20},"end":{"line":1,"column":21},"identifierName":"c"}, + "name": "c" + } + ], + "body": { + "type": "BlockStatement", + "start":23,"end":26,"loc":{"start":{"line":1,"column":23},"end":{"line":1,"column":26}}, + "body": [], + "directives": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/283/options.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/283/options.json deleted file mode 100644 index 391497d6bd30..000000000000 --- a/packages/babel-parser/test/fixtures/es2015/uncategorised/283/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:5)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/283/output.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/283/output.json new file mode 100644 index 000000000000..e08b920bac0a --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/283/output.json @@ -0,0 +1,50 @@ +{ + "type": "File", + "start":0,"end":15,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":15}}, + "errors": [ + "SyntaxError: Rest element must be last element. (1:5)", + "SyntaxError: Unexpected trailing comma after rest element. (1:1)" + ], + "program": { + "type": "Program", + "start":0,"end":15,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":15}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":15,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":15}}, + "expression": { + "type": "ArrowFunctionExpression", + "start":0,"end":15,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":15}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "RestElement", + "start":1,"end":5,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":5}}, + "argument": { + "type": "Identifier", + "start":4,"end":5,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":5},"identifierName":"a"}, + "name": "a" + } + }, + { + "type": "Identifier", + "start":7,"end":8,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":8},"identifierName":"b"}, + "name": "b" + } + ], + "body": { + "type": "BlockStatement", + "start":13,"end":15,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":15}}, + "body": [], + "directives": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/7/options.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/7/options.json deleted file mode 100644 index d2abaaa1d24c..000000000000 --- a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/7/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:10)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/7/output.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/7/output.json new file mode 100644 index 000000000000..843fd66b5281 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/7/output.json @@ -0,0 +1,87 @@ +{ + "type": "File", + "start":0,"end":25,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":25}}, + "errors": [ + "SyntaxError: Rest element must be last element. (1:10)" + ], + "program": { + "type": "Program", + "start":0,"end":25,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":25}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":25,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":25}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":4,"end":24,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":24}}, + "id": { + "type": "ObjectPattern", + "start":4,"end":18,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":18}}, + "properties": [ + { + "type": "RestElement", + "start":6,"end":10,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":10}}, + "argument": { + "type": "Identifier", + "start":9,"end":10,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":10},"identifierName":"x"}, + "name": "x" + } + }, + { + "type": "ObjectProperty", + "start":12,"end":13,"loc":{"start":{"line":1,"column":12},"end":{"line":1,"column":13}}, + "key": { + "type": "Identifier", + "start":12,"end":13,"loc":{"start":{"line":1,"column":12},"end":{"line":1,"column":13},"identifierName":"y"}, + "name": "y" + }, + "computed": false, + "method": false, + "shorthand": true, + "value": { + "type": "Identifier", + "start":12,"end":13,"loc":{"start":{"line":1,"column":12},"end":{"line":1,"column":13},"identifierName":"y"}, + "name": "y" + }, + "extra": { + "shorthand": true + } + }, + { + "type": "ObjectProperty", + "start":15,"end":16,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":16}}, + "key": { + "type": "Identifier", + "start":15,"end":16,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":16},"identifierName":"z"}, + "name": "z" + }, + "computed": false, + "method": false, + "shorthand": true, + "value": { + "type": "Identifier", + "start":15,"end":16,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":16},"identifierName":"z"}, + "name": "z" + }, + "extra": { + "shorthand": true + } + } + ] + }, + "init": { + "type": "Identifier", + "start":21,"end":24,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":24},"identifierName":"obj"}, + "name": "obj" + } + } + ], + "kind": "let" + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/9/options.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/9/options.json deleted file mode 100644 index 7d618d0fb27e..000000000000 --- a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/9/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:13)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/9/output.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/9/output.json new file mode 100644 index 000000000000..f2d7d10d18fd --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/9/output.json @@ -0,0 +1,76 @@ +{ + "type": "File", + "start":0,"end":28,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":28}}, + "errors": [ + "SyntaxError: Rest element must be last element. (1:13)" + ], + "program": { + "type": "Program", + "start":0,"end":28,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":28}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":28,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":28}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":4,"end":27,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":27}}, + "id": { + "type": "ObjectPattern", + "start":4,"end":21,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":21}}, + "properties": [ + { + "type": "ObjectProperty", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7}}, + "key": { + "type": "Identifier", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7},"identifierName":"x"}, + "name": "x" + }, + "computed": false, + "method": false, + "shorthand": true, + "value": { + "type": "Identifier", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7},"identifierName":"x"}, + "name": "x" + }, + "extra": { + "shorthand": true + } + }, + { + "type": "RestElement", + "start":9,"end":13,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":13}}, + "argument": { + "type": "Identifier", + "start":12,"end":13,"loc":{"start":{"line":1,"column":12},"end":{"line":1,"column":13},"identifierName":"y"}, + "name": "y" + } + }, + { + "type": "RestElement", + "start":15,"end":19,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":19}}, + "argument": { + "type": "Identifier", + "start":18,"end":19,"loc":{"start":{"line":1,"column":18},"end":{"line":1,"column":19},"identifierName":"z"}, + "name": "z" + } + } + ] + }, + "init": { + "type": "Identifier", + "start":24,"end":27,"loc":{"start":{"line":1,"column":24},"end":{"line":1,"column":27},"identifierName":"obj"}, + "name": "obj" + } + } + ], + "kind": "let" + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-rest/options.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-rest/options.json deleted file mode 100644 index dfc19ace341a..000000000000 --- a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-rest/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:6)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-rest/output.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-rest/output.json new file mode 100644 index 000000000000..df207ef83418 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-rest/output.json @@ -0,0 +1,52 @@ +{ + "type": "File", + "start":0,"end":15,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":15}}, + "errors": [ + "SyntaxError: Unexpected trailing comma after rest element. (1:6)" + ], + "program": { + "type": "Program", + "start":0,"end":15,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":15}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":15,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":15}}, + "expression": { + "type": "AssignmentExpression", + "start":1,"end":13,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":13}}, + "operator": "=", + "left": { + "type": "ObjectPattern", + "start":1,"end":8,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":8}}, + "properties": [ + { + "type": "RestElement", + "start":2,"end":6,"loc":{"start":{"line":1,"column":2},"end":{"line":1,"column":6}}, + "argument": { + "type": "Identifier", + "start":5,"end":6,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":6},"identifierName":"a"}, + "name": "a" + } + } + ], + "extra": { + "trailingComma": 6 + } + }, + "right": { + "type": "ObjectExpression", + "start":11,"end":13,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":13}}, + "properties": [] + }, + "extra": { + "parenthesized": true, + "parenStart": 0 + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-for-in/options.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-for-in/options.json deleted file mode 100644 index d2abaaa1d24c..000000000000 --- a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-for-in/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:10)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-for-in/output.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-for-in/output.json new file mode 100644 index 000000000000..c22f64624b72 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-for-in/output.json @@ -0,0 +1,47 @@ +{ + "type": "File", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}}, + "errors": [ + "SyntaxError: Unexpected trailing comma after rest element. (1:10)" + ], + "program": { + "type": "Program", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ForInStatement", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}}, + "left": { + "type": "ObjectPattern", + "start":5,"end":12,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":12}}, + "properties": [ + { + "type": "RestElement", + "start":6,"end":10,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":10}}, + "argument": { + "type": "Identifier", + "start":9,"end":10,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":10},"identifierName":"a"}, + "name": "a" + } + } + ], + "extra": { + "trailingComma": 10 + } + }, + "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}} + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-nested/options.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-nested/options.json deleted file mode 100644 index dfc19ace341a..000000000000 --- a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-nested/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:6)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-nested/output.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-nested/output.json new file mode 100644 index 000000000000..bbe9488e834d --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/comma-after-spread-nested/output.json @@ -0,0 +1,54 @@ +{ + "type": "File", + "start":0,"end":15,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":15}}, + "errors": [ + "SyntaxError: Unexpected trailing comma after rest element. (1:6)" + ], + "program": { + "type": "Program", + "start":0,"end":15,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":15}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":15,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":15}}, + "expression": { + "type": "AssignmentExpression", + "start":0,"end":14,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":14}}, + "operator": "=", + "left": { + "type": "ArrayPattern", + "start":0,"end":9,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":9}}, + "elements": [ + { + "type": "ObjectPattern", + "start":1,"end":8,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":8}}, + "properties": [ + { + "type": "RestElement", + "start":2,"end":6,"loc":{"start":{"line":1,"column":2},"end":{"line":1,"column":6}}, + "argument": { + "type": "Identifier", + "start":5,"end":6,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":6},"identifierName":"a"}, + "name": "a" + } + } + ], + "extra": { + "trailingComma": 6 + } + } + ] + }, + "right": { + "type": "ArrayExpression", + "start":12,"end":14,"loc":{"start":{"line":1,"column":12},"end":{"line":1,"column":14}}, + "elements": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/options.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/options.json deleted file mode 100644 index 7153dafad063..000000000000 --- a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:2)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/output.json b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/output.json new file mode 100644 index 000000000000..39d46a3dd36f --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2018/object-rest-spread/expression-rest-not-last-invalid/output.json @@ -0,0 +1,70 @@ +{ + "type": "File", + "start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}}, + "errors": [ + "SyntaxError: Unexpected trailing comma after rest element. (1:2)", + "SyntaxError: Invalid left-hand side in object destructuring pattern. (1:2)" + ], + "program": { + "type": "Program", + "start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":19,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":19}}, + "expression": { + "type": "AssignmentExpression", + "start":1,"end":18,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":18}}, + "operator": "=", + "left": { + "type": "ObjectPattern", + "start":1,"end":13,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":13}}, + "properties": [ + { + "type": "SpreadElement", + "start":2,"end":9,"loc":{"start":{"line":1,"column":2},"end":{"line":1,"column":9}}, + "argument": { + "type": "Identifier", + "start":5,"end":9,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":9},"identifierName":"rest"}, + "name": "rest" + } + }, + { + "type": "ObjectProperty", + "start":11,"end":12,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":12}}, + "method": false, + "key": { + "type": "Identifier", + "start":11,"end":12,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":12},"identifierName":"b"}, + "name": "b" + }, + "computed": false, + "shorthand": true, + "value": { + "type": "Identifier", + "start":11,"end":12,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":12},"identifierName":"b"}, + "name": "b" + }, + "extra": { + "shorthand": true + } + } + ] + }, + "right": { + "type": "ObjectExpression", + "start":16,"end":18,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":18}}, + "properties": [] + }, + "extra": { + "parenthesized": true, + "parenStart": 0 + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-arrow-function/arrow-with-multiple-rest/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-arrow-function/arrow-with-multiple-rest/options.json deleted file mode 100644 index 391497d6bd30..000000000000 --- a/packages/babel-parser/test/fixtures/esprima/es2015-arrow-function/arrow-with-multiple-rest/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:5)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-arrow-function/arrow-with-multiple-rest/output.json b/packages/babel-parser/test/fixtures/esprima/es2015-arrow-function/arrow-with-multiple-rest/output.json new file mode 100644 index 000000000000..2acedec2f48d --- /dev/null +++ b/packages/babel-parser/test/fixtures/esprima/es2015-arrow-function/arrow-with-multiple-rest/output.json @@ -0,0 +1,57 @@ +{ + "type": "File", + "start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":17}}, + "errors": [ + "SyntaxError: Rest element must be last element. (1:5)", + "SyntaxError: Unexpected trailing comma after rest element. (1:1)" + ], + "program": { + "type": "Program", + "start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":17}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":17}}, + "expression": { + "type": "ArrowFunctionExpression", + "start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":17}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "RestElement", + "start":1,"end":5,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":5}}, + "argument": { + "type": "Identifier", + "start":4,"end":5,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":5},"identifierName":"a"}, + "name": "a" + } + }, + { + "type": "RestElement", + "start":7,"end":11,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":11}}, + "argument": { + "type": "Identifier", + "start":10,"end":11,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":11},"identifierName":"b"}, + "name": "b" + } + } + ], + "body": { + "type": "NumericLiteral", + "start":16,"end":17,"loc":{"start":{"line":1,"column":16},"end":{"line":1,"column":17}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-after-named-after-default/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-after-named-after-default/options.json index e806dd7b3a52..7b33361dcc3e 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-after-named-after-default/options.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-after-named-after-default/options.json @@ -1,3 +1,4 @@ { - "throws": "Unexpected token (1:17)" -} + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:17)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-after-named/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-after-named/options.json index 9e093bfdcd96..e6a7cdd33298 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-after-named/options.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-after-named/options.json @@ -1,3 +1,4 @@ { - "throws": "Unexpected token (1:12)" -} + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:12)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-missing-module-specifier/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-missing-module-specifier/options.json index 89bfc2d73f8a..402dfc289f1f 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-missing-module-specifier/options.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default-missing-module-specifier/options.json @@ -1,3 +1,4 @@ { - "throws": "Unexpected token (1:10)" -} + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:10)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-missing-comma/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-missing-comma/options.json index cb6c66081ebf..0466b20f5cdd 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-missing-comma/options.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-missing-comma/options.json @@ -1,3 +1,4 @@ { - "throws": "Unexpected token (1:11)" -} + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:11)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-missing-module-specifier/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-missing-module-specifier/options.json index d5583f7bc5ba..a2c59362af1b 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-missing-module-specifier/options.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-missing-module-specifier/options.json @@ -1,3 +1,4 @@ { - "throws": "Unexpected token (1:19)" -} + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:19)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-after-named/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-after-named/options.json index 9e093bfdcd96..e6a7cdd33298 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-after-named/options.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-after-named/options.json @@ -1,3 +1,4 @@ { - "throws": "Unexpected token (1:12)" -} + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:12)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-after-namespace/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-after-namespace/options.json index 98d712379010..dfe182cb2c54 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-after-namespace/options.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-after-namespace/options.json @@ -1,3 +1,4 @@ { - "throws": "Unexpected token (1:15)" -} + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:15)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-as-missing-from/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-as-missing-from/options.json index 562afcef4837..2f03fe7bc250 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-as-missing-from/options.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-as-missing-from/options.json @@ -1,3 +1,4 @@ { - "throws": "Unexpected token (1:23)" -} + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:23)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-namespace-after-named/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-namespace-after-named/options.json index 9e093bfdcd96..e6a7cdd33298 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-namespace-after-named/options.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-namespace-after-named/options.json @@ -1,3 +1,4 @@ { - "throws": "Unexpected token (1:12)" -} + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:12)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-namespace-missing-as/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-namespace-missing-as/options.json index 2a28555f76db..a067230a5baa 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-namespace-missing-as/options.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-namespace-missing-as/options.json @@ -1,3 +1,4 @@ { - "throws": "Unexpected token (1:9)" -} + "sourceType": "module", + "throws": "Unexpected token, expected \"as\" (1:9)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0258/options.json b/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0258/options.json deleted file mode 100644 index 8c60527068a3..000000000000 --- a/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0258/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Rest element must be last element. (1:18)" -} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0258/output.json b/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0258/output.json new file mode 100644 index 000000000000..6fcf4fdf8e71 --- /dev/null +++ b/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0258/output.json @@ -0,0 +1,54 @@ +{ + "type": "File", + "start":0,"end":24,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":24}}, + "errors": [ + "SyntaxError: Rest element must be last element. (1:18)" + ], + "program": { + "type": "Program", + "start":0,"end":24,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":24}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "FunctionDeclaration", + "start":0,"end":24,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":24}}, + "id": { + "type": "Identifier", + "start":9,"end":10,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":10},"identifierName":"f"}, + "name": "f" + }, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":11,"end":12,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":12},"identifierName":"a"}, + "name": "a" + }, + { + "type": "RestElement", + "start":14,"end":18,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":18}}, + "argument": { + "type": "Identifier", + "start":17,"end":18,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":18},"identifierName":"b"}, + "name": "b" + } + }, + { + "type": "Identifier", + "start":20,"end":21,"loc":{"start":{"line":1,"column":20},"end":{"line":1,"column":21},"identifierName":"c"}, + "name": "c" + } + ], + "body": { + "type": "BlockStatement", + "start":22,"end":24,"loc":{"start":{"line":1,"column":22},"end":{"line":1,"column":24}}, + "body": [], + "directives": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/declare-module/7/options.json b/packages/babel-parser/test/fixtures/flow/declare-module/7/options.json index a97faa10f276..28a372e833b4 100644 --- a/packages/babel-parser/test/fixtures/flow/declare-module/7/options.json +++ b/packages/babel-parser/test/fixtures/flow/declare-module/7/options.json @@ -1,3 +1,8 @@ { - "throws": "Unexpected token (1:34)" -} + "sourceType": "module", + "plugins": [ + "jsx", + "flow" + ], + "throws": "Unexpected token, expected \"exports\" (1:34)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid1/options.json b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid1/options.json index d50e8469b860..6001c77a8aec 100644 --- a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid1/options.json +++ b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid1/options.json @@ -1,3 +1,8 @@ { - "throws": "Unexpected token (1:14)" -} + "sourceType": "module", + "plugins": [ + "jsx", + "flow" + ], + "throws": "Unexpected token, expected \"type\" (1:14)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid2/options.json b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid2/options.json index c958665c03e2..32950d042bdb 100644 --- a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid2/options.json +++ b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid2/options.json @@ -1,3 +1,8 @@ { - "throws": "Unexpected token (1:7)" -} + "sourceType": "module", + "plugins": [ + "jsx", + "flow" + ], + "throws": "Unexpected token, expected \"type\" (1:7)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid_decl1/options.json b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid_decl1/options.json index a015cd4108e6..d1154368e0bd 100644 --- a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid_decl1/options.json +++ b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid_decl1/options.json @@ -1,3 +1,8 @@ { - "throws": "Unexpected token (1:22)" -} + "sourceType": "module", + "plugins": [ + "jsx", + "flow" + ], + "throws": "Unexpected token, expected \"type\" (1:22)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid_decl3/options.json b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid_decl3/options.json index a015cd4108e6..d1154368e0bd 100644 --- a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid_decl3/options.json +++ b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_invalid_decl3/options.json @@ -1,3 +1,8 @@ { - "throws": "Unexpected token (1:22)" -} + "sourceType": "module", + "plugins": [ + "jsx", + "flow" + ], + "throws": "Unexpected token, expected \"type\" (1:22)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid1/options.json b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid1/options.json index c958665c03e2..32950d042bdb 100644 --- a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid1/options.json +++ b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid1/options.json @@ -1,3 +1,8 @@ { - "throws": "Unexpected token (1:7)" -} + "sourceType": "module", + "plugins": [ + "jsx", + "flow" + ], + "throws": "Unexpected token, expected \"type\" (1:7)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid2/options.json b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid2/options.json index d50e8469b860..6001c77a8aec 100644 --- a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid2/options.json +++ b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid2/options.json @@ -1,3 +1,8 @@ { - "throws": "Unexpected token (1:14)" -} + "sourceType": "module", + "plugins": [ + "jsx", + "flow" + ], + "throws": "Unexpected token, expected \"type\" (1:14)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid3/options.json b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid3/options.json index c958665c03e2..32950d042bdb 100644 --- a/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid3/options.json +++ b/packages/babel-parser/test/fixtures/flow/opaque-type-alias/opaque_subtype_invalid3/options.json @@ -1,3 +1,8 @@ { - "throws": "Unexpected token (1:7)" -} + "sourceType": "module", + "plugins": [ + "jsx", + "flow" + ], + "throws": "Unexpected token, expected \"type\" (1:7)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/flow/type-imports/invalid-import-type-namespace/options.json b/packages/babel-parser/test/fixtures/flow/type-imports/invalid-import-type-namespace/options.json index 9e093bfdcd96..ac0cbbd262eb 100644 --- a/packages/babel-parser/test/fixtures/flow/type-imports/invalid-import-type-namespace/options.json +++ b/packages/babel-parser/test/fixtures/flow/type-imports/invalid-import-type-namespace/options.json @@ -1,3 +1,8 @@ { - "throws": "Unexpected token (1:12)" -} + "sourceType": "module", + "plugins": [ + "jsx", + "flow" + ], + "throws": "Unexpected token, expected \"*\" (1:7)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/types/import-type-escaped-error/options.json b/packages/babel-parser/test/fixtures/typescript/types/import-type-escaped-error/options.json index 620770863a48..683222229e10 100644 --- a/packages/babel-parser/test/fixtures/typescript/types/import-type-escaped-error/options.json +++ b/packages/babel-parser/test/fixtures/typescript/types/import-type-escaped-error/options.json @@ -3,5 +3,5 @@ "plugins": [ "typescript" ], - "throws": "Unexpected token (1:17)" + "throws": "Unexpected token, expected \"from\" (1:17)" } \ No newline at end of file From 2d355b4c9bdb3c058c22c01629b4c677553edafd Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 14 Jan 2022 11:11:49 -0800 Subject: [PATCH 3/6] Fix linter error by changing return type to boolean. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/lval.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 0d0feaf26ead..68ffc6418c5e 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -632,7 +632,7 @@ export default class LValParser extends NodeUtils { } } - checkCommaAfterRest(close: $Values): void { + checkCommaAfterRest(close: $Values): boolean { if (!this.match(tt.comma)) { return false; } From 42ebf708f2a2a8d30b1693c624338f073941af4f Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 14 Jan 2022 11:36:32 -0800 Subject: [PATCH 4/6] Corresponding change in typescript's checkCommaAfterRest. Reviewed by @tolmasky. --- packages/babel-parser/src/plugins/typescript/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 1673c1e3e27f..e869bcf3d75d 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3161,15 +3161,16 @@ export default (superClass: Class): Class => return super.parseMaybeDecoratorArguments(expr); } - checkCommaAfterRest(close) { + checkCommaAfterRest(close): boolean { if ( this.state.isAmbientContext && this.match(tt.comma) && this.lookaheadCharCode() === close ) { this.next(); + return false; } else { - super.checkCommaAfterRest(close); + return super.checkCommaAfterRest(close); } } From 3014752b181d033215d18da9132edd692c221e03 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 17 Jan 2022 15:42:04 -0800 Subject: [PATCH 5/6] Make checkExpressionErrors return whether it has errors again, but we should look into this more. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/util.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index 3818fbc6838e..260d0dd1d3e7 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -282,14 +282,18 @@ export default class UtilParser extends Tokenizer { checkExpressionErrors( refExpressionErrors: ?ExpressionErrors, andThrow: boolean, - ): void { - if (!andThrow || !refExpressionErrors) { - return; - } - + ) { + if (!refExpressionErrors) return false; const { shorthandAssignLoc, doubleProtoLoc, optionalParametersLoc } = refExpressionErrors; + const hasErrors = + !!shorthandAssignLoc || !!doubleProtoLoc || !!optionalParametersLoc; + + if (!andThrow) { + return hasErrors; + } + if (shorthandAssignLoc != null) { this.raise(Errors.InvalidCoverInitializedName, { at: shorthandAssignLoc, From 22d311261203b76e5b6d977219cb75094dfa55ee Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 17 Jan 2022 08:18:19 -0800 Subject: [PATCH 6/6] Use a WeakMap to associate indexes to Positions until we decide whether to make index an actual property in the next minor release. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/error.js | 7 ++++--- packages/babel-parser/src/parser/expression.js | 17 ++++++++++++----- packages/babel-parser/src/parser/node.js | 11 ++++++----- packages/babel-parser/src/parser/util.js | 7 ++++--- packages/babel-parser/src/plugins/flow/index.js | 4 ++-- packages/babel-parser/src/plugins/jsx/index.js | 5 +++-- packages/babel-parser/src/tokenizer/index.js | 17 ++++++++++++----- .../babel-parser/src/util/expression-scope.js | 8 +++++--- packages/babel-parser/src/util/location.js | 13 ++++++++++--- 9 files changed, 58 insertions(+), 31 deletions(-) diff --git a/packages/babel-parser/src/parser/error.js b/packages/babel-parser/src/parser/error.js index db9e3de14d19..c8d6c4eeef51 100644 --- a/packages/babel-parser/src/parser/error.js +++ b/packages/babel-parser/src/parser/error.js @@ -1,6 +1,6 @@ // @flow /* eslint sort-keys: "error" */ -import { type Position } from "../util/location"; +import { type Position, indexes } from "../util/location"; import CommentsParser from "./comments"; import { type ErrorCode, ErrorCodes } from "./error-codes"; import { type Node } from "../types"; @@ -111,7 +111,8 @@ export default class ParserError extends CommentsParser { { code, template }: ErrorTemplate, ...params: any ): Error | empty { - const pos = loc.index; + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + const pos: number = indexes.get(loc); const message = template.replace(/%(\d+)/g, (_, i: number) => params[i]) + ` (${loc.line}:${loc.column})`; @@ -138,7 +139,7 @@ export default class ParserError extends CommentsParser { errorTemplate: string, ...params: any ): Error | empty { - const pos = loc.index; + const pos = indexes.get(loc); const message = errorTemplate.replace(/%(\d+)/g, (_, i: number) => params[i]) + ` (${loc.line}:${loc.column})`; diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 9759b69544cd..a5671162f971 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -44,7 +44,11 @@ import { isIdentifierStart, canBeReservedWord, } from "../util/identifier"; -import { Position, createPositionWithColumnOffset } from "../util/location"; +import { + indexes, + Position, + createPositionWithColumnOffset, +} from "../util/location"; import * as charCodes from "charcodes"; import { BIND_OUTSIDE, @@ -313,13 +317,15 @@ export default class ExpressionParser extends LValParser { if ( refExpressionErrors.doubleProtoLoc != null && - refExpressionErrors.doubleProtoLoc.index >= startPos + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + indexes.get(refExpressionErrors.doubleProtoLoc) >= startPos ) { refExpressionErrors.doubleProtoLoc = null; // reset because double __proto__ is valid in assignment expression } if ( refExpressionErrors.shorthandAssignLoc != null && - refExpressionErrors.shorthandAssignLoc.index >= startPos + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + indexes.get(refExpressionErrors.shorthandAssignLoc) >= startPos ) { refExpressionErrors.shorthandAssignLoc = null; // reset because shorthand default was used correctly } @@ -912,7 +918,7 @@ export default class ExpressionParser extends LValParser { return ( base.type === "Identifier" && base.name === "async" && - this.state.lastTokEndLoc.index === base.end && + indexes.get(this.state.lastTokEndLoc) === base.end && !this.canInsertSemicolon() && // check there are no escape sequences, such as \u{61}sync base.end - base.start === 5 && @@ -1764,7 +1770,8 @@ export default class ExpressionParser extends LValParser { this.takeSurroundingComments( val, startPos, - this.state.lastTokEndLoc.index, + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + indexes.get(this.state.lastTokEndLoc), ); return val; diff --git a/packages/babel-parser/src/parser/node.js b/packages/babel-parser/src/parser/node.js index b12c533ce96b..6c7805a2276d 100644 --- a/packages/babel-parser/src/parser/node.js +++ b/packages/babel-parser/src/parser/node.js @@ -2,7 +2,7 @@ import type Parser from "./index"; import UtilParser from "./util"; -import { SourceLocation, type Position } from "../util/location"; +import { indexes, type Position, SourceLocation } from "../util/location"; import type { Comment, Node as NodeType, NodeBase } from "../types"; // Start an AST node, attaching a start offset. @@ -126,9 +126,9 @@ export class NodeUtils extends UtilParser { ); } node.type = type; - node.end = endLoc.index; + node.end = indexes.get(endLoc); node.loc.end = endLoc; - if (this.options.ranges) node.range[1] = endLoc.index; + if (this.options.ranges) node.range[1] = node.end; if (this.options.attachComment) this.processComment(node); return node; } @@ -143,9 +143,10 @@ export class NodeUtils extends UtilParser { node: NodeBase, endLoc?: Position = this.state.lastTokEndLoc, ): void { - node.end = endLoc.index; + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + node.end = indexes.get(endLoc); node.loc.end = endLoc; - if (this.options.ranges) node.range[1] = endLoc.index; + if (this.options.ranges) node.range[1] = node.end; } /** diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index 260d0dd1d3e7..3da813b6e86d 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -1,6 +1,6 @@ // @flow -import { type Position } from "../util/location"; +import { indexes, type Position } from "../util/location"; import { tokenIsLiteralPropertyName, tokenLabelName, @@ -120,7 +120,7 @@ export default class UtilParser extends Tokenizer { hasPrecedingLineBreak(): boolean { return lineBreak.test( - this.input.slice(this.state.lastTokEndLoc.index, this.state.start), + this.input.slice(indexes.get(this.state.lastTokEndLoc), this.state.start), ); } @@ -152,7 +152,8 @@ export default class UtilParser extends Tokenizer { // Throws if the current token and the prev one are separated by a space. assertNoSpace(message: string = "Unexpected space."): void { - if (this.state.start > this.state.lastTokEndLoc.index) { + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + if (this.state.start > indexes.get(this.state.lastTokEndLoc)) { /* eslint-disable @babel/development-internal/dry-error-messages */ this.raise( { diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 07ea2109d1e8..93d67ce4e42f 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -17,7 +17,7 @@ import { tokenIsFlowInterfaceOrTypeOrOpaque, } from "../../tokenizer/types"; import * as N from "../../types"; -import { Position } from "../../util/location"; +import { indexes, Position } from "../../util/location"; import { types as tc } from "../../tokenizer/context"; import * as charCodes from "charcodes"; import { isIteratorStart } from "../../util/identifier"; @@ -269,7 +269,7 @@ export default (superClass: Class): Class => this.next(); // eat `%` this.expectContextual(tt._checks); // Force '%' and 'checks' to be adjacent - if (this.state.lastTokStart > moduloLoc.index + 1) { + if (this.state.lastTokStart > indexes.get(moduloLoc) + 1) { this.raise(FlowErrors.UnexpectedSpaceBetweenModuloChecks, { at: moduloLoc, }); diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index 257fb76ed5a8..4d6af8d2f3bc 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -18,7 +18,7 @@ import { import { TokContext, types as tc } from "../../tokenizer/context"; import * as N from "../../types"; import { isIdentifierChar, isIdentifierStart } from "../../util/identifier"; -import type { Position } from "../../util/location"; +import { indexes, type Position } from "../../util/location"; import { isNewLine } from "../../util/whitespace"; import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error"; @@ -327,7 +327,8 @@ export default (superClass: Class): Class => jsxParseEmptyExpression(): N.JSXEmptyExpression { const node = this.startNodeAt( - this.state.lastTokEndLoc.index, + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + indexes.get(this.state.lastTokEndLoc), this.state.lastTokEndLoc, ); return this.finishNodeAt(node, "JSXEmptyExpression", this.state.startLoc); diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 1e198600f865..f9597200fe74 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -3,7 +3,11 @@ /*:: declare var invariant; */ import type { Options } from "../options"; -import { Position, createPositionWithColumnOffset } from "../util/location"; +import { + indexes, + Position, + createPositionWithColumnOffset, +} from "../util/location"; import * as N from "../types"; import * as charCodes from "charcodes"; import { isIdentifierStart, isIdentifierChar } from "../util/identifier"; @@ -1249,7 +1253,7 @@ export default class Tokenizer extends ParserErrors { if (isBigInt) { const str = this.input - .slice(startLoc.index, this.state.pos) + .slice(indexes.get(startLoc), this.state.pos) .replace(/[_n]/g, ""); this.finishToken(tt.bigint, str); return; @@ -1494,10 +1498,12 @@ export default class Tokenizer extends ParserErrors { } recordStrictModeErrors(message: ErrorTemplate, loc: Position) { - if (this.state.strict && !this.state.strictErrors.has(loc.index)) { + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + const index: number = indexes.get(loc); + if (this.state.strict && !this.state.strictErrors.has(index)) { this.raise(message, { at: loc }); } else { - this.state.strictErrors.set(loc.index, { loc, message }); + this.state.strictErrors.set(index, { loc, message }); } } @@ -1607,7 +1613,8 @@ export default class Tokenizer extends ParserErrors { if (throwOnInvalid) { this.raise(Errors.InvalidEscapeSequence, { at: codeLoc }); } else { - this.state.pos = codeLoc.index - 1; + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + this.state.pos = indexes.get(codeLoc) - 1; } } return n; diff --git a/packages/babel-parser/src/util/expression-scope.js b/packages/babel-parser/src/util/expression-scope.js index 2cc38db8c121..a4f42259a6dd 100644 --- a/packages/babel-parser/src/util/expression-scope.js +++ b/packages/babel-parser/src/util/expression-scope.js @@ -1,7 +1,7 @@ // @flow import type { ErrorData, ErrorTemplate, raiseFunction } from "../parser/error"; -import { Position } from "./location"; +import { indexes, Position } from "./location"; /*:: declare var invariant; */ /** @@ -80,10 +80,12 @@ class ArrowHeadParsingScope extends ExpressionScope { super(type); } recordDeclarationError(message: ErrorTemplate, loc: Position) { - this.errors.set(loc.index, { message, loc }); + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + this.errors.set(indexes.get(loc), { message, loc }); } clearDeclarationError(loc: Position) { - this.errors.delete(loc.index); + // $FlowIgnore[incompatible-type] We know this exists, so it can't be undefined. + this.errors.delete(indexes.get(loc)); } iterateErrors(iterator: (data: ErrorData) => void) { this.errors.forEach(iterator); diff --git a/packages/babel-parser/src/util/location.js b/packages/babel-parser/src/util/location.js index 49ace09998d0..e1f21d86226f 100644 --- a/packages/babel-parser/src/util/location.js +++ b/packages/babel-parser/src/util/location.js @@ -17,7 +17,8 @@ export class Position { this.column = col; // this.index = index; - Object.defineProperty(this, "index", { enumerable: false, value: index }); + // Object.defineProperty(this, "index", { enumerable: false, value: index }); + indexes.set(this, index); } } @@ -34,6 +35,8 @@ export class SourceLocation { } } +export const indexes: WeakMap = new WeakMap(); + /** * creates a new position with a non-zero column offset from the given position. * This function should be only be used when we create AST node out of the token @@ -49,6 +52,10 @@ export function createPositionWithColumnOffset( position: Position, columnOffset: number, ) { - const { line, column, index } = position; - return new Position(line, column + columnOffset, index + columnOffset); + const { line, column } = position; + return new Position( + line, + column + columnOffset, + indexes.get(position) + columnOffset, + ); }