From 5d22ab3df33c52fc5cf8c594dc0c954ee3340b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Sat, 29 Feb 2020 17:53:11 -0500 Subject: [PATCH 01/10] refactor: add raiseWithData method --- .../babel-parser/src/parser/expression.js | 4 +- packages/babel-parser/src/parser/location.js | 55 ++++++++++++------- packages/babel-parser/src/parser/statement.js | 4 +- packages/babel-parser/src/parser/util.js | 8 +-- 4 files changed, 43 insertions(+), 28 deletions(-) diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index d718f257a3f2..f9370a540a17 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -1217,10 +1217,10 @@ export default class ExpressionParser extends LValParser { this.expectPlugin("importMeta"); if (!this.inModule) { - this.raise( + this.raiseWithData( id.start, - `import.meta may appear only with 'sourceType: "module"'`, { code: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED" }, + `import.meta may appear only with 'sourceType: "module"'`, ); } this.sawUnambiguousESM = true; diff --git a/packages/babel-parser/src/parser/location.js b/packages/babel-parser/src/parser/location.js index 21c08a581239..1d6d66660c80 100644 --- a/packages/babel-parser/src/parser/location.js +++ b/packages/babel-parser/src/parser/location.js @@ -1,5 +1,5 @@ // @flow - +/* eslint sort-keys: "error" */ import { getLineInfo, type Position } from "../util/location"; import CommentsParser from "./comments"; @@ -9,6 +9,15 @@ import CommentsParser from "./comments"; // of the error message, and then raises a `SyntaxError` with that // message. +type ErrorContext = { + pos: number, + loc: Position, + missingPluginNames?: Array, + code?: string, +}; + +export const Errors = {}; + export default class LocationParser extends CommentsParser { // Forward-declaration: defined in tokenizer/index.js /*:: @@ -26,33 +35,39 @@ export default class LocationParser extends CommentsParser { return loc; } - raise( + raise(pos: number, errorTemplate: string, ...params: any): Error | empty { + return this.raiseWithData(pos, undefined, errorTemplate, ...params); + } + + raiseWithData( pos: number, - message: string, - { - missingPluginNames, - code, - }: { + data: { missingPluginNames?: Array, code?: string, - } = {}, + }, + errorTemplate: string, + ...params: any ): Error | empty { const loc = this.getLocationForPosition(pos); - - message += ` (${loc.line}:${loc.column})`; - // $FlowIgnore - const err: SyntaxError & { pos: number, loc: Position } = new SyntaxError( + const message = + errorTemplate.replace(/%(\d+)/g, (_, i: number) => params[i]) + + ` (${loc.line}:${loc.column})`; + return this._raise( + Object.assign( + { + loc, + pos, + }, + data, + ), message, ); - err.pos = pos; - err.loc = loc; - if (missingPluginNames) { - err.missingPlugin = missingPluginNames; - } - if (code !== undefined) { - err.code = code; - } + } + _raise(errorContext: ErrorContext, message: string): Error | empty { + // $FlowIgnore + const err: SyntaxError & ErrorContext = new SyntaxError(message); + Object.assign(err, errorContext); if (this.options.errorRecovery) { if (!this.isLookahead) this.state.errors.push(err); return err; diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 582f702d4e3f..61277292be63 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -308,12 +308,12 @@ export default class StatementParser extends ExpressionParser { assertModuleNodeAllowed(node: N.Node): void { if (!this.options.allowImportExportEverywhere && !this.inModule) { - this.raise( + this.raiseWithData( node.start, - `'import' and 'export' may appear only with 'sourceType: "module"'`, { code: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", }, + `'import' and 'export' may appear only with 'sourceType: "module"'`, ); } } diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index ceb42ef5a410..e2e8d4749d44 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -152,10 +152,10 @@ export default class UtilParser extends Tokenizer { expectPlugin(name: string, pos?: ?number): true { if (!this.hasPlugin(name)) { - throw this.raise( + throw this.raiseWithData( pos != null ? pos : this.state.start, - `This experimental syntax requires enabling the parser plugin: '${name}'`, { missingPluginNames: [name] }, + `This experimental syntax requires enabling the parser plugin: '${name}'`, ); } @@ -164,12 +164,12 @@ export default class UtilParser extends Tokenizer { expectOnePlugin(names: Array, pos?: ?number): void { if (!names.some(n => this.hasPlugin(n))) { - throw this.raise( + throw this.raiseWithData( pos != null ? pos : this.state.start, + { missingPluginNames: names }, `This experimental syntax requires enabling one of the following parser plugin(s): '${names.join( ", ", )}'`, - { missingPluginNames: names }, ); } } From 29c0ba64b9cc0da4042cdec2e5b8bd8ab067a25e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Sun, 1 Mar 2020 12:47:58 -0500 Subject: [PATCH 02/10] refactor: error message template --- .../babel-parser/src/parser/expression.js | 175 +++++------------- packages/babel-parser/src/parser/location.js | 174 ++++++++++++++++- packages/babel-parser/src/parser/lval.js | 66 +++---- packages/babel-parser/src/parser/statement.js | 156 +++++----------- packages/babel-parser/src/parser/util.js | 3 +- packages/babel-parser/src/tokenizer/index.js | 76 +++----- packages/babel-parser/src/util/class-scope.js | 11 +- packages/babel-parser/src/util/scope.js | 3 +- 8 files changed, 341 insertions(+), 323 deletions(-) diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index f9370a540a17..a4564a5c6156 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -47,6 +47,7 @@ import { PARAM, functionFlags, } from "../util/production-parameter"; +import { Errors } from "./location"; export default class ExpressionParser extends LValParser { // Forward-declaration: defined in statement.js @@ -103,7 +104,7 @@ export default class ExpressionParser extends LValParser { refExpressionErrors.doubleProto = key.start; } } else { - this.raise(key.start, "Redefinition of __proto__ property"); + this.raise(key.start, Errors.DuplicateProto); } } @@ -352,7 +353,7 @@ export default class ExpressionParser extends LValParser { ) { this.raise( left.argument.start, - "Illegal expression. Wrap left hand side or entire exponentiation in parentheses.", + Errors.UnexpectedTokenUnaryExponentiation, ); } @@ -383,7 +384,7 @@ export default class ExpressionParser extends LValParser { ) { throw this.raise( this.state.start, - `Unexpected "await" after pipeline body; await must have parentheses in minimal proposal`, + Errors.UnexpectedAwaitAfterPipelineBody, ); } } @@ -403,10 +404,7 @@ export default class ExpressionParser extends LValParser { (coalesce && (nextOp === tt.logicalOR || nextOp === tt.logicalAND)) || (logical && nextOp === tt.nullishCoalescing) ) { - throw this.raise( - this.state.start, - `Nullish coalescing operator(??) requires parens when mixing with logical operators`, - ); + throw this.raise(this.state.start, Errors.MixingCoalesceWithLogical); } return this.parseExprOp( @@ -500,12 +498,12 @@ export default class ExpressionParser extends LValParser { const arg = node.argument; if (arg.type === "Identifier") { - this.raise(node.start, "Deleting local variable in strict mode"); + this.raise(node.start, Errors.StrictDelete); } else if ( arg.type === "MemberExpression" && arg.property.type === "PrivateName" ) { - this.raise(node.start, "Deleting a private field is not allowed"); + this.raise(node.start, Errors.DeletePrivateField); } } @@ -618,7 +616,7 @@ export default class ExpressionParser extends LValParser { if (node.property.type === "PrivateName") { if (node.object.type === "Super") { - this.raise(startPos, "Private fields can't be accessed on super"); + this.raise(startPos, Errors.SuperPrivateField); } this.classScope.usePrivateName( node.property.id.name, @@ -739,10 +737,7 @@ export default class ExpressionParser extends LValParser { node.quasi = this.parseTemplate(true); if (typeArguments) node.typeParameters = typeArguments; if (state.optionalChainMember) { - this.raise( - startPos, - "Tagged Template Literals are not allowed in optionalChain", - ); + this.raise(startPos, Errors.OptionalChainingNoTemplate); } return this.finishNode(node, "TaggedTemplateExpression"); } @@ -763,11 +758,11 @@ export default class ExpressionParser extends LValParser { ): N.Expression { if (node.callee.type === "Import") { if (node.arguments.length !== 1) { - this.raise(node.start, "import() requires exactly one argument"); + this.raise(node.start, Errors.ImportCallArity); } else { const importArg = node.arguments[0]; if (importArg && importArg.type === "SpreadElement") { - this.raise(importArg.start, "... is not allowed in import()"); + this.raise(importArg.start, Errors.ImportCallSpreadArgument); } } } @@ -799,7 +794,7 @@ export default class ExpressionParser extends LValParser { if (dynamicImport) { this.raise( this.state.lastTokStart, - "Trailing comma is disallowed inside import(...) arguments", + Errors.ImportCallArgumentTrailingComma, ); } if (nodeForExtra) { @@ -888,19 +883,12 @@ export default class ExpressionParser extends LValParser { !this.scope.allowDirectSuper && !this.options.allowSuperOutsideMethod ) { - this.raise( - node.start, - "super() is only valid inside a class constructor of a subclass. " + - "Maybe a typo in the method name ('constructor') or not extending another class?", - ); + this.raise(node.start, Errors.SuperNotAllowed); } else if ( !this.scope.allowSuper && !this.options.allowSuperOutsideMethod ) { - this.raise( - node.start, - "super is only allowed in object methods and classes", - ); + this.raise(node.start, Errors.UnexpectedSuper); } if ( @@ -908,11 +896,7 @@ export default class ExpressionParser extends LValParser { !this.match(tt.bracketL) && !this.match(tt.dot) ) { - this.raise( - node.start, - "super can only be used with function calls (i.e. super()) or " + - "in property accesses (i.e. super.prop or super[prop])", - ); + this.raise(node.start, Errors.UnsupportedSuper); } return this.finishNode(node, "Super"); @@ -926,10 +910,7 @@ export default class ExpressionParser extends LValParser { } if (!this.match(tt.parenL)) { - this.raise( - this.state.lastTokStart, - "import can only be used in import() or import.meta", - ); + this.raise(this.state.lastTokStart, Errors.UnsupportedImport); } return this.finishNode(node, "Import"); case tt._this: @@ -1092,10 +1073,7 @@ export default class ExpressionParser extends LValParser { if (callee.type === "MemberExpression") { return this.finishNode(node, "BindExpression"); } else { - throw this.raise( - callee.start, - "Binding should be performed on object property.", - ); + throw this.raise(callee.start, Errors.UnsupportedBind); } } @@ -1106,19 +1084,13 @@ export default class ExpressionParser extends LValParser { if ( this.getPluginOption("pipelineOperator", "proposal") !== "smart" ) { - this.raise( - node.start, - "Primary Topic Reference found but pipelineOperator not passed 'smart' for 'proposal' option.", - ); + this.raise(node.start, Errors.PrimaryTopicRequiresSmartPipeline); } this.next(); if (!this.primaryTopicReferenceIsAllowedInCurrentTopicContext()) { - this.raise( - node.start, - `Topic reference was used in a lexical context without topic binding`, - ); + this.raise(node.start, Errors.PrimaryTopicNotAllowed); } this.registerTopicReference(); @@ -1146,10 +1118,7 @@ export default class ExpressionParser extends LValParser { if (isPrivate) { this.expectOnePlugin(["classPrivateProperties", "classPrivateMethods"]); if (!isPrivateNameAllowed) { - this.raise( - this.state.pos, - "Private names can only be used as the name of a class element (i.e. class C { #p = 42; #m() {} } )\n or a property of member expression (i.e. this.#p).", - ); + this.raise(this.state.pos, Errors.UnexpectedPrivateField); } const node = this.startNode(); this.next(); @@ -1202,7 +1171,9 @@ export default class ExpressionParser extends LValParser { if (node.property.name !== propertyName || containsEsc) { this.raise( node.property.start, - `The only valid meta property for ${meta.name} is ${meta.name}.${propertyName}`, + Errors.UnsupportedMetaProperty, + meta.name, + propertyName, ); } @@ -1220,15 +1191,12 @@ export default class ExpressionParser extends LValParser { this.raiseWithData( id.start, { code: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED" }, - `import.meta may appear only with 'sourceType: "module"'`, + Errors.ImportMetaOutsideModule, ); } this.sawUnambiguousESM = true; } else if (!this.hasPlugin("importMeta")) { - this.raise( - id.start, - `Dynamic imports require a parameter: import('a.js')`, - ); + this.raise(id.start, Errors.ImportCallArityLtOne); } return this.parseMetaProperty(node, id, "meta"); @@ -1409,7 +1377,7 @@ export default class ExpressionParser extends LValParser { const metaProp = this.parseMetaProperty(node, meta, "target"); if (!this.scope.inNonArrowFunction && !this.scope.inClass) { - let error = "new.target can only be used in functions"; + let error = Errors.UnexpectedNewTarget; if (this.hasPlugin("classProperties")) { error += " or class properties"; @@ -1424,20 +1392,14 @@ export default class ExpressionParser extends LValParser { node.callee = this.parseNoCallExpr(); if (node.callee.type === "Import") { - this.raise(node.callee.start, "Cannot use new with import(...)"); + this.raise(node.callee.start, Errors.ImportCallNotNewExpression); } else if ( node.callee.type === "OptionalMemberExpression" || node.callee.type === "OptionalCallExpression" ) { - this.raise( - this.state.lastTokEnd, - "constructors in/after an Optional Chain are not allowed", - ); + this.raise(this.state.lastTokEnd, Errors.OptionalChainingNoNew); } else if (this.eat(tt.questionDot)) { - this.raise( - this.state.start, - "constructors in/after an Optional Chain are not allowed", - ); + this.raise(this.state.start, Errors.OptionalChainingNoNew); } this.parseNewArguments(node); @@ -1461,7 +1423,7 @@ export default class ExpressionParser extends LValParser { const elem = this.startNode(); if (this.state.value === null) { if (!isTagged) { - this.raise(this.state.start + 1, "Invalid escape sequence in template"); + this.raise(this.state.start + 1, Errors.InvalidEscapeSequenceTemplate); } } elem.value = { @@ -1558,10 +1520,7 @@ export default class ExpressionParser extends LValParser { let decorators = []; if (this.match(tt.at)) { if (this.hasPlugin("decorators")) { - this.raise( - this.state.start, - "Stage 2 decorators disallow object literal property decorators", - ); + this.raise(this.state.start, Errors.UnsupportedPropertyDecorator); } // we needn't check if decorators (stage 0) plugin is enabled since it's checked by @@ -1658,9 +1617,9 @@ export default class ExpressionParser extends LValParser { const start = method.start; if (method.params.length !== paramCount) { if (method.kind === "get") { - this.raise(start, "getter must not have any formal parameters"); + this.raise(start, Errors.BadGetterArity); } else { - this.raise(start, "setter must have exactly one formal parameter"); + this.raise(start, Errors.BadSetterArity); } } @@ -1668,10 +1627,7 @@ export default class ExpressionParser extends LValParser { method.kind === "set" && method.params[method.params.length - 1].type === "RestElement" ) { - this.raise( - start, - "setter function argument must not be a rest parameter", - ); + this.raise(start, Errors.BadSetterRestParameter); } } @@ -1946,10 +1902,7 @@ export default class ExpressionParser extends LValParser { !!node.key ? node.key.end : node.start; - this.raise( - errorPos, - "Illegal 'use strict' directive in function with non-simple parameter list", - ); + this.raise(errorPos, Errors.IllegalLanguageModeDirective); } } // Start a new scope with regard to labels @@ -2076,7 +2029,7 @@ export default class ExpressionParser extends LValParser { } else if (this.match(tt.question)) { this.expectPlugin("partialApplication"); if (!allowPlaceholder) { - this.raise(this.state.start, "Unexpected argument placeholder"); + this.raise(this.state.start, Errors.UnexpectedArgumentPlaceholder); } const node = this.startNode(); this.next(); @@ -2161,19 +2114,13 @@ export default class ExpressionParser extends LValParser { isBinding: boolean, ): void { if (this.prodParam.hasYield && word === "yield") { - this.raise( - startLoc, - "Can not use 'yield' as identifier inside a generator", - ); + this.raise(startLoc, Errors.YieldBindingIdentifier); return; } if (word === "await") { if (this.prodParam.hasAwait) { - this.raise( - startLoc, - "Can not use 'await' as identifier inside an async function", - ); + this.raise(startLoc, Errors.AwaitBindingIdentifier); return; } if ( @@ -2189,14 +2136,11 @@ export default class ExpressionParser extends LValParser { !this.scope.inNonArrowFunction && word === "arguments" ) { - this.raise( - startLoc, - "'arguments' is not allowed in class field initializer", - ); + this.raise(startLoc, Errors.ArgumentsDisallowedInInitializer); return; } if (checkKeywords && isKeyword(word)) { - this.raise(startLoc, `Unexpected keyword '${word}'`); + this.raise(startLoc, Errors.UnexpectedKeyword, word); return; } @@ -2208,12 +2152,9 @@ export default class ExpressionParser extends LValParser { if (reservedTest(word, this.inModule)) { if (!this.prodParam.hasAwait && word === "await") { - this.raise( - startLoc, - "Can not use keyword 'await' outside an async function", - ); + this.raise(startLoc, Errors.AwaitNotInAsyncFunction); } else { - this.raise(startLoc, `Unexpected reserved word '${word}'`); + this.raise(startLoc, Errors.UnexpectedReservedWord, word); } } } @@ -2235,18 +2176,12 @@ export default class ExpressionParser extends LValParser { this.next(); if (this.state.inParameters) { - this.raise( - node.start, - "await is not allowed in async function parameters", - ); + this.raise(node.start, Errors.AwaitExpressionFormalParameter); } else if (this.state.awaitPos === -1) { this.state.awaitPos = node.start; } if (this.eat(tt.star)) { - this.raise( - node.start, - "await* has been removed from the async functions proposal. Use Promise.all() instead.", - ); + this.raise(node.start, Errors.ObsoleteAwaitStar); } if (!this.scope.inFunction && !this.options.allowAwaitOutsideFunction) { @@ -2285,7 +2220,7 @@ export default class ExpressionParser extends LValParser { const node = this.startNode(); if (this.state.inParameters) { - this.raise(node.start, "yield is not allowed in generator parameters"); + this.raise(node.start, Errors.YieldInParameter); } else if (this.state.yieldPos === -1) { this.state.yieldPos = node.start; } @@ -2313,10 +2248,7 @@ export default class ExpressionParser extends LValParser { if (left.type === "SequenceExpression") { // Ensure that the pipeline head is not a comma-delimited // sequence expression. - this.raise( - leftStartPos, - `Pipeline head should not be a comma-separated sequence expression`, - ); + this.raise(leftStartPos, Errors.PipelineHeadSequenceExpression); } } } @@ -2350,18 +2282,12 @@ export default class ExpressionParser extends LValParser { if (this.match(tt.arrow)) { // If the following token is invalidly `=>`, then throw a human-friendly error // instead of something like 'Unexpected token, expected ";"'. - throw this.raise( - this.state.start, - `Unexpected arrow "=>" after pipeline body; arrow function in pipeline body must be parenthesized`, - ); + throw this.raise(this.state.start, Errors.PipelineBodyNoArrow); } else if ( pipelineStyle === "PipelineTopicExpression" && childExpression.type === "SequenceExpression" ) { - this.raise( - startPos, - `Pipeline body may not be a comma-separated sequence expression`, - ); + this.raise(startPos, Errors.PipelineBodySequenceExpression); } } @@ -2384,10 +2310,7 @@ export default class ExpressionParser extends LValParser { break; case "PipelineTopicExpression": if (!this.topicReferenceWasUsedInCurrentTopicContext()) { - this.raise( - startPos, - `Pipeline is in topic style but does not use topic reference`, - ); + this.raise(startPos, Errors.PipelineTopicUnused); } bodyNode.expression = childExpression; break; diff --git a/packages/babel-parser/src/parser/location.js b/packages/babel-parser/src/parser/location.js index 1d6d66660c80..4262e3eb6006 100644 --- a/packages/babel-parser/src/parser/location.js +++ b/packages/babel-parser/src/parser/location.js @@ -16,7 +16,179 @@ type ErrorContext = { code?: string, }; -export const Errors = {}; +export const Errors = Object.freeze({ + ArgumentsDisallowedInInitializer: + "'arguments' is not allowed in class field initializer", + AsyncFunctionInSingleStatementContext: + "Async functions can only be declared at the top level or inside a block", + AwaitBindingIdentifier: + "Can not use 'await' as identifier inside an async function", + AwaitExpressionFormalParameter: + "await is not allowed in async function parameters", + AwaitNotInAsyncFunction: + "Can not use keyword 'await' outside an async function", + BadGetterArity: "getter must not have any formal parameters", + BadSetterArity: "setter must have exactly one formal parameter", + BadSetterRestParameter: + "setter function argument must not be a rest parameter", + ConstructorClassField: "Classes may not have a field named 'constructor'", + ConstructorClassPrivateField: + "Classes may not have a private field named '#constructor'", + // todo: rephrase to get/set accessor + ConstructorIsAccessor: "Constructor can't have get/set modifier", + ConstructorIsAsync: "Constructor can't be an async function", + ConstructorIsGenerator: "Constructor can't be a generator", + DeclarationMissingInitializer: "%0 require an initialization value", + DecoratorBeforeExport: + "Decorators must be placed *before* the 'export' keyword. You can set the 'decoratorsBeforeExport' option to false to use the 'export @decorator class {}' syntax", + DecoratorConstructor: + "Decorators can't be used with a constructor. Did you mean '@dec class { ... }'?", + DecoratorExportClass: + "Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead.", + DecoratorSemicolon: "Decorators must not be followed by a semicolon", + DeletePrivateField: "Deleting a private field is not allowed", + DestructureNamedImport: + "ES2015 named imports do not destructure. Use another statement for destructuring after the import.", + DuplicateConstructor: "Duplicate constructor in the same class", + DuplicateDefaultExport: "Only one default export allowed per module.", + DuplicateExport: + "`%0` has already been exported. Exported identifiers must be unique.", + DuplicateProto: "Redefinition of __proto__ property", + DuplicateRegExpFlags: "Duplicate regular expression flag", + ElementAfterRest: "Rest element must be last element", + EscapedCharNotAnIdentifier: "Invalid Unicode escape", + ForInOfLoopInitializer: + "%0 loop variable declaration may not have an initializer", + GeneratorInSingleStatementContext: + "Generators can only be declared at the top level or inside a block", + IllegalBreakContinue: "Unsyntactic %0", + IllegalLanguageModeDirective: + "Illegal 'use strict' directive in function with non-simple parameter list", + IllegalReturn: "'return' outside of function", + ImportCallArgumentTrailingComma: + "Trailing comma is disallowed inside import(...) arguments", + ImportCallArity: "import() requires exactly one argument", + ImportCallArityLtOne: "Dynamic imports require a parameter: import('a.js')", + ImportCallNotNewExpression: "Cannot use new with import(...)", + ImportCallSpreadArgument: "... is not allowed in import()", + ImportMetaOutsideModule: `import.meta may appear only with 'sourceType: "module"'`, + ImportOutsideModule: `'import' and 'export' may appear only with 'sourceType: "module"'`, + InvalidCodePoint: "Code point out of bounds", + InvalidDigit: "Expected number in radix %0", + InvalidEscapeSequence: "Bad character escape sequence", + InvalidEscapeSequenceTemplate: "Invalid escape sequence in template", + InvalidEscapedReservedWord: "Escape sequence in keyword %0", + InvalidIdentifier: "Invalid identifier %0", + InvalidLhs: "Invalid left-hand side in %0", + InvalidLhsBinding: "Binding invalid left-hand side in %0", + InvalidNumber: "Invalid number", + InvalidOrUnexpectedToken: "Unexpected character '%0'", + InvalidParenthesizedAssignment: "Invalid parenthesized assignment pattern", + InvalidPrivateFieldResolution: "Private name #%0 is not defined", + InvalidPropertyBindingPattern: "Binding member expression", + InvalidRestAssignmentPattern: "Invalid rest operator's argument", + LabelRedeclaration: "Label '%0' is already declared", + LetInLexicalBinding: + "'let' is not allowed to be used as a name in 'let' or 'const' declarations.", + MalformedRegExpFlags: "Invalid regular expression flag", + MissingClassName: "A class name is required", + MissingEqInAssignment: + "Only '=' operator can be used for specifying default value.", + MissingUnicodeEscape: "Expecting Unicode escape sequence \\uXXXX", + MixingCoalesceWithLogical: + "Nullish coalescing operator(??) requires parens when mixing with logical operators", + ModuleExportUndefined: "Export '%0' is not defined", + MultipleDefaultsInSwitch: "Multiple default clauses", + NewlineAfterThrow: "Illegal newline after throw", + NoCatchOrFinally: "Missing catch or finally clause", + NumberIdentifier: "Identifier directly after number", + NumericSeparatorInEscapeSequence: + "Numeric separators are not allowed inside unicode escape sequences or hex escape sequences", + ObsoleteAwaitStar: + "await* has been removed from the async functions proposal. Use Promise.all() instead.", + OptionalChainingNoNew: + "constructors in/after an Optional Chain are not allowed", + OptionalChainingNoTemplate: + "Tagged Template Literals are not allowed in optionalChain", + ParamDupe: "Argument name clash", + PatternHasAccessor: "Object pattern can't contain getter or setter", + PatternHasMethod: "Object pattern can't contain methods", + PipelineBodyNoArrow: + 'Unexpected arrow "=>" after pipeline body; arrow function in pipeline body must be parenthesized', + PipelineBodySequenceExpression: + "Pipeline body may not be a comma-separated sequence expression", + PipelineHeadSequenceExpression: + "Pipeline head should not be a comma-separated sequence expression", + PipelineTopicUnused: + "Pipeline is in topic style but does not use topic reference", + PrimaryTopicNotAllowed: + "Topic reference was used in a lexical context without topic binding", + PrimaryTopicRequiresSmartPipeline: + "Primary Topic Reference found but pipelineOperator not passed 'smart' for 'proposal' option.", + PrivateNameRedeclaration: "Duplicate private name #%0", + RestTrailingComma: "Unexpected trailing comma after rest element", + SloppyFunction: + "In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement", + StaticPrototype: "Classes may not have static property named prototype", + StrictDelete: "Deleting local variable in strict mode", + StrictEvalArguments: "Assigning to '%0' in strict mode", + StrictEvalArgumentsBinding: "Binding '%0' in strict mode", + StrictFunction: + "In strict mode code, functions can only be declared at top level or inside a block", + StrictOctalLiteral: "Octal literal in strict mode", + StrictWith: "'with' in strict mode", + SuperNotAllowed: + "super() is only valid inside a class constructor of a subclass. Maybe a typo in the method name ('constructor') or not extending another class?", + SuperPrivateField: "Private fields can't be accessed on super", + //todo: rephrase this error message as it is too subjective + TrailingDecorator: "You have trailing decorators with no method", + UnexpectedArgumentPlaceholder: "Unexpected argument placeholder", + UnexpectedAwaitAfterPipelineBody: + 'Unexpected "await" after pipeline body; await must have parentheses in minimal proposal', + UnexpectedDigitAfterHash: "Unexpected digit after hash token", + UnexpectedImportExport: + "'import' and 'export' may only appear at the top level", + UnexpectedKeyword: "Unexpected keyword '%0'", + UnexpectedLeadingDecorator: + "Leading decorators must be attached to a class declaration", + UnexpectedLexicalDeclaration: + "Lexical declaration cannot appear in a single-statement context", + UnexpectedNewTarget: "new.target can only be used in functions", + UnexpectedNumericSeparator: + "A numeric separator is only allowed between two digits", + UnexpectedPrivateField: + "Private names can only be used as the name of a class element (i.e. class C { #p = 42; #m() {} } )\n or a property of member expression (i.e. this.#p).", + UnexpectedReservedWord: "Unexpected reserved word '%0'", + UnexpectedSuper: "super is only allowed in object methods and classes", + UnexpectedToken: "Unexpected token '%'", + UnexpectedTokenUnaryExponentiation: + "Illegal expression. Wrap left hand side or entire exponentiation in parentheses.", + UnsupportedBind: "Binding should be performed on object property.", + //todo: rephrase this error message as it is too subjective + UnsupportedDecoratorExport: + "You can only use decorators on an export when exporting a class", + UnsupportedDefaultExport: + "Only expressions, functions or classes are allowed as the `default` export.", + UnsupportedImport: "import can only be used in import() or import.meta", + UnsupportedMetaProperty: "The only valid meta property for %0 is %0.%1", + //todo: remove Stage 2 as we are likely to forget updating when it progressed + UnsupportedParameterDecorator: + "Stage 2 decorators cannot be used to decorate parameters", + UnsupportedPropertyDecorator: + "Stage 2 decorators disallow object literal property decorators", + UnsupportedSuper: + "super can only be used with function calls (i.e. super()) or in property accesses (i.e. super.prop or super[prop])", + UnterminatedComment: "Unterminated comment", + UnterminatedRegExp: "Unterminated regular expression", + UnterminatedString: "Unterminated string constant", + UnterminatedTemplate: "Unterminated template", + VarRedeclaration: "Identifier '%0' has already been declared", + YieldBindingIdentifier: + "Can not use 'yield' as identifier inside a generator", + YieldInParameter: "yield is not allowed in generator parameters", + ZeroDigitNumericSeparator: + "Numeric separator can not be used after leading 0", +}); export default class LocationParser extends CommentsParser { // Forward-declaration: defined in tokenizer/index.js diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index ce81dccd8c7c..899cf81a3544 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -22,6 +22,7 @@ import { import { NodeUtils } from "./node"; import { type BindingTypes, BIND_NONE } from "../util/scopeflags"; import { ExpressionErrors } from "./util"; +import { Errors } from "./location"; const unwrapParenthesizedExpression = (node: Node) => { return node.type === "ParenthesizedExpression" @@ -62,7 +63,7 @@ export default class LValParser extends NodeUtils { parenthesized.type !== "Identifier" && parenthesized.type !== "MemberExpression" ) { - this.raise(node.start, "Invalid parenthesized assignment pattern"); + this.raise(node.start, Errors.InvalidParenthesizedAssignment); } } @@ -114,10 +115,7 @@ export default class LValParser extends NodeUtils { case "AssignmentExpression": if (node.operator !== "=") { - this.raise( - node.left.end, - "Only '=' operator can be used for specifying default value.", - ); + this.raise(node.left.end, Errors.MissingEqInAssignment); } node.type = "AssignmentPattern"; @@ -140,8 +138,8 @@ export default class LValParser extends NodeUtils { if (prop.type === "ObjectMethod") { const error = prop.kind === "get" || prop.kind === "set" - ? "Object pattern can't contain getter or setter" - : "Object pattern can't contain methods"; + ? Errors.PatternHasAccessor + : Errors.PatternHasMethod; this.raise(prop.key.start, error); } else if (prop.type === "SpreadElement" && !isLast) { @@ -288,10 +286,7 @@ export default class LValParser extends NodeUtils { } else { const decorators = []; if (this.match(tt.at) && this.hasPlugin("decorators")) { - this.raise( - this.state.start, - "Stage 2 decorators cannot be used to decorate parameters", - ); + this.raise(this.state.start, Errors.UnsupportedParameterDecorator); } while (this.match(tt.at)) { decorators.push(this.parseDecorator()); @@ -361,9 +356,10 @@ export default class LValParser extends NodeUtils { ) { this.raise( expr.start, - `${bindingType === BIND_NONE ? "Assigning to" : "Binding"} '${ - expr.name - }' in strict mode`, + bindingType === BIND_NONE + ? Errors.StrictEvalArguments + : Errors.StrictEvalArgumentsBinding, + expr.name, ); } @@ -382,16 +378,13 @@ export default class LValParser extends NodeUtils { const key = `_${expr.name}`; if (checkClashes[key]) { - this.raise(expr.start, "Argument name clash"); + this.raise(expr.start, Errors.ParamDupe); } else { checkClashes[key] = true; } } if (disallowLetBinding && expr.name === "let") { - this.raise( - expr.start, - "'let' is not allowed to be used as a name in 'let' or 'const' declarations.", - ); + this.raise(expr.start, Errors.LetInLexicalBinding); } if (!(bindingType & BIND_NONE)) { this.scope.declareName(expr.name, bindingType, expr.start); @@ -400,7 +393,7 @@ export default class LValParser extends NodeUtils { case "MemberExpression": if (bindingType !== BIND_NONE) { - this.raise(expr.start, "Binding member expression"); + this.raise(expr.start, Errors.InvalidPropertyBindingPattern); } break; @@ -464,15 +457,24 @@ export default class LValParser extends NodeUtils { break; default: { - const message = - (bindingType === BIND_NONE - ? "Invalid" - : /* istanbul ignore next */ "Binding invalid") + - " left-hand side" + - (contextDescription - ? " in " + contextDescription - : /* istanbul ignore next */ "expression"); - this.raise(expr.start, message); + if (contextDescription) { + this.raise( + expr.start, + bindingType === BIND_NONE + ? Errors.InvalidLhs + : Errors.InvalidLhsBinding, + contextDescription, + ); + } else { + // todo: check if contextDescription is never empty + const message = + (bindingType === BIND_NONE + ? "Invalid" + : /* istanbul ignore next */ "Binding invalid") + + " left-hand side expression"; + + this.raise(expr.start, message); + } } } } @@ -482,7 +484,7 @@ export default class LValParser extends NodeUtils { node.argument.type !== "Identifier" && node.argument.type !== "MemberExpression" ) { - this.raise(node.argument.start, "Invalid rest operator's argument"); + this.raise(node.argument.start, Errors.InvalidRestAssignmentPattern); } } @@ -497,10 +499,10 @@ export default class LValParser extends NodeUtils { } raiseRestNotLast(pos: number) { - throw this.raise(pos, `Rest element must be last element`); + throw this.raise(pos, Errors.ElementAfterRest); } raiseTrailingCommaAfterRest(pos: number) { - this.raise(pos, `Unexpected trailing comma after rest element`); + this.raise(pos, Errors.RestTrailingComma); } } diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 61277292be63..03b2e0dd9907 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -3,6 +3,7 @@ import * as N from "../types"; import { types as tt, type TokenType } from "../tokenizer/types"; import ExpressionParser from "./expression"; +import { Errors } from "./location"; import { isIdentifierChar, isIdentifierStart, @@ -61,7 +62,7 @@ export default class StatementParser extends ExpressionParser { for (const [name] of Array.from(this.scope.undefinedExports)) { const pos = this.scope.undefinedExports.get(name); // $FlowIssue - this.raise(pos, `Export '${name}' is not defined`); + this.raise(pos, Errors.ModuleExportUndefined, name); } } @@ -177,16 +178,9 @@ export default class StatementParser extends ExpressionParser { if (this.lookaheadCharCode() === charCodes.dot) break; if (context) { if (this.state.strict) { - this.raise( - this.state.start, - "In strict mode code, functions can only be declared at top level or inside a block", - ); + this.raise(this.state.start, Errors.StrictFunction); } else if (context !== "if" && context !== "label") { - this.raise( - this.state.start, - "In non-strict mode code, functions can only be declared at top level, " + - "inside a block, or as the body of an if statement", - ); + this.raise(this.state.start, Errors.SloppyFunction); } } return this.parseFunctionStatement(node, false, !context); @@ -210,10 +204,7 @@ export default class StatementParser extends ExpressionParser { case tt._var: kind = kind || this.state.value; if (context && kind !== "var") { - this.raise( - this.state.start, - "Lexical declaration cannot appear in a single-statement context", - ); + this.raise(this.state.start, Errors.UnexpectedLexicalDeclaration); } return this.parseVarStatement(node, kind); @@ -236,10 +227,7 @@ export default class StatementParser extends ExpressionParser { } if (!this.options.allowImportExportEverywhere && !topLevel) { - this.raise( - this.state.start, - "'import' and 'export' may only appear at the top level", - ); + this.raise(this.state.start, Errors.UnexpectedImportExport); } this.next(); @@ -278,7 +266,7 @@ export default class StatementParser extends ExpressionParser { if (context) { this.raise( this.state.start, - "Async functions can only be declared at the top level or inside a block", + Errors.AsyncFunctionInSingleStatementContext, ); } this.next(); @@ -313,7 +301,7 @@ export default class StatementParser extends ExpressionParser { { code: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", }, - `'import' and 'export' may appear only with 'sourceType: "module"'`, + Errors.ImportOutsideModule, ); } } @@ -351,17 +339,10 @@ export default class StatementParser extends ExpressionParser { this.hasPlugin("decorators") && !this.getPluginOption("decorators", "decoratorsBeforeExport") ) { - this.raise( - this.state.start, - "Using the export keyword between a decorator and a class is not allowed. " + - "Please use `export @dec class` instead.", - ); + this.raise(this.state.start, Errors.DecoratorExportClass); } } else if (!this.canHaveLeadingDecorator()) { - throw this.raise( - this.state.start, - "Leading decorators must be attached to a class declaration", - ); + throw this.raise(this.state.start, Errors.UnexpectedLeadingDecorator); } } @@ -451,7 +432,7 @@ export default class StatementParser extends ExpressionParser { } } if (i === this.state.labels.length) { - this.raise(node.start, "Unsyntactic " + keyword); + this.raise(node.start, Errors.IllegalBreakContinue, keyword); } } @@ -576,7 +557,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, "'return' outside of function"); + this.raise(this.state.start, Errors.IllegalReturn); } this.next(); @@ -619,7 +600,10 @@ export default class StatementParser extends ExpressionParser { cur.test = this.parseExpression(); } else { if (sawDefault) { - this.raise(this.state.lastTokStart, "Multiple default clauses"); + this.raise( + this.state.lastTokStart, + Errors.MultipleDefaultsInSwitch, + ); } sawDefault = true; cur.test = null; @@ -645,7 +629,7 @@ export default class StatementParser extends ExpressionParser { if ( lineBreak.test(this.input.slice(this.state.lastTokEnd, this.state.start)) ) { - this.raise(this.state.lastTokEnd, "Illegal newline after throw"); + this.raise(this.state.lastTokEnd, Errors.NewlineAfterThrow); } node.argument = this.parseExpression(); this.semicolon(); @@ -690,7 +674,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, "Missing catch or finally clause"); + this.raise(node.start, Errors.NoCatchOrFinally); } return this.finishNode(node, "TryStatement"); @@ -727,7 +711,7 @@ export default class StatementParser extends ExpressionParser { parseWithStatement(node: N.WithStatement): N.WithStatement { if (this.state.strict) { - this.raise(this.state.start, "'with' in strict mode"); + this.raise(this.state.start, Errors.StrictWith); } this.next(); node.object = this.parseHeaderExpression(); @@ -758,7 +742,7 @@ export default class StatementParser extends ExpressionParser { ): N.LabeledStatement { for (const label of this.state.labels) { if (label.name === maybeName) { - this.raise(expr.start, `Label '${maybeName}' is already declared`); + this.raise(expr.start, Errors.LabelRedeclaration, maybeName); } } @@ -875,7 +859,7 @@ export default class StatementParser extends ExpressionParser { this.setStrict(true); if (octalPosition) { - this.raise(octalPosition, "Octal literal in strict mode"); + this.raise(octalPosition, Errors.StrictOctalLiteral); } } @@ -948,12 +932,11 @@ export default class StatementParser extends ExpressionParser { ) { this.raise( init.start, - `${ - isForIn ? "for-in" : "for-of" - } loop variable declaration may not have an initializer`, + Errors.ForInOfLoopInitializer, + isForIn ? "for-in" : "for-of", ); } else if (init.type === "AssignmentPattern") { - this.raise(init.start, "Invalid left-hand side in for-loop"); + this.raise(init.start, Errors.InvalidLhs, "for-loop"); } node.left = init; @@ -1006,7 +989,8 @@ export default class StatementParser extends ExpressionParser { ) { this.raise( this.state.lastTokEnd, - "Complex binding patterns require an initialization value", + Errors.DeclarationMissingInitializer, + "Complex binding patterns", ); } decl.init = null; @@ -1043,10 +1027,7 @@ export default class StatementParser extends ExpressionParser { this.initFunction(node, isAsync); if (this.match(tt.star) && isHangingStatement) { - this.raise( - this.state.start, - "Generators can only be declared at the top level or inside a block", - ); + this.raise(this.state.start, Errors.GeneratorInSingleStatementContext); } node.generator = this.eat(tt.star); @@ -1196,10 +1177,7 @@ export default class StatementParser extends ExpressionParser { while (!this.eat(tt.braceR)) { if (this.eat(tt.semi)) { if (decorators.length > 0) { - throw this.raise( - this.state.lastTokEnd, - "Decorators must not be followed by a semicolon", - ); + throw this.raise(this.state.lastTokEnd, Errors.DecoratorSemicolon); } continue; } @@ -1225,19 +1203,13 @@ export default class StatementParser extends ExpressionParser { member.decorators && member.decorators.length > 0 ) { - this.raise( - member.start, - "Decorators can't be used with a constructor. Did you mean '@dec class { ... }'?", - ); + this.raise(member.start, Errors.DecoratorConstructor); } } }); if (decorators.length) { - throw this.raise( - this.state.start, - "You have trailing decorators with no method", - ); + throw this.raise(this.state.start, Errors.TrailingDecorator); } this.classScope.exit(); @@ -1329,7 +1301,7 @@ export default class StatementParser extends ExpressionParser { } if (this.isNonstaticConstructor(publicMethod)) { - this.raise(publicMethod.key.start, "Constructor can't be a generator"); + this.raise(publicMethod.key.start, Errors.ConstructorIsGenerator); } this.pushClassMethod( @@ -1369,7 +1341,7 @@ export default class StatementParser extends ExpressionParser { // TypeScript allows multiple overloaded constructor declarations. if (state.hadConstructor && !this.hasPlugin("typescript")) { - this.raise(key.start, "Duplicate constructor in the same class"); + this.raise(key.start, Errors.DuplicateConstructor); } state.hadConstructor = true; allowsDirectSuper = constructorAllowsSuper; @@ -1417,10 +1389,7 @@ export default class StatementParser extends ExpressionParser { ); } else { if (this.isNonstaticConstructor(publicMethod)) { - this.raise( - publicMethod.key.start, - "Constructor can't be an async function", - ); + this.raise(publicMethod.key.start, Errors.ConstructorIsAsync); } this.pushClassMethod( @@ -1449,10 +1418,7 @@ export default class StatementParser extends ExpressionParser { this.pushClassPrivateMethod(classBody, privateMethod, false, false); } else { if (this.isNonstaticConstructor(publicMethod)) { - this.raise( - publicMethod.key.start, - "Constructor can't have get/set modifier", - ); + this.raise(publicMethod.key.start, Errors.ConstructorIsAccessor); } this.pushClassMethod( classBody, @@ -1486,17 +1452,11 @@ export default class StatementParser extends ExpressionParser { ((key: $FlowSubtype).name === "prototype" || (key: $FlowSubtype).value === "prototype") ) { - this.raise( - key.start, - "Classes may not have static property named prototype", - ); + this.raise(key.start, Errors.StaticPrototype); } if (key.type === "PrivateName" && key.id.name === "constructor") { - this.raise( - key.start, - "Classes may not have a private field named '#constructor'", - ); + this.raise(key.start, Errors.ConstructorClassPrivateField); } return key; @@ -1509,10 +1469,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, - "Classes may not have a field named 'constructor'", - ); + this.raise(prop.key.start, Errors.ConstructorClassField); } classBody.body.push(this.parseClassProperty(prop)); @@ -1653,7 +1610,7 @@ export default class StatementParser extends ExpressionParser { if (optionalId || !isStatement) { node.id = null; } else { - this.unexpected(null, "A class name is required"); + this.unexpected(null, Errors.MissingClassName); } } } @@ -1771,7 +1728,7 @@ export default class StatementParser extends ExpressionParser { // export async; if (!this.isUnparsedContextual(next, "function")) { - this.unexpected(next, `Unexpected token, expected "function"`); + this.unexpected(next, { label: "function" }); } } @@ -1816,20 +1773,12 @@ export default class StatementParser extends ExpressionParser { this.hasPlugin("decorators") && this.getPluginOption("decorators", "decoratorsBeforeExport") ) { - this.raise( - this.state.start, - "Decorators must be placed *before* the 'export' keyword." + - " You can set the 'decoratorsBeforeExport' option to false to use" + - " the 'export @decorator class {}' syntax", - ); + this.raise(this.state.start, Errors.DecoratorBeforeExport); } 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, - "Only expressions, functions or classes are allowed as the `default` export.", - ); + throw this.raise(this.state.start, Errors.UnsupportedDefaultExport); } else { const res = this.parseMaybeAssign(); this.semicolon(); @@ -1878,12 +1827,7 @@ 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, - "Decorators must be placed *before* the 'export' keyword." + - " You can set the 'decoratorsBeforeExport' option to false to use" + - " the 'export @decorator class {}' syntax", - ); + this.unexpected(this.state.start, Errors.DecoratorBeforeExport); } else { return true; } @@ -1956,10 +1900,7 @@ export default class StatementParser extends ExpressionParser { (node.declaration.type === "ClassDeclaration" || node.declaration.type === "ClassExpression"); if (!node.declaration || !isClass) { - throw this.raise( - node.start, - "You can only use decorators on an export when exporting a class", - ); + throw this.raise(node.start, Errors.UnsupportedDecoratorExport); } this.takeDecorators(node.declaration); } @@ -1999,8 +1940,9 @@ export default class StatementParser extends ExpressionParser { this.raise( node.start, name === "default" - ? "Only one default export allowed per module." - : `\`${name}\` has already been exported. Exported identifiers must be unique.`, + ? Errors.DuplicateDefaultExport + : Errors.DuplicateExport, + name, ); } this.state.exportedIdentifiers.push(name); @@ -2117,11 +2059,7 @@ export default class StatementParser extends ExpressionParser { } else { // Detect an attempt to deep destructure if (this.eat(tt.colon)) { - throw this.raise( - this.state.start, - "ES2015 named imports do not destructure. " + - "Use another statement for destructuring after the import.", - ); + throw this.raise(this.state.start, Errors.DestructureNamedImport); } this.expect(tt.comma); diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index e2e8d4749d44..b2b833f6c107 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -7,6 +7,7 @@ import type { Node } from "../types"; import { lineBreak, skipWhiteSpace } from "../util/whitespace"; import { isIdentifierChar } from "../util/identifier"; import * as charCodes from "charcodes"; +import { Errors } from "./location"; const literal = /^('|")((?:\\?.)*?)\1/; @@ -280,7 +281,7 @@ export default class UtilParser extends Tokenizer { this.unexpected(shorthandAssign); } if (doubleProto >= 0) { - this.raise(doubleProto, "Redefinition of __proto__ property"); + this.raise(doubleProto, Errors.DuplicateProto); } } } diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index ba544a9fb7a3..cdea98675dcc 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -7,7 +7,7 @@ import * as charCodes from "charcodes"; import { isIdentifierStart, isIdentifierChar } from "../util/identifier"; import { types as tt, keywords as keywordTypes, type TokenType } from "./types"; import { type TokContext, types as ct } from "./context"; -import LocationParser from "../parser/location"; +import LocationParser, { Errors } from "../parser/location"; import { SourceLocation } from "../util/location"; import { lineBreak, @@ -265,7 +265,7 @@ export default class Tokenizer extends LocationParser { const startLoc = this.state.curPosition(); const start = this.state.pos; const end = this.input.indexOf("*/", this.state.pos + 2); - if (end === -1) throw this.raise(start, "Unterminated comment"); + if (end === -1) throw this.raise(start, Errors.UnterminatedComment); this.state.pos = end + 2; lineBreakG.lastIndex = start; @@ -401,7 +401,7 @@ export default class Tokenizer extends LocationParser { const nextPos = this.state.pos + 1; const next = this.input.charCodeAt(nextPos); if (next >= charCodes.digit0 && next <= charCodes.digit9) { - throw this.raise(this.state.pos, "Unexpected digit after hash token"); + throw this.raise(this.state.pos, Errors.UnexpectedDigitAfterHash); } if ( @@ -411,7 +411,7 @@ export default class Tokenizer extends LocationParser { ) { this.finishOp(tt.hash, 1); } else { - throw this.raise(this.state.pos, "Unexpected character '#'"); + throw this.raise(this.state.pos, Errors.InvalidOrUnexpectedToken, "#"); } } @@ -821,7 +821,8 @@ export default class Tokenizer extends LocationParser { throw this.raise( this.state.pos, - `Unexpected character '${String.fromCodePoint(code)}'`, + Errors.InvalidOrUnexpectedToken, + String.fromCodePoint(code), ); } @@ -836,11 +837,11 @@ export default class Tokenizer extends LocationParser { let escaped, inClass; for (;;) { if (this.state.pos >= this.length) { - throw this.raise(start, "Unterminated regular expression"); + throw this.raise(start, Errors.UnterminatedRegExp); } const ch = this.input.charAt(this.state.pos); if (lineBreak.test(ch)) { - throw this.raise(start, "Unterminated regular expression"); + throw this.raise(start, Errors.UnterminatedRegExp); } if (escaped) { escaped = false; @@ -867,13 +868,13 @@ export default class Tokenizer extends LocationParser { if (VALID_REGEX_FLAGS.has(char)) { if (mods.indexOf(char) > -1) { - this.raise(this.state.pos + 1, "Duplicate regular expression flag"); + this.raise(this.state.pos + 1, Errors.DuplicateRegExpFlags); } } else if ( isIdentifierChar(charCode) || charCode === charCodes.backslash ) { - this.raise(this.state.pos + 1, "Invalid regular expression flag"); + this.raise(this.state.pos + 1, Errors.MalformedRegExpFlags); } else { break; } @@ -929,26 +930,17 @@ export default class Tokenizer extends LocationParser { 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, - "A numeric separator is only allowed between two digits", - ); + this.raise(this.state.pos, Errors.UnexpectedNumericSeparator); } else if ( forbiddenSiblings.indexOf(prev) > -1 || forbiddenSiblings.indexOf(next) > -1 || Number.isNaN(next) ) { - this.raise( - this.state.pos, - "A numeric separator is only allowed between two digits", - ); + this.raise(this.state.pos, Errors.UnexpectedNumericSeparator); } if (!allowNumSeparator) { - this.raise( - this.state.pos, - "Numeric separators are not allowed inside unicode escape sequences or hex escape sequences", - ); + this.raise(this.state.pos, Errors.NumericSeparatorInEscapeSequence); } // Ignore this _ character @@ -972,10 +964,7 @@ export default class Tokenizer extends LocationParser { if (this.options.errorRecovery && val <= 9) { val = 0; - this.raise( - this.state.start + i + 2, - "Expected number in radix " + radix, - ); + this.raise(this.state.start + i + 2, Errors.InvalidDigit, radix); } else if (forceLen) { val = 0; invalid = true; @@ -1004,7 +993,7 @@ export default class Tokenizer extends LocationParser { this.state.pos += 2; // 0x const val = this.readInt(radix); if (val == null) { - this.raise(this.state.start + 2, "Expected number in radix " + radix); + this.raise(this.state.start + 2, Errors.InvalidDigit, radix); } if (this.hasPlugin("bigInt")) { @@ -1015,7 +1004,7 @@ export default class Tokenizer extends LocationParser { } if (isIdentifierStart(this.input.codePointAt(this.state.pos))) { - throw this.raise(this.state.pos, "Identifier directly after number"); + throw this.raise(this.state.pos, Errors.NumberIdentifier); } if (isBigInt) { @@ -1036,7 +1025,7 @@ export default class Tokenizer extends LocationParser { let isNonOctalDecimalInt = false; if (!startsWithDot && this.readInt(10) === null) { - this.raise(start, "Invalid number"); + this.raise(start, Errors.InvalidNumber); } let octal = this.state.pos - start >= 2 && @@ -1045,6 +1034,7 @@ export default class Tokenizer extends LocationParser { if (this.state.strict) { this.raise( start, + // todo: merge with Errors.StrictOctalLiteral "Legacy octal literals are not allowed in strict mode", ); } @@ -1081,10 +1071,7 @@ export default class Tokenizer extends LocationParser { .slice(start, this.state.pos) .indexOf("_"); if (underscorePos > 0) { - this.raise( - underscorePos + start, - "Numeric separator can not be used after leading 0", - ); + this.raise(underscorePos + start, Errors.ZeroDigitNumericSeparator); } } @@ -1101,7 +1088,7 @@ export default class Tokenizer extends LocationParser { } if (isIdentifierStart(this.input.codePointAt(this.state.pos))) { - throw this.raise(this.state.pos, "Identifier directly after number"); + throw this.raise(this.state.pos, Errors.NumberIdentifier); } // remove "_" for numeric literal separator, and "n" for BigInts @@ -1132,7 +1119,7 @@ export default class Tokenizer extends LocationParser { ++this.state.pos; if (code !== null && code > 0x10ffff) { if (throwOnInvalid) { - this.raise(codePos, "Code point out of bounds"); + this.raise(codePos, Errors.InvalidCodePoint); } else { return null; } @@ -1148,7 +1135,7 @@ export default class Tokenizer extends LocationParser { chunkStart = ++this.state.pos; for (;;) { if (this.state.pos >= this.length) { - throw this.raise(this.state.start, "Unterminated string constant"); + throw this.raise(this.state.start, Errors.UnterminatedString); } const ch = this.input.charCodeAt(this.state.pos); if (ch === quote) break; @@ -1165,7 +1152,7 @@ export default class Tokenizer extends LocationParser { ++this.state.curLine; this.state.lineStart = this.state.pos; } else if (isNewLine(ch)) { - throw this.raise(this.state.start, "Unterminated string constant"); + throw this.raise(this.state.start, Errors.UnterminatedString); } else { ++this.state.pos; } @@ -1182,7 +1169,7 @@ export default class Tokenizer extends LocationParser { containsInvalid = false; for (;;) { if (this.state.pos >= this.length) { - throw this.raise(this.state.start, "Unterminated template"); + throw this.raise(this.state.start, Errors.UnterminatedTemplate); } const ch = this.input.charCodeAt(this.state.pos); if ( @@ -1307,7 +1294,7 @@ export default class Tokenizer extends LocationParser { if (inTemplate) { return null; } else if (this.state.strict) { - this.raise(codePos, "Octal literal in strict mode"); + this.raise(codePos, Errors.StrictOctalLiteral); } else if (!this.state.containsOctal) { // These properties are only used to throw an error for an octal which occurs // in a directive which occurs prior to a "use strict" directive. @@ -1334,7 +1321,7 @@ export default class Tokenizer extends LocationParser { const n = this.readInt(16, len, forceLen, false); if (n === null) { if (throwOnInvalid) { - this.raise(codePos, "Bad character escape sequence"); + this.raise(codePos, Errors.InvalidEscapeSequence); } else { this.state.pos = codePos - 1; } @@ -1369,10 +1356,7 @@ export default class Tokenizer extends LocationParser { this.state.pos === start ? isIdentifierStart : isIdentifierChar; if (this.input.charCodeAt(++this.state.pos) !== charCodes.lowercaseU) { - this.raise( - this.state.pos, - "Expecting Unicode escape sequence \\uXXXX", - ); + this.raise(this.state.pos, Errors.MissingUnicodeEscape); continue; } @@ -1380,7 +1364,7 @@ export default class Tokenizer extends LocationParser { const esc = this.readCodePoint(true); if (esc !== null) { if (!identifierCheck(esc)) { - this.raise(escStart, "Invalid Unicode escape"); + this.raise(escStart, Errors.EscapedCharNotAnIdentifier); } word += String.fromCodePoint(esc); @@ -1409,7 +1393,7 @@ export default class Tokenizer extends LocationParser { this.state.isIterator && (!this.isIterator(word) || !this.state.inType) ) { - this.raise(this.state.pos, `Invalid identifier ${word}`); + this.raise(this.state.pos, Errors.InvalidIdentifier, word); } this.finishToken(type, word); @@ -1418,7 +1402,7 @@ export default class Tokenizer extends LocationParser { checkKeywordEscapes(): void { const kw = this.state.type.keyword; if (kw && this.state.containsEsc) { - this.raise(this.state.start, `Escape sequence in keyword ${kw}`); + this.raise(this.state.start, Errors.InvalidEscapedReservedWord, kw); } } diff --git a/packages/babel-parser/src/util/class-scope.js b/packages/babel-parser/src/util/class-scope.js index 0d1332160a62..50ab0b614a33 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 { Errors } from "../parser/location"; export class ClassScope { // A list of private named declared in the current class @@ -52,7 +53,7 @@ export default class ClassScopeHandler { current.undefinedPrivateNames.set(name, pos); } } else { - this.raiseUndeclaredPrivateName(name, pos); + this.raise(pos, Errors.InvalidPrivateFieldResolution, name); } } } @@ -86,7 +87,7 @@ export default class ClassScopeHandler { } if (redefined) { - this.raise(pos, `Duplicate private name #${name}`); + this.raise(pos, Errors.PrivateNameRedeclaration, name); } classScope.privateNames.add(name); @@ -103,11 +104,7 @@ export default class ClassScopeHandler { classScope.undefinedPrivateNames.set(name, pos); } else { // top-level - this.raiseUndeclaredPrivateName(name, pos); + this.raise(pos, Errors.InvalidPrivateFieldResolution, name); } } - - raiseUndeclaredPrivateName(name: string, pos: number) { - this.raise(pos, `Private name #${name} is not defined`); - } } diff --git a/packages/babel-parser/src/util/scope.js b/packages/babel-parser/src/util/scope.js index 452951a284d0..6409f606e35d 100644 --- a/packages/babel-parser/src/util/scope.js +++ b/packages/babel-parser/src/util/scope.js @@ -16,6 +16,7 @@ import { type BindingTypes, } from "./scopeflags"; import * as N from "../types"; +import { Errors } from "../parser/location"; // Start an AST node, attaching a start offset. export class Scope { @@ -133,7 +134,7 @@ export default class ScopeHandler { pos: number, ) { if (this.isRedeclaredInScope(scope, name, bindingType)) { - this.raise(pos, `Identifier '${name}' has already been declared`); + this.raise(pos, Errors.VarRedeclaration, name); } } From c5a0f509f4616381707bd8f51bcae93b398df92a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Sun, 1 Mar 2020 15:40:39 -0500 Subject: [PATCH 03/10] fix missing plugin error structure --- packages/babel-parser/src/parser/location.js | 4 ++-- packages/babel-parser/src/parser/util.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/babel-parser/src/parser/location.js b/packages/babel-parser/src/parser/location.js index 4262e3eb6006..4c5ea26a8906 100644 --- a/packages/babel-parser/src/parser/location.js +++ b/packages/babel-parser/src/parser/location.js @@ -12,7 +12,7 @@ import CommentsParser from "./comments"; type ErrorContext = { pos: number, loc: Position, - missingPluginNames?: Array, + missingPlugin?: Array, code?: string, }; @@ -214,7 +214,7 @@ export default class LocationParser extends CommentsParser { raiseWithData( pos: number, data: { - missingPluginNames?: Array, + missingPlugin?: Array, code?: string, }, errorTemplate: string, diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index b2b833f6c107..dc6580fcc4a1 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -155,7 +155,7 @@ export default class UtilParser extends Tokenizer { if (!this.hasPlugin(name)) { throw this.raiseWithData( pos != null ? pos : this.state.start, - { missingPluginNames: [name] }, + { missingPlugin: [name] }, `This experimental syntax requires enabling the parser plugin: '${name}'`, ); } @@ -167,7 +167,7 @@ export default class UtilParser extends Tokenizer { if (!names.some(n => this.hasPlugin(n))) { throw this.raiseWithData( pos != null ? pos : this.state.start, - { missingPluginNames: names }, + { missingPlugin: names }, `This experimental syntax requires enabling one of the following parser plugin(s): '${names.join( ", ", )}'`, From 4cd4653274ca91418c697071c5df509703422eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Sun, 1 Mar 2020 21:44:42 -0500 Subject: [PATCH 04/10] fix flow errors --- packages/babel-parser/src/parser/location.js | 3 ++- packages/babel-parser/src/parser/statement.js | 2 +- packages/babel-parser/src/util/class-scope.js | 2 +- packages/babel-parser/src/util/scope.js | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/babel-parser/src/parser/location.js b/packages/babel-parser/src/parser/location.js index 4c5ea26a8906..cd8cf50ed11a 100644 --- a/packages/babel-parser/src/parser/location.js +++ b/packages/babel-parser/src/parser/location.js @@ -213,7 +213,7 @@ export default class LocationParser extends CommentsParser { raiseWithData( pos: number, - data: { + data?: { missingPlugin?: Array, code?: string, }, @@ -226,6 +226,7 @@ export default class LocationParser extends CommentsParser { ` (${loc.line}:${loc.column})`; return this._raise( Object.assign( + {}, { loc, pos, diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 03b2e0dd9907..c23b3b82e007 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -1728,7 +1728,7 @@ export default class StatementParser extends ExpressionParser { // export async; if (!this.isUnparsedContextual(next, "function")) { - this.unexpected(next, { label: "function" }); + this.unexpected(next, tt._function); } } diff --git a/packages/babel-parser/src/util/class-scope.js b/packages/babel-parser/src/util/class-scope.js index 50ab0b614a33..6de6d790ea90 100644 --- a/packages/babel-parser/src/util/class-scope.js +++ b/packages/babel-parser/src/util/class-scope.js @@ -19,7 +19,7 @@ export class ClassScope { undefinedPrivateNames: Map = new Map(); } -type raiseFunction = (number, string) => void; +type raiseFunction = (number, string, ...any) => void; export default class ClassScopeHandler { stack: Array = []; diff --git a/packages/babel-parser/src/util/scope.js b/packages/babel-parser/src/util/scope.js index 6409f606e35d..5875cc3f194b 100644 --- a/packages/babel-parser/src/util/scope.js +++ b/packages/babel-parser/src/util/scope.js @@ -33,7 +33,7 @@ export class Scope { } } -type raiseFunction = (number, string) => void; +type raiseFunction = (number, string, ...any) => void; // The functions in this module keep track of declared variables in the // current scope in order to detect duplicate variable names. From 8c17114bfe35d03caeb7e0faabcf8b93394a039c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Sun, 1 Mar 2020 21:53:31 -0500 Subject: [PATCH 05/10] refactor: use error message template in eslint plugin --- packages/babel-parser/src/plugins/estree.js | 24 +++++++-------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index 6104e616057e..659bdd2c0da8 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -6,6 +6,7 @@ import type { ExpressionErrors } from "../parser/util"; import * as N from "../types"; import type { Position } from "../util/location"; import { type BindingTypes, BIND_NONE } from "../util/scopeflags"; +import { Errors } from "../parser/location"; function isSimpleProperty(node: N.Node): boolean { return ( @@ -99,19 +100,16 @@ export default (superClass: Class): Class => const paramCount = prop.kind === "get" ? 0 : 1; const start = prop.start; if (prop.value.params.length !== paramCount) { - if (prop.kind === "get") { - this.raise(start, "getter must not have any formal parameters"); + if (method.kind === "get") { + this.raise(start, Errors.BadGetterArity); } else { - this.raise(start, "setter must have exactly one formal parameter"); + this.raise(start, Errors.BadSetterArity); } } else if ( prop.kind === "set" && prop.value.params[0].type === "RestElement" ) { - this.raise( - start, - "setter function argument must not be a rest parameter", - ); + this.raise(start, Errors.BadSetterRestParameter); } } @@ -170,7 +168,7 @@ export default (superClass: Class): Class => if (refExpressionErrors && refExpressionErrors.doubleProto === -1) { refExpressionErrors.doubleProto = key.start; } else { - this.raise(key.start, "Redefinition of __proto__ property"); + this.raise(key.start, Errors.DuplicateProto); } } @@ -374,15 +372,9 @@ export default (superClass: Class): Class => toAssignableObjectExpressionProp(prop: N.Node, isLast: boolean) { if (prop.kind === "get" || prop.kind === "set") { - throw this.raise( - prop.key.start, - "Object pattern can't contain getter or setter", - ); + throw this.raise(prop.key.start, Errors.PatternHasAccessor); } else if (prop.method) { - throw this.raise( - prop.key.start, - "Object pattern can't contain methods", - ); + throw this.raise(prop.key.start, Errors.PatternHasMethod); } else { super.toAssignableObjectExpressionProp(prop, isLast); } From 0b8d5ca6768fba20f87152e54c7ed85890ded7cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Mon, 2 Mar 2020 15:58:51 -0500 Subject: [PATCH 06/10] refacotr: use error message template in flow plugin --- packages/babel-parser/src/parser/location.js | 1 + packages/babel-parser/src/plugins/flow.js | 263 +++++++++++-------- 2 files changed, 148 insertions(+), 116 deletions(-) diff --git a/packages/babel-parser/src/parser/location.js b/packages/babel-parser/src/parser/location.js index cd8cf50ed11a..e89c6f36ebbc 100644 --- a/packages/babel-parser/src/parser/location.js +++ b/packages/babel-parser/src/parser/location.js @@ -16,6 +16,7 @@ type ErrorContext = { code?: string, }; +// The Errors key follows https://cs.chromium.org/chromium/src/v8/src/common/message-template.h unless it does not exist export const Errors = Object.freeze({ ArgumentsDisallowedInInitializer: "'arguments' is not allowed in class field initializer", diff --git a/packages/babel-parser/src/plugins/flow.js b/packages/babel-parser/src/plugins/flow.js index fe47f8be39bb..75214dd3a9f7 100644 --- a/packages/babel-parser/src/plugins/flow.js +++ b/packages/babel-parser/src/plugins/flow.js @@ -22,6 +22,7 @@ import { SCOPE_OTHER, } from "../util/scopeflags"; import type { ExpressionErrors } from "../parser/util"; +import { Errors } from "../parser/location"; const reservedTypes = new Set([ "_", @@ -42,6 +43,79 @@ const reservedTypes = new Set([ "void", ]); +/* eslint sort-keys: "error" */ +const flowErrors = Object.freeze({ + AmbiguousConditionalArrow: + "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate.", + AmbiguousDeclareModuleKind: + "Found both `declare module.exports` and `declare export` in the same module. Modules can only have 1 since they are either an ES module or they are a CommonJS module", + AssignReservedType: "Cannot overwrite reserved type %0", + DuplicateDeclareModuleExports: "Duplicate `declare module.exports` statement", + EnumBooleanMemberNotInitialized: + "Boolean enum members need to be initialized. Use either `%0 = true,` or `%0 = false,` in enum `%1`.", + EnumDuplicateMemberName: + "Enum member names need to be unique, but the name `%0` has already been used before in enum `%1`.", + EnumInconsistentMemberValues: + "Enum `%0` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers.", + EnumInvalidExplicitType: + "Enum type `%1` is not valid. Use one of `boolean`, `number`, `string`, or `symbol` in enum `%0`.", + EnumInvalidExplicitTypeUnknownSupplied: + "Supplied enum type is not valid. Use one of `boolean`, `number`, `string`, or `symbol` in enum `%0`.", + EnumInvalidMemberInitializerPrimaryType: + "Enum `%0` has type `%2`, so the initializer of `%1` needs to be a %2 literal.", + EnumInvalidMemberInitializerSymbolType: + "Symbol enum members cannot be initialized. Use `%1,` in enum `%0`.", + EnumInvalidMemberInitializerUnknownType: + "The enum member initializer for `%1` needs to be a literal (either a boolean, number, or string) in enum `%0`.", + EnumInvalidMemberName: + "Enum member names cannot start with lowercase 'a' through 'z'. Instead of using `%0`, consider using `%1`, in enum `%2`.", + EnumNumberMemberNotInitialized: + "Number enum members need to be initialized, e.g. `%1 = 1` in enum `%0`.", + EnumStringMemberInconsistentlyInitailized: + "String enum members need to consistently either all use initializers, or use no initializers, in enum `%0`.", + ImportTypeShorthandOnlyInPureImport: + "The `type` and `typeof` keywords on named imports can only be used on regular `import` statements. It cannot be used with `import type` or `import typeof` statements", + InexactInsideExact: + "Explicit inexact syntax cannot appear inside an explicit exact object type", + InexactInsideNonObject: + "Explicit inexact syntax cannot appear in class or interface definitions", + InexactVariance: "Explicit inexact syntax cannot have variance", + InvalidNonTypeImportInDeclareModule: + "Imports within a `declare module` body must always be `import type` or `import typeof`", + MissingTypeParamDefault: + "Type parameter declaration needs a default, since a preceding type parameter declaration has a default.", + NestedDeclareModule: + "`declare module` cannot be used inside another `declare module`", + NestedFlowComment: "Cannot have a flow comment inside another flow comment", + OptionalBindingPattern: + "A binding pattern parameter cannot be optional in an implementation signature.", + SpreadVariance: "Spread properties cannot have variance", + TypeBeforeInitializer: + "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`", + TypeCastInPattern: + "The type cast expression is expected to be wrapped with parenthesis", + UnexpectedExplicitInexactInObject: + "Explicit inexact syntax must appear at the end of an inexact object", + UnexpectedReservedType: "Unexpected reserved type %0", + UnexpectedReservedUnderscore: + "`_` is only allowed as a type argument to call or new", + //todo: replace ´ by ` + UnexpectedSpaceBetweenModuloChecks: + "Spaces between ´%´ and ´checks´ are not allowed here.", + UnexpectedSpreadType: + "Spread operator cannot appear in class or interface definitions", + UnexpectedSubtractionOperand: + 'Unexpected token, expected "number" or "bigint"', + UnexpectedTokenAfterTypeParameter: + "Expected an arrow function after this type parameter declaration", + UnsupportedDeclareExportKind: + "`declare export %0` is not supported. Use `%1` instead", + UnsupportedStatementInDeclareModule: + "Only declares and type imports are allowed inside declare module", + UnterminatedFlowComment: "Unterminated flow-comment", +}); +/* eslint-disable sort-keys */ + function isEsModuleType(bodyElement: N.Node): boolean { return ( bodyElement.type === "DeclareExportAllDeclaration" || @@ -170,10 +244,7 @@ export default (superClass: Class): Class => moduloLoc.line !== checksLoc.line || moduloLoc.column !== checksLoc.column - 1 ) { - this.raise( - moduloPos, - "Spaces between ´%´ and ´checks´ are not allowed here.", - ); + this.raise(moduloPos, flowErrors.UnexpectedSpaceBetweenModuloChecks); } if (this.eat(tt.parenL)) { node.value = this.parseExpression(); @@ -266,10 +337,7 @@ export default (superClass: Class): Class => return this.flowParseDeclareModuleExports(node); } else { if (insideModule) { - this.raise( - this.state.lastTokStart, - "`declare module` cannot be used inside another `declare module`", - ); + this.raise(this.state.lastTokStart, flowErrors.NestedDeclareModule); } return this.flowParseDeclareModule(node); } @@ -318,14 +386,14 @@ export default (superClass: Class): Class => if (!this.isContextual("type") && !this.match(tt._typeof)) { this.raise( this.state.lastTokStart, - "Imports within a `declare module` body must always be `import type` or `import typeof`", + flowErrors.InvalidNonTypeImportInDeclareModule, ); } this.parseImport(bodyNode); } else { this.expectContextual( "declare", - "Only declares and type imports are allowed inside declare module", + flowErrors.UnsupportedStatementInDeclareModule, ); bodyNode = this.flowParseDeclare(bodyNode, true); @@ -342,23 +410,28 @@ export default (superClass: Class): Class => let kind = null; let hasModuleExport = false; - const errorMessage = - "Found both `declare module.exports` and `declare export` in the same module. " + - "Modules can only have 1 since they are either an ES module or they are a CommonJS module"; body.forEach(bodyElement => { if (isEsModuleType(bodyElement)) { if (kind === "CommonJS") { - this.raise(bodyElement.start, errorMessage); + this.raise( + bodyElement.start, + flowErrors.AmbiguousDeclareModuleKind, + ); } kind = "ES"; } else if (bodyElement.type === "DeclareModuleExports") { if (hasModuleExport) { this.raise( bodyElement.start, - "Duplicate `declare module.exports` statement", + flowErrors.DuplicateDeclareModuleExports, + ); + } + if (kind === "ES") { + this.raise( + bodyElement.start, + flowErrors.AmbiguousDeclareModuleKind, ); } - if (kind === "ES") this.raise(bodyElement.start, errorMessage); kind = "CommonJS"; hasModuleExport = true; } @@ -396,9 +469,11 @@ export default (superClass: Class): Class => ) { const label = this.state.value; const suggestion = exportSuggestions[label]; - this.unexpected( + throw this.raise( this.state.start, - `\`declare export ${label}\` is not supported. Use \`${suggestion}\` instead`, + flowErrors.UnsupportedDeclareExportKind, + label, + suggestion, ); } @@ -554,22 +629,20 @@ export default (superClass: Class): Class => checkNotUnderscore(word: string) { if (word === "_") { - this.raise( - this.state.start, - "`_` is only allowed as a type argument to call or new", - ); + this.raise(this.state.start, flowErrors.UnexpectedReservedUnderscore); } } checkReservedType(word: string, startLoc: number, declaration?: boolean) { if (!reservedTypes.has(word)) return; - if (declaration) { - this.raise(startLoc, `Cannot overwrite reserved type ${word}`); - return; - } - - this.raise(startLoc, `Unexpected reserved type ${word}`); + this.raise( + startLoc, + declaration + ? flowErrors.AssignReservedType + : flowErrors.UnexpectedReservedType, + word, + ); } flowParseRestrictedIdentifier( @@ -652,11 +725,7 @@ export default (superClass: Class): Class => node.default = this.flowParseType(); } else { if (requireDefault) { - this.raise( - nodeStart, - // eslint-disable-next-line max-len - "Type parameter declaration needs a default, since a preceding type parameter declaration has a default.", - ); + this.raise(nodeStart, flowErrors.MissingTypeParamDefault); } } @@ -991,7 +1060,7 @@ export default (superClass: Class): Class => ) { this.raise( inexactStart, - "Explicit inexact syntax must appear at the end of an inexact object", + flowErrors.UnexpectedExplicitInexactInObject, ); } } @@ -1034,35 +1103,26 @@ export default (superClass: Class): Class => if (!allowSpread) { this.raise( this.state.lastTokStart, - "Explicit inexact syntax cannot appear in class or interface definitions", + flowErrors.InexactInsideNonObject, ); } else if (!allowInexact) { - this.raise( - this.state.lastTokStart, - "Explicit inexact syntax cannot appear inside an explicit exact object type", - ); + this.raise(this.state.lastTokStart, flowErrors.InexactInsideExact); } if (variance) { - this.raise( - variance.start, - "Explicit inexact syntax cannot have variance", - ); + this.raise(variance.start, flowErrors.InexactVariance); } return null; } if (!allowSpread) { - this.raise( - this.state.lastTokStart, - "Spread operator cannot appear in class or interface definitions", - ); + this.raise(this.state.lastTokStart, flowErrors.UnexpectedSpreadType); } if (protoStart != null) { this.unexpected(protoStart); } if (variance) { - this.raise(variance.start, "Spread properties cannot have variance"); + this.raise(variance.start, flowErrors.SpreadVariance); } node.argument = this.flowParseType(); @@ -1120,17 +1180,14 @@ export default (superClass: Class): Class => property.value.params.length + (property.value.rest ? 1 : 0); if (length !== paramCount) { if (property.kind === "get") { - this.raise(start, "getter must not have any formal parameters"); + this.raise(start, Errors.BadGetterArity); } else { - this.raise(start, "setter must have exactly one formal parameter"); + this.raise(start, Errors.BadSetterArity); } } if (property.kind === "set" && property.value.rest) { - this.raise( - start, - "setter function argument must not be a rest parameter", - ); + this.raise(start, Errors.BadSetterRestParameter); } } @@ -1437,7 +1494,7 @@ export default (superClass: Class): Class => throw this.raise( this.state.start, - `Unexpected token, expected "number" or "bigint"`, + flowErrors.UnexpectedSubtractionOperand, ); } @@ -1800,10 +1857,7 @@ 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, - "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate.", - ); + this.raise(state.start, flowErrors.AmbiguousConditionalArrow); } if (failed && valid.length === 1) { @@ -2125,10 +2179,7 @@ export default (superClass: Class): Class => (!expr.extra || !expr.extra.parenthesized) && (exprList.length > 1 || !isParenthesizedExpr) ) { - this.raise( - expr.typeAnnotation.start, - "The type cast expression is expected to be wrapped with parenthesis", - ); + this.raise(expr.typeAnnotation.start, flowErrors.TypeCastInPattern); } } @@ -2303,10 +2354,7 @@ export default (superClass: Class): Class => parseAssignableListItemTypes(param: N.Pattern): N.Pattern { if (this.eat(tt.question)) { if (param.type !== "Identifier") { - this.raise( - param.start, - "A binding pattern parameter cannot be optional in an implementation signature.", - ); + this.raise(param.start, flowErrors.OptionalBindingPattern); } ((param: any): N.Identifier).optional = true; @@ -2330,11 +2378,7 @@ export default (superClass: Class): Class => node.typeAnnotation && node.right.start < node.typeAnnotation.start ) { - this.raise( - node.typeAnnotation.start, - "Type annotations must come before default assignments, " + - "e.g. instead of `age = 25: number` use `age: number = 25`", - ); + this.raise(node.typeAnnotation.start, flowErrors.TypeBeforeInitializer); } return node; @@ -2458,8 +2502,7 @@ export default (superClass: Class): Class => if (nodeIsTypeImport && specifierIsTypeImport) { this.raise( firstIdentLoc, - "The `type` and `typeof` keywords on named imports can only be used on regular " + - "`import` statements. It cannot be used with `import type` or `import typeof` statements", + flowErrors.ImportTypeShorthandOnlyInPureImport, ); } @@ -2637,7 +2680,7 @@ export default (superClass: Class): Class => /*:: invariant(typeParameters) */ throw this.raise( typeParameters.start, - "Expected an arrow function after this type parameter declaration", + flowErrors.UnexpectedTokenAfterTypeParameter, ); } @@ -2896,7 +2939,7 @@ 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, "Unterminated flow-comment"); + this.raise(this.state.pos, flowErrors.UnterminatedFlowComment); } return fileNode; } @@ -2904,10 +2947,7 @@ export default (superClass: Class): Class => skipBlockComment(): void { if (this.hasPlugin("flowComments") && this.skipFlowComment()) { if (this.state.hasFlowComment) { - this.unexpected( - null, - "Cannot have a flow comment inside another flow comment", - ); + this.unexpected(null, flowErrors.NestedFlowComment); } this.hasFlowCommentCompletion(); this.state.pos += this.skipFlowComment(); @@ -2918,7 +2958,7 @@ export default (superClass: Class): Class => if (this.state.hasFlowComment) { const end = this.input.indexOf("*-/", (this.state.pos += 2)); if (end === -1) { - throw this.raise(this.state.pos - 2, "Unterminated comment"); + throw this.raise(this.state.pos - 2, Errors.UnterminatedComment); } this.state.pos = end + 3; return; @@ -2961,7 +3001,7 @@ export default (superClass: Class): Class => hasFlowCommentCompletion(): void { const end = this.input.indexOf("*/", this.state.pos); if (end === -1) { - throw this.raise(this.state.pos, "Unterminated comment"); + throw this.raise(this.state.pos, Errors.UnterminatedComment); } } @@ -2973,8 +3013,9 @@ export default (superClass: Class): Class => ): void { this.raise( pos, - `Boolean enum members need to be initialized. Use either \`${memberName} = true,\` ` + - `or \`${memberName} = false,\` in enum \`${enumName}\`.`, + flowErrors.EnumBooleanMemberNotInitialized, + memberName, + enumName, ); } @@ -2985,8 +3026,10 @@ export default (superClass: Class): Class => const suggestion = memberName[0].toUpperCase() + memberName.slice(1); this.raise( pos, - `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using ` + - `\`${memberName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`, + flowErrors.EnumInvalidMemberName, + memberName, + suggestion, + enumName, ); } @@ -2994,22 +3037,14 @@ export default (superClass: Class): Class => pos: number, { enumName, memberName }: { enumName: string, memberName: string }, ): void { - this.raise( - pos, - `Enum member names need to be unique, but the name \`${memberName}\` has already been used ` + - `before in enum \`${enumName}\`.`, - ); + this.raise(pos, flowErrors.EnumDuplicateMemberName, memberName, enumName); } flowEnumErrorInconsistentMemberValues( pos: number, { enumName }: { enumName: string }, ): void { - this.raise( - pos, - `Enum \`${enumName}\` has inconsistent member initializers. Either use no initializers, or ` + - `consistently use literals (either booleans, numbers, or strings) for all member initializers.`, - ); + this.raise(pos, flowErrors.EnumInconsistentMemberValues, enumName); } flowEnumErrorInvalidExplicitType( @@ -3019,14 +3054,14 @@ export default (superClass: Class): Class => suppliedType, }: { enumName: string, suppliedType: null | string }, ) { - const suggestion = - `Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in ` + - `enum \`${enumName}\`.`; - const message = + return this.raise( + pos, suppliedType === null - ? `Supplied enum type is not valid. ${suggestion}` - : `Enum type \`${suppliedType}\` is not valid. ${suggestion}`; - return this.raise(pos, message); + ? flowErrors.EnumInvalidExplicitTypeUnknownSupplied + : flowErrors.EnumInvalidExplicitType, + enumName, + suppliedType, + ); } flowEnumErrorInvalidMemberInitializer( @@ -3038,22 +3073,16 @@ export default (superClass: Class): Class => case "boolean": case "number": case "string": - message = - `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of ` + - `\`${memberName}\` needs to be a ${explicitType} literal.`; + message = flowErrors.EnumInvalidMemberInitializerPrimaryType; break; case "symbol": - message = - `Symbol enum members cannot be initialized. Use \`${memberName},\` in ` + - `enum \`${enumName}\`.`; + message = flowErrors.EnumInvalidMemberInitializerSymbolType; break; default: // null - message = - `The enum member initializer for \`${memberName}\` needs to be a literal (either ` + - `a boolean, number, or string) in enum \`${enumName}\`.`; + message = flowErrors.EnumInvalidMemberInitializerUnknownType; } - return this.raise(pos, message); + return this.raise(pos, message, enumName, memberName, explicitType); } flowEnumErrorNumberMemberNotInitialized( @@ -3062,7 +3091,9 @@ export default (superClass: Class): Class => ): void { this.raise( pos, - `Number enum members need to be initialized, e.g. \`${memberName} = 1\` in enum \`${enumName}\`.`, + flowErrors.EnumNumberMemberNotInitialized, + enumName, + memberName, ); } @@ -3072,8 +3103,8 @@ export default (superClass: Class): Class => ): void { this.raise( pos, - `String enum members need to consistently either all use initializers, or use no initializers, ` + - `in enum \`${enumName}\`.`, + flowErrors.EnumStringMemberInconsistentlyInitailized, + enumName, ); } From 5ededb59e9424807664eebafeeb9c8a8e15da9b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Mon, 2 Mar 2020 21:12:59 -0500 Subject: [PATCH 07/10] refactor: use error message template in typescript plugin --- packages/babel-parser/src/plugins/flow.js | 1 + .../src/plugins/typescript/index.js | 123 +++++++++--------- 2 files changed, 63 insertions(+), 61 deletions(-) diff --git a/packages/babel-parser/src/plugins/flow.js b/packages/babel-parser/src/plugins/flow.js index 75214dd3a9f7..9eaf3c9851a7 100644 --- a/packages/babel-parser/src/plugins/flow.js +++ b/packages/babel-parser/src/plugins/flow.js @@ -44,6 +44,7 @@ const reservedTypes = new Set([ ]); /* eslint sort-keys: "error" */ +// The Errors key follows https://github.com/facebook/flow/blob/master/src/parser/parse_error.ml unless it does not exist const flowErrors = Object.freeze({ AmbiguousConditionalArrow: "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate.", diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 53c16cfb85cd..d981058a079d 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -27,6 +27,7 @@ import TypeScriptScopeHandler from "./scope"; import * as charCodes from "charcodes"; import type { ExpressionErrors } from "../../parser/util"; import { PARAM } from "../../util/production-parameter"; +import { Errors } from "../../parser/location"; type TsModifier = | "readonly" @@ -58,6 +59,42 @@ type ParsingContext = | "TypeMembers" | "TypeParametersOrArguments"; +const tsErrors = Object.freeze({ + ClassMethodHasDeclare: "Class methods cannot have the 'declare' modifier", + ClassMethodHasReadonly: "Class methods cannot have the 'readonly' modifier", + DeclareClassFieldHasInitializer: + "'declare' class fields cannot have an initializer", + DuplicateModifier: "Duplicate modifier: '%0'", + EmptyHeritageClauseType: "'%0' list cannot be empty.", + IndexSignatureHasAbstract: + "Index signatures cannot have the 'abstract' modifier", + IndexSignatureHasAccessibility: + "Index signatures cannot have an accessibility modifier ('%0')", + IndexSignatureHasStatic: "Index signatures cannot have the 'static' modifier", + OptionalTypeBeforeRequired: + "A required element cannot follow an optional element.", + PatternIsOptional: + "A binding pattern parameter cannot be optional in an implementation signature.", + PrivateElementHasAbstract: + "Private elements cannot have the 'abstract' modifier.", + PrivateElementHasAccessibility: + "Private elements cannot have an accessibility modifier ('%0')", + TemplateTypeHasSubstitution: + "Template literal types cannot have any substitution", + TypeAnnotationAfterAssign: + "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`", + UnexpectedReadonly: + "'readonly' type modifier is only permitted on array and tuple literal types.", + UnexpectedTypeAnnotation: "Did not expect a type annotation here.", + UnexpectedTypeCastInParameter: "Unexpected type cast in parameter position.", + UnsupportedImportTypeArgument: + "Argument in a type import must be a string literal", + UnsupportedParameterPropertyKind: + "A parameter property may not be declared using a binding pattern.", + UnsupportedSignatureParameterKind: + "Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got %0", +}); + // Doesn't handle "void" or "null" because those are keywords, not identifiers. function keywordTypeFromName( value: string, @@ -149,7 +186,7 @@ export default (superClass: Class): Class => if (!modifier) break; if (Object.hasOwnProperty.call(modified, modifier)) { - this.raise(startPos, `Duplicate modifier: '${modifier}'`); + this.raise(startPos, tsErrors.DuplicateModifier, modifier); } modified[modifier] = true; } @@ -263,10 +300,7 @@ export default (superClass: Class): Class => this.expect(tt._import); this.expect(tt.parenL); if (!this.match(tt.string)) { - this.raise( - this.state.start, - "Argument in a type import must be a string literal", - ); + this.raise(this.state.start, tsErrors.UnsupportedImportTypeArgument); } // For compatibility to estree we cannot call parseLiteral directly here @@ -402,8 +436,8 @@ export default (superClass: Class): Class => ) { this.raise( pattern.start, - "Name in a signature must be an Identifier, ObjectPattern or ArrayPattern," + - `instead got ${pattern.type}`, + tsErrors.UnsupportedSignatureParameterKind, + pattern.type, ); } return (pattern: any); @@ -601,10 +635,7 @@ export default (superClass: Class): Class => if (elementNode.type === "TSOptionalType") { seenOptionalElement = true; } else if (seenOptionalElement && elementNode.type !== "TSRestType") { - this.raise( - elementNode.start, - "A required element cannot follow an optional element.", - ); + this.raise(elementNode.start, tsErrors.OptionalTypeBeforeRequired); } }); @@ -678,7 +709,7 @@ export default (superClass: Class): Class => if (templateNode.expressions.length > 0) { this.raise( templateNode.expressions[0].start, - "Template literal types cannot have any substitution", + tsErrors.TemplateTypeHasSubstitution, ); } node.literal = templateNode; @@ -790,10 +821,7 @@ export default (superClass: Class): Class => case "TSArrayType": return; default: - this.raise( - node.start, - "'readonly' type modifier is only permitted on array and tuple literal types.", - ); + this.raise(node.start, tsErrors.UnexpectedReadonly); } } @@ -1031,7 +1059,8 @@ export default (superClass: Class): Class => if (containsEsc) { this.raise( this.state.lastTokStart, - "Escape sequence in keyword asserts", + Errors.InvalidEscapedReservedWord, + "asserts", ); } @@ -1098,7 +1127,7 @@ export default (superClass: Class): Class => ); if (!delimitedList.length) { - this.raise(originalStart, `'${descriptor}' list cannot be empty.`); + this.raise(originalStart, tsErrors.EmptyHeritageClauseType, descriptor); } return delimitedList; @@ -1639,10 +1668,7 @@ export default (superClass: Class): Class => if (accessibility) pp.accessibility = accessibility; if (readonly) pp.readonly = readonly; if (elt.type !== "Identifier" && elt.type !== "AssignmentPattern") { - this.raise( - pp.start, - "A parameter property may not be declared using a binding pattern.", - ); + this.raise(pp.start, tsErrors.UnsupportedParameterPropertyKind); } pp.parameter = ((elt: any): N.Identifier | N.AssignmentPattern); return this.finishNode(pp, "TSParameterProperty"); @@ -1941,23 +1967,16 @@ export default (superClass: Class): Class => classBody.body.push(idx); if ((member: any).abstract) { - this.raise( - member.start, - "Index signatures cannot have the 'abstract' modifier", - ); + this.raise(member.start, tsErrors.IndexSignatureHasAbstract); } if (isStatic) { - this.raise( - member.start, - "Index signatures cannot have the 'static' modifier", - ); + this.raise(member.start, tsErrors.IndexSignatureHasStatic); } if ((member: any).accessibility) { this.raise( member.start, - `Index signatures cannot have an accessibility modifier ('${ - (member: any).accessibility - }')`, + tsErrors.IndexSignatureHasAccessibility, + (member: any).accessibility, ); } @@ -1982,17 +2001,11 @@ export default (superClass: Class): Class => if (optional) methodOrProp.optional = true; if ((methodOrProp: any).readonly && this.match(tt.parenL)) { - this.raise( - methodOrProp.start, - "Class methods cannot have the 'readonly' modifier", - ); + this.raise(methodOrProp.start, tsErrors.ClassMethodHasReadonly); } if ((methodOrProp: any).declare && this.match(tt.parenL)) { - this.raise( - methodOrProp.start, - "Class methods cannot have the 'declare' modifier", - ); + this.raise(methodOrProp.start, tsErrors.ClassMethodHasDeclare); } } @@ -2142,10 +2155,7 @@ export default (superClass: Class): Class => this.parseClassPropertyAnnotation(node); if (node.declare && this.match(tt.equal)) { - this.raise( - this.state.start, - "'declare' class fields cannot have an initializer", - ); + this.raise(this.state.start, tsErrors.DeclareClassFieldHasInitializer); } return super.parseClassProperty(node); @@ -2156,17 +2166,15 @@ export default (superClass: Class): Class => ): N.ClassPrivateProperty { // $FlowIgnore if (node.abstract) { - this.raise( - node.start, - "Private elements cannot have the 'abstract' modifier.", - ); + this.raise(node.start, tsErrors.PrivateElementHasAbstract); } // $FlowIgnore if (node.accessibility) { this.raise( node.start, - `Private elements cannot have an accessibility modifier ('${node.accessibility}')`, + tsErrors.PrivateElementHasAccessibility, + node.accessibility, ); } @@ -2389,10 +2397,7 @@ export default (superClass: Class): Class => parseAssignableListItemTypes(param: N.Pattern) { if (this.eat(tt.question)) { if (param.type !== "Identifier") { - this.raise( - param.start, - "A binding pattern parameter cannot be optional in an implementation signature.", - ); + this.raise(param.start, tsErrors.PatternIsOptional); } ((param: any): N.Identifier).optional = true; @@ -2507,8 +2512,7 @@ export default (superClass: Class): Class => ) { this.raise( node.typeAnnotation.start, - "Type annotations must come before default assignments, " + - "e.g. instead of `age = 25: number` use `age: number = 25`", + tsErrors.TypeAnnotationAfterAssign, ); } @@ -2537,10 +2541,7 @@ export default (superClass: Class): Class => if (!this.state.maybeInArrowParameters) { exprList[i] = this.typeCastToParameter(expr); } else { - this.raise( - expr.start, - "Unexpected type cast in parameter position.", - ); + this.raise(expr.start, tsErrors.UnexpectedTypeCastInParameter); } break; } @@ -2567,7 +2568,7 @@ export default (superClass: Class): Class => for (let i = 0; i < exprList.length; i++) { const expr = exprList[i]; if (expr && expr.type === "TSTypeCastExpression") { - this.raise(expr.start, "Did not expect a type annotation here."); + this.raise(expr.start, tsErrors.UnexpectedTypeAnnotation); } } From d3c067eae79dbd28888fe5ae17ec6f1053b5cbd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Mon, 2 Mar 2020 21:29:15 -0500 Subject: [PATCH 08/10] refactor: use error message template in jsx plugin --- .../babel-parser/src/plugins/jsx/index.js | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index fce057e48b74..8e123f6288ba 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -11,10 +11,23 @@ import * as N from "../../types"; import { isIdentifierChar, isIdentifierStart } from "../../util/identifier"; import type { Position } from "../../util/location"; import { isNewLine } from "../../util/whitespace"; +import { Errors } from "../../parser/location"; const HEX_NUMBER = /^[\da-fA-F]+$/; const DECIMAL_NUMBER = /^\d+$/; +const jsxErrors = Object.freeze({ + AttributeIsEmpty: + "JSX attributes must only be assigned a non-empty expression", + MissingClosingTagFragment: "Expected corresponding JSX closing tag for <>", + MissingClosingTagElement: "Expected corresponding JSX closing tag for <%0>", + UnsupportedJsxValue: + "JSX value should be either an expression or a quoted JSX text", + UnterminatedJsxContent: "Unterminated JSX contents", + UnwrappedAdjacentJSXElements: + "Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...?", +}); + // Be aware that this file is always executed and not only when the plugin is enabled. // Therefore this contexts and tokens do always exist. tc.j_oTag = new TokContext("): Class => let chunkStart = this.state.pos; for (;;) { if (this.state.pos >= this.length) { - throw this.raise(this.state.start, "Unterminated JSX contents"); + throw this.raise(this.state.start, jsxErrors.UnterminatedJsxContent); } const ch = this.input.charCodeAt(this.state.pos); @@ -143,7 +156,7 @@ export default (superClass: Class): Class => let chunkStart = ++this.state.pos; for (;;) { if (this.state.pos >= this.length) { - throw this.raise(this.state.start, "Unterminated string constant"); + throw this.raise(this.state.start, Errors.UnterminatedString); } const ch = this.input.charCodeAt(this.state.pos); @@ -280,10 +293,7 @@ export default (superClass: Class): Class => this.next(); node = this.jsxParseExpressionContainer(node); if (node.expression.type === "JSXEmptyExpression") { - this.raise( - node.start, - "JSX attributes must only be assigned a non-empty expression", - ); + this.raise(node.start, jsxErrors.AttributeIsEmpty); } return node; @@ -292,10 +302,7 @@ export default (superClass: Class): Class => return this.parseExprAtom(); default: - throw this.raise( - this.state.start, - "JSX value should be either an expression or a quoted JSX text", - ); + throw this.raise(this.state.start, jsxErrors.UnsupportedJsxValue); } } @@ -450,15 +457,14 @@ export default (superClass: Class): Class => this.raise( // $FlowIgnore closingElement.start, - "Expected corresponding JSX closing tag for <>", + jsxErrors.MissingClosingTagFragment, ); } else if (!isFragment(openingElement) && isFragment(closingElement)) { this.raise( // $FlowIgnore closingElement.start, - "Expected corresponding JSX closing tag for <" + - getQualifiedJSXName(openingElement.name) + - ">", + jsxErrors.MissingClosingTagElement, + getQualifiedJSXName(openingElement.name), ); } else if (!isFragment(openingElement) && !isFragment(closingElement)) { if ( @@ -469,9 +475,8 @@ export default (superClass: Class): Class => this.raise( // $FlowIgnore closingElement.start, - "Expected corresponding JSX closing tag for <" + - getQualifiedJSXName(openingElement.name) + - ">", + jsxErrors.MissingClosingTagElement, + getQualifiedJSXName(openingElement.name), ); } } @@ -488,8 +493,7 @@ export default (superClass: Class): Class => if (this.isRelational("<")) { throw this.raise( this.state.start, - "Adjacent JSX elements must be wrapped in an enclosing tag. " + - "Did you want a JSX fragment <>...?", + jsxErrors.UnwrappedAdjacentJSXElements, ); } From f2b51c2e6f6b7aa3a7356d16049b6abc7087e0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Mon, 2 Mar 2020 21:55:49 -0500 Subject: [PATCH 09/10] address review comments --- packages/babel-parser/src/plugins/flow.js | 78 +++++++++---------- .../babel-parser/src/plugins/jsx/index.js | 16 ++-- .../src/plugins/typescript/index.js | 42 +++++----- 3 files changed, 68 insertions(+), 68 deletions(-) diff --git a/packages/babel-parser/src/plugins/flow.js b/packages/babel-parser/src/plugins/flow.js index 9eaf3c9851a7..0f9958c4a66b 100644 --- a/packages/babel-parser/src/plugins/flow.js +++ b/packages/babel-parser/src/plugins/flow.js @@ -45,7 +45,7 @@ const reservedTypes = new Set([ /* eslint sort-keys: "error" */ // The Errors key follows https://github.com/facebook/flow/blob/master/src/parser/parse_error.ml unless it does not exist -const flowErrors = Object.freeze({ +const FlowErrors = Object.freeze({ AmbiguousConditionalArrow: "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate.", AmbiguousDeclareModuleKind: @@ -245,7 +245,7 @@ export default (superClass: Class): Class => moduloLoc.line !== checksLoc.line || moduloLoc.column !== checksLoc.column - 1 ) { - this.raise(moduloPos, flowErrors.UnexpectedSpaceBetweenModuloChecks); + this.raise(moduloPos, FlowErrors.UnexpectedSpaceBetweenModuloChecks); } if (this.eat(tt.parenL)) { node.value = this.parseExpression(); @@ -338,7 +338,7 @@ export default (superClass: Class): Class => return this.flowParseDeclareModuleExports(node); } else { if (insideModule) { - this.raise(this.state.lastTokStart, flowErrors.NestedDeclareModule); + this.raise(this.state.lastTokStart, FlowErrors.NestedDeclareModule); } return this.flowParseDeclareModule(node); } @@ -387,14 +387,14 @@ export default (superClass: Class): Class => if (!this.isContextual("type") && !this.match(tt._typeof)) { this.raise( this.state.lastTokStart, - flowErrors.InvalidNonTypeImportInDeclareModule, + FlowErrors.InvalidNonTypeImportInDeclareModule, ); } this.parseImport(bodyNode); } else { this.expectContextual( "declare", - flowErrors.UnsupportedStatementInDeclareModule, + FlowErrors.UnsupportedStatementInDeclareModule, ); bodyNode = this.flowParseDeclare(bodyNode, true); @@ -416,7 +416,7 @@ export default (superClass: Class): Class => if (kind === "CommonJS") { this.raise( bodyElement.start, - flowErrors.AmbiguousDeclareModuleKind, + FlowErrors.AmbiguousDeclareModuleKind, ); } kind = "ES"; @@ -424,13 +424,13 @@ export default (superClass: Class): Class => if (hasModuleExport) { this.raise( bodyElement.start, - flowErrors.DuplicateDeclareModuleExports, + FlowErrors.DuplicateDeclareModuleExports, ); } if (kind === "ES") { this.raise( bodyElement.start, - flowErrors.AmbiguousDeclareModuleKind, + FlowErrors.AmbiguousDeclareModuleKind, ); } kind = "CommonJS"; @@ -472,7 +472,7 @@ export default (superClass: Class): Class => const suggestion = exportSuggestions[label]; throw this.raise( this.state.start, - flowErrors.UnsupportedDeclareExportKind, + FlowErrors.UnsupportedDeclareExportKind, label, suggestion, ); @@ -630,7 +630,7 @@ export default (superClass: Class): Class => checkNotUnderscore(word: string) { if (word === "_") { - this.raise(this.state.start, flowErrors.UnexpectedReservedUnderscore); + this.raise(this.state.start, FlowErrors.UnexpectedReservedUnderscore); } } @@ -640,8 +640,8 @@ export default (superClass: Class): Class => this.raise( startLoc, declaration - ? flowErrors.AssignReservedType - : flowErrors.UnexpectedReservedType, + ? FlowErrors.AssignReservedType + : FlowErrors.UnexpectedReservedType, word, ); } @@ -726,7 +726,7 @@ export default (superClass: Class): Class => node.default = this.flowParseType(); } else { if (requireDefault) { - this.raise(nodeStart, flowErrors.MissingTypeParamDefault); + this.raise(nodeStart, FlowErrors.MissingTypeParamDefault); } } @@ -1061,7 +1061,7 @@ export default (superClass: Class): Class => ) { this.raise( inexactStart, - flowErrors.UnexpectedExplicitInexactInObject, + FlowErrors.UnexpectedExplicitInexactInObject, ); } } @@ -1104,26 +1104,26 @@ export default (superClass: Class): Class => if (!allowSpread) { this.raise( this.state.lastTokStart, - flowErrors.InexactInsideNonObject, + FlowErrors.InexactInsideNonObject, ); } else if (!allowInexact) { - this.raise(this.state.lastTokStart, flowErrors.InexactInsideExact); + this.raise(this.state.lastTokStart, FlowErrors.InexactInsideExact); } if (variance) { - this.raise(variance.start, flowErrors.InexactVariance); + this.raise(variance.start, FlowErrors.InexactVariance); } return null; } if (!allowSpread) { - this.raise(this.state.lastTokStart, flowErrors.UnexpectedSpreadType); + this.raise(this.state.lastTokStart, FlowErrors.UnexpectedSpreadType); } if (protoStart != null) { this.unexpected(protoStart); } if (variance) { - this.raise(variance.start, flowErrors.SpreadVariance); + this.raise(variance.start, FlowErrors.SpreadVariance); } node.argument = this.flowParseType(); @@ -1495,7 +1495,7 @@ export default (superClass: Class): Class => throw this.raise( this.state.start, - flowErrors.UnexpectedSubtractionOperand, + FlowErrors.UnexpectedSubtractionOperand, ); } @@ -1858,7 +1858,7 @@ 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(state.start, FlowErrors.AmbiguousConditionalArrow); } if (failed && valid.length === 1) { @@ -2180,7 +2180,7 @@ export default (superClass: Class): Class => (!expr.extra || !expr.extra.parenthesized) && (exprList.length > 1 || !isParenthesizedExpr) ) { - this.raise(expr.typeAnnotation.start, flowErrors.TypeCastInPattern); + this.raise(expr.typeAnnotation.start, FlowErrors.TypeCastInPattern); } } @@ -2355,7 +2355,7 @@ 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.OptionalBindingPattern); + this.raise(param.start, FlowErrors.OptionalBindingPattern); } ((param: any): N.Identifier).optional = true; @@ -2379,7 +2379,7 @@ export default (superClass: Class): Class => node.typeAnnotation && node.right.start < node.typeAnnotation.start ) { - this.raise(node.typeAnnotation.start, flowErrors.TypeBeforeInitializer); + this.raise(node.typeAnnotation.start, FlowErrors.TypeBeforeInitializer); } return node; @@ -2503,7 +2503,7 @@ export default (superClass: Class): Class => if (nodeIsTypeImport && specifierIsTypeImport) { this.raise( firstIdentLoc, - flowErrors.ImportTypeShorthandOnlyInPureImport, + FlowErrors.ImportTypeShorthandOnlyInPureImport, ); } @@ -2681,7 +2681,7 @@ export default (superClass: Class): Class => /*:: invariant(typeParameters) */ throw this.raise( typeParameters.start, - flowErrors.UnexpectedTokenAfterTypeParameter, + FlowErrors.UnexpectedTokenAfterTypeParameter, ); } @@ -2940,7 +2940,7 @@ 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(this.state.pos, FlowErrors.UnterminatedFlowComment); } return fileNode; } @@ -2948,7 +2948,7 @@ export default (superClass: Class): Class => skipBlockComment(): void { if (this.hasPlugin("flowComments") && this.skipFlowComment()) { if (this.state.hasFlowComment) { - this.unexpected(null, flowErrors.NestedFlowComment); + this.unexpected(null, FlowErrors.NestedFlowComment); } this.hasFlowCommentCompletion(); this.state.pos += this.skipFlowComment(); @@ -3014,7 +3014,7 @@ export default (superClass: Class): Class => ): void { this.raise( pos, - flowErrors.EnumBooleanMemberNotInitialized, + FlowErrors.EnumBooleanMemberNotInitialized, memberName, enumName, ); @@ -3027,7 +3027,7 @@ export default (superClass: Class): Class => const suggestion = memberName[0].toUpperCase() + memberName.slice(1); this.raise( pos, - flowErrors.EnumInvalidMemberName, + FlowErrors.EnumInvalidMemberName, memberName, suggestion, enumName, @@ -3038,14 +3038,14 @@ export default (superClass: Class): Class => pos: number, { enumName, memberName }: { enumName: string, memberName: string }, ): void { - this.raise(pos, flowErrors.EnumDuplicateMemberName, memberName, enumName); + this.raise(pos, FlowErrors.EnumDuplicateMemberName, memberName, enumName); } flowEnumErrorInconsistentMemberValues( pos: number, { enumName }: { enumName: string }, ): void { - this.raise(pos, flowErrors.EnumInconsistentMemberValues, enumName); + this.raise(pos, FlowErrors.EnumInconsistentMemberValues, enumName); } flowEnumErrorInvalidExplicitType( @@ -3058,8 +3058,8 @@ export default (superClass: Class): Class => return this.raise( pos, suppliedType === null - ? flowErrors.EnumInvalidExplicitTypeUnknownSupplied - : flowErrors.EnumInvalidExplicitType, + ? FlowErrors.EnumInvalidExplicitTypeUnknownSupplied + : FlowErrors.EnumInvalidExplicitType, enumName, suppliedType, ); @@ -3074,14 +3074,14 @@ export default (superClass: Class): Class => case "boolean": case "number": case "string": - message = flowErrors.EnumInvalidMemberInitializerPrimaryType; + message = FlowErrors.EnumInvalidMemberInitializerPrimaryType; break; case "symbol": - message = flowErrors.EnumInvalidMemberInitializerSymbolType; + message = FlowErrors.EnumInvalidMemberInitializerSymbolType; break; default: // null - message = flowErrors.EnumInvalidMemberInitializerUnknownType; + message = FlowErrors.EnumInvalidMemberInitializerUnknownType; } return this.raise(pos, message, enumName, memberName, explicitType); } @@ -3092,7 +3092,7 @@ export default (superClass: Class): Class => ): void { this.raise( pos, - flowErrors.EnumNumberMemberNotInitialized, + FlowErrors.EnumNumberMemberNotInitialized, enumName, memberName, ); @@ -3104,7 +3104,7 @@ export default (superClass: Class): Class => ): void { this.raise( pos, - flowErrors.EnumStringMemberInconsistentlyInitailized, + FlowErrors.EnumStringMemberInconsistentlyInitailized, enumName, ); } diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index 8e123f6288ba..6158cae7579a 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -16,7 +16,7 @@ import { Errors } from "../../parser/location"; const HEX_NUMBER = /^[\da-fA-F]+$/; const DECIMAL_NUMBER = /^\d+$/; -const jsxErrors = Object.freeze({ +const JsxErrors = Object.freeze({ AttributeIsEmpty: "JSX attributes must only be assigned a non-empty expression", MissingClosingTagFragment: "Expected corresponding JSX closing tag for <>", @@ -96,7 +96,7 @@ 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(this.state.start, JsxErrors.UnterminatedJsxContent); } const ch = this.input.charCodeAt(this.state.pos); @@ -293,7 +293,7 @@ export default (superClass: Class): Class => this.next(); node = this.jsxParseExpressionContainer(node); if (node.expression.type === "JSXEmptyExpression") { - this.raise(node.start, jsxErrors.AttributeIsEmpty); + this.raise(node.start, JsxErrors.AttributeIsEmpty); } return node; @@ -302,7 +302,7 @@ export default (superClass: Class): Class => return this.parseExprAtom(); default: - throw this.raise(this.state.start, jsxErrors.UnsupportedJsxValue); + throw this.raise(this.state.start, JsxErrors.UnsupportedJsxValue); } } @@ -457,13 +457,13 @@ export default (superClass: Class): Class => this.raise( // $FlowIgnore closingElement.start, - jsxErrors.MissingClosingTagFragment, + JsxErrors.MissingClosingTagFragment, ); } else if (!isFragment(openingElement) && isFragment(closingElement)) { this.raise( // $FlowIgnore closingElement.start, - jsxErrors.MissingClosingTagElement, + JsxErrors.MissingClosingTagElement, getQualifiedJSXName(openingElement.name), ); } else if (!isFragment(openingElement) && !isFragment(closingElement)) { @@ -475,7 +475,7 @@ export default (superClass: Class): Class => this.raise( // $FlowIgnore closingElement.start, - jsxErrors.MissingClosingTagElement, + JsxErrors.MissingClosingTagElement, getQualifiedJSXName(openingElement.name), ); } @@ -493,7 +493,7 @@ export default (superClass: Class): Class => if (this.isRelational("<")) { throw this.raise( this.state.start, - jsxErrors.UnwrappedAdjacentJSXElements, + JsxErrors.UnwrappedAdjacentJSXElements, ); } diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index d981058a079d..9b6273c649e8 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -59,7 +59,7 @@ type ParsingContext = | "TypeMembers" | "TypeParametersOrArguments"; -const tsErrors = Object.freeze({ +const TSErrors = Object.freeze({ ClassMethodHasDeclare: "Class methods cannot have the 'declare' modifier", ClassMethodHasReadonly: "Class methods cannot have the 'readonly' modifier", DeclareClassFieldHasInitializer: @@ -186,7 +186,7 @@ export default (superClass: Class): Class => if (!modifier) break; if (Object.hasOwnProperty.call(modified, modifier)) { - this.raise(startPos, tsErrors.DuplicateModifier, modifier); + this.raise(startPos, TSErrors.DuplicateModifier, modifier); } modified[modifier] = true; } @@ -300,7 +300,7 @@ 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(this.state.start, TSErrors.UnsupportedImportTypeArgument); } // For compatibility to estree we cannot call parseLiteral directly here @@ -436,7 +436,7 @@ export default (superClass: Class): Class => ) { this.raise( pattern.start, - tsErrors.UnsupportedSignatureParameterKind, + TSErrors.UnsupportedSignatureParameterKind, pattern.type, ); } @@ -635,7 +635,7 @@ export default (superClass: Class): Class => if (elementNode.type === "TSOptionalType") { seenOptionalElement = true; } else if (seenOptionalElement && elementNode.type !== "TSRestType") { - this.raise(elementNode.start, tsErrors.OptionalTypeBeforeRequired); + this.raise(elementNode.start, TSErrors.OptionalTypeBeforeRequired); } }); @@ -709,7 +709,7 @@ export default (superClass: Class): Class => if (templateNode.expressions.length > 0) { this.raise( templateNode.expressions[0].start, - tsErrors.TemplateTypeHasSubstitution, + TSErrors.TemplateTypeHasSubstitution, ); } node.literal = templateNode; @@ -821,7 +821,7 @@ export default (superClass: Class): Class => case "TSArrayType": return; default: - this.raise(node.start, tsErrors.UnexpectedReadonly); + this.raise(node.start, TSErrors.UnexpectedReadonly); } } @@ -1127,7 +1127,7 @@ export default (superClass: Class): Class => ); if (!delimitedList.length) { - this.raise(originalStart, tsErrors.EmptyHeritageClauseType, descriptor); + this.raise(originalStart, TSErrors.EmptyHeritageClauseType, descriptor); } return delimitedList; @@ -1668,7 +1668,7 @@ export default (superClass: Class): Class => if (accessibility) pp.accessibility = accessibility; if (readonly) pp.readonly = readonly; if (elt.type !== "Identifier" && elt.type !== "AssignmentPattern") { - this.raise(pp.start, tsErrors.UnsupportedParameterPropertyKind); + this.raise(pp.start, TSErrors.UnsupportedParameterPropertyKind); } pp.parameter = ((elt: any): N.Identifier | N.AssignmentPattern); return this.finishNode(pp, "TSParameterProperty"); @@ -1967,15 +1967,15 @@ export default (superClass: Class): Class => classBody.body.push(idx); if ((member: any).abstract) { - this.raise(member.start, tsErrors.IndexSignatureHasAbstract); + this.raise(member.start, TSErrors.IndexSignatureHasAbstract); } if (isStatic) { - this.raise(member.start, tsErrors.IndexSignatureHasStatic); + this.raise(member.start, TSErrors.IndexSignatureHasStatic); } if ((member: any).accessibility) { this.raise( member.start, - tsErrors.IndexSignatureHasAccessibility, + TSErrors.IndexSignatureHasAccessibility, (member: any).accessibility, ); } @@ -2001,11 +2001,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(methodOrProp.start, TSErrors.ClassMethodHasReadonly); } if ((methodOrProp: any).declare && this.match(tt.parenL)) { - this.raise(methodOrProp.start, tsErrors.ClassMethodHasDeclare); + this.raise(methodOrProp.start, TSErrors.ClassMethodHasDeclare); } } @@ -2155,7 +2155,7 @@ export default (superClass: Class): Class => this.parseClassPropertyAnnotation(node); if (node.declare && this.match(tt.equal)) { - this.raise(this.state.start, tsErrors.DeclareClassFieldHasInitializer); + this.raise(this.state.start, TSErrors.DeclareClassFieldHasInitializer); } return super.parseClassProperty(node); @@ -2166,14 +2166,14 @@ export default (superClass: Class): Class => ): N.ClassPrivateProperty { // $FlowIgnore if (node.abstract) { - this.raise(node.start, tsErrors.PrivateElementHasAbstract); + this.raise(node.start, TSErrors.PrivateElementHasAbstract); } // $FlowIgnore if (node.accessibility) { this.raise( node.start, - tsErrors.PrivateElementHasAccessibility, + TSErrors.PrivateElementHasAccessibility, node.accessibility, ); } @@ -2397,7 +2397,7 @@ export default (superClass: Class): Class => parseAssignableListItemTypes(param: N.Pattern) { if (this.eat(tt.question)) { if (param.type !== "Identifier") { - this.raise(param.start, tsErrors.PatternIsOptional); + this.raise(param.start, TSErrors.PatternIsOptional); } ((param: any): N.Identifier).optional = true; @@ -2512,7 +2512,7 @@ export default (superClass: Class): Class => ) { this.raise( node.typeAnnotation.start, - tsErrors.TypeAnnotationAfterAssign, + TSErrors.TypeAnnotationAfterAssign, ); } @@ -2541,7 +2541,7 @@ export default (superClass: Class): Class => if (!this.state.maybeInArrowParameters) { exprList[i] = this.typeCastToParameter(expr); } else { - this.raise(expr.start, tsErrors.UnexpectedTypeCastInParameter); + this.raise(expr.start, TSErrors.UnexpectedTypeCastInParameter); } break; } @@ -2568,7 +2568,7 @@ export default (superClass: Class): Class => for (let i = 0; i < exprList.length; i++) { const expr = exprList[i]; if (expr && expr.type === "TSTypeCastExpression") { - this.raise(expr.start, tsErrors.UnexpectedTypeAnnotation); + this.raise(expr.start, TSErrors.UnexpectedTypeAnnotation); } } From 1af4064bd1e104ead161e1a35856fed82859d839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 3 Mar 2020 11:31:00 -0500 Subject: [PATCH 10/10] Update packages/babel-parser/src/parser/location.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Nicolò Ribaudo --- packages/babel-parser/src/parser/location.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/babel-parser/src/parser/location.js b/packages/babel-parser/src/parser/location.js index e89c6f36ebbc..fc548b0b726e 100644 --- a/packages/babel-parser/src/parser/location.js +++ b/packages/babel-parser/src/parser/location.js @@ -225,17 +225,7 @@ export default class LocationParser extends CommentsParser { const message = errorTemplate.replace(/%(\d+)/g, (_, i: number) => params[i]) + ` (${loc.line}:${loc.column})`; - return this._raise( - Object.assign( - {}, - { - loc, - pos, - }, - data, - ), - message, - ); + return this._raise(Object.assign(({ loc, pos }: Object), data), message); } _raise(errorContext: ErrorContext, message: string): Error | empty {