From ad551b67e1eb618a9c9a1c6227b68173ba42e846 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 8 Feb 2022 12:53:55 -0800 Subject: [PATCH 001/104] Flow ParseError version. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 84 ++++ .../babel-parser/src/parse-error/standard.js | 380 ++++++++++++++++++ .../src/parse-error/strict-mode.js | 11 + .../babel-parser/src/parser/error-message.js | 259 ------------ packages/babel-parser/src/tokenizer/index.js | 34 +- 5 files changed, 505 insertions(+), 263 deletions(-) create mode 100644 packages/babel-parser/src/parse-error.js create mode 100644 packages/babel-parser/src/parse-error/standard.js create mode 100644 packages/babel-parser/src/parse-error/strict-mode.js delete mode 100644 packages/babel-parser/src/parser/error-message.js diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js new file mode 100644 index 000000000000..df9f7e2333a0 --- /dev/null +++ b/packages/babel-parser/src/parse-error.js @@ -0,0 +1,84 @@ +// @flow + +import { Position } from "./util/location"; +import type { Node } from "./types"; + +const { assign: ObjectAssign } = Object; + +export enum ParseErrorCode { + SyntaxError = "BABEL_PARSER_SYNTAX_ERROR", + SourceTypeModuleError = "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", +} + +type ToMessage = (self: ErrorProperties) => string; + +class ParseError extends SyntaxError { + static reasonCode: string; + static toMessage: ToMessage; + + name: string = "SyntaxError"; + + code: ParseErrorCode = ParseErrorCode.SyntaxError; + reasonCode: string = this.constructor.reasonCode; + + loc: Position; + pos: number; + + constructor(properties: {| + ...ErrorProperties, + loc: Position, + |}): ParseError { + super(); + + return ObjectAssign(this, { + ...properties, + pos: properties.loc.index, + }); + } +} + +declare function toReasonCodelessParseErrorClass( + T +): Class>; +declare function toReasonCodelessParseErrorClass( + (T) => string +): Class>; + +function toReasonCodelessParseErrorClass(toMessageOrMessage) { + return fromToMessage( + typeof toMessageOrMessage === "string" + ? () => toMessageOrMessage + : toMessageOrMessage + ); +} + +function fromToMessage(toMessage: ToMessage): Class> { + return class extends ParseError { + static toMessage = toMessage; + }; +} + +export function toParseErrorClasses( + toClasses: (typeof toReasonCodelessParseErrorClass) => T +): T { + // $FlowIgnore + return Object.fromEntries( + Object.entries(toClasses(toReasonCodelessParseErrorClass)).map( + ([reasonCode, ParseErrorClass]) => + // $FlowIgnore + Object.assign(ParseErrorClass, { reasonCode }) + ) + ); +} + + +type Origin = {| at: Position |} | {| at: Node |}; +export type RaiseProperties = {| + ...ErrorProperties, + ...Origin, +|}; + +import StandardErrors from "./parse-error/standard"; +import StrictErrors from "./parse-error/strict-mode"; + +export const Errors = { ...StandardErrors, ...StrictErrors }; diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js new file mode 100644 index 000000000000..aff5eaf055b5 --- /dev/null +++ b/packages/babel-parser/src/parse-error/standard.js @@ -0,0 +1,380 @@ +// @flow + +import { Position } from "../util/location"; +import { toParseErrorClasses } from "../parse-error"; +import { type TokenType, tokenLabelName } from "../tokenizer/types"; + +export default toParseErrorClasses(_ => ({ + + AccessorIsGenerator: _<{ accessor: string }>(({ accessor }) => `A ${accessor} cannot be a generator`), + + ArgumentsInClass: _( + "'arguments' is only allowed in functions and class methods." + ), + 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." + ), + AwaitBindingIdentifierInStaticBlock: _( + "Can not use 'await' as identifier inside a static block." + ), + AwaitExpressionFormalParameter: _( + "'await' is not allowed in async function parameters." + ), + AwaitNotInAsyncContext: _( + "'await' is only allowed within async functions and at the top levels of modules." + ), + AwaitNotInAsyncFunction: _("'await' is only allowed within async functions."), + BadGetterArity: _("A 'get' accesor must not have any formal parameters."), + BadSetterArity: _("A 'set' accesor must have exactly one formal parameter."), + BadSetterRestParameter: _( + "A 'set' accesor 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'." + ), + ConstructorIsAccessor: _("Class constructor may not be an accessor."), + ConstructorIsAsync: _("Constructor can't be an async function."), + ConstructorIsGenerator: _("Constructor can't be a generator."), + DeclarationMissingInitializer: _<{ declaration: string }>( + ({ declaration }) => `${declaration}' 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."), + DecoratorStaticBlock: _("Decorators can't be used with a static block."), + 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: _<{| export: string |}>( + ({ export: name }) => + `\`${name}\` 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."), + ExportBindingIsString: _<{ localBinding: string, exportBinding: string }>(({ localBinding, exportBinding }) => + "A string literal cannot be used as an exported binding without `from`.\n- Did you mean `export { '${localBinding}' as '{exportBinding}' } from 'some-module'`?" + ), + ExportDefaultFromAsIdentifier: _( + "'from' is not allowed as an identifier after 'export default'." + ), + + ForInOfLoopInitializer: _<{ construct: "for-in" | "for-of" }>(({ construct }) => + `'${construct}' loop variable declaration may not have an initializer.` + ), + + ForOfAsync: _("The left-hand side of a for-of loop may not be 'async'."), + ForOfLet: _("The left-hand side of a for-of loop may not start with 'let'."), + GeneratorInSingleStatementContext: _( + "Generators can only be declared at the top level or inside a block." + ), + + IllegalBreakContinue: _<{ construct: "break" | "continue" }>(({ construct }) => + `Unsyntactic ${construct}.` + ), + + IllegalLanguageModeDirective: _( + "Illegal 'use strict' directive in function with non-simple parameter list." + ), + IllegalReturn: _("'return' outside of function."), + ImportBindingIsString: _<{ importBinding: string }>(({ importBinding }) => + `A string literal cannot be used as an imported binding.\n- Did you mean \`import { "${importBinding}" as foo }\`?` + ), + ImportCallArgumentTrailingComma: _( + "Trailing comma is disallowed inside import(...) arguments." + ), + ImportCallArity: _<{ required: 1 | 1 }>( + ({ required }) => + `\`import()\` requires exactly ${ + required === 1 ? "one argument" : "one or two arguments" + }.` + ), + ImportCallNotNewExpression: _("Cannot use new with import(...)."), + ImportCallSpreadArgument: _("`...` is not allowed in `import()`."), + IncompatibleRegExpUVFlags: _( + "The 'u' and 'v' regular expression flags cannot be enabled at the same time." + ), + InvalidBigIntLiteral: _("Invalid BigIntLiteral."), + InvalidCodePoint: _("Code point out of bounds."), + InvalidCoverInitializedName: _("Invalid shorthand property initializer."), + InvalidDecimal: _("Invalid decimal."), + InvalidDigit: _<{ radix: number }>( + ({ radix }) => `Expected number in radix ${radix}.` + ), + InvalidEscapeSequence: _("Bad character escape sequence."), + InvalidEscapeSequenceTemplate: _("Invalid escape sequence in template."), + InvalidEscapedReservedWord: _<{ keyword: string }>( + ({ keyword }) => `Escape sequence in keyword ${keyword}.` + ), + InvalidIdentifier: _<{ identifier: string }>( + ({ identifier }) => `Invalid identifier ${identifier}.` + ), + InvalidLhs: _<{ contextDescription: string }>( + ({ contextDescription }) => + `Invalid left-hand side in ${contextDescription}.` + ), + InvalidLhsBinding: _<{ contextDescription: string }>( + ({ contextDescription }) => + `Binding invalid left-hand side in ${contextDescription}.` + ), + InvalidNumber: _("Invalid number."), + InvalidOrMissingExponent: _( + "Floating-point numbers require a valid exponent after the 'e'." + ), + InvalidOrUnexpectedToken: _<{ found: string }>( + ({ found }) => `Unexpected character '${found}'.` + ), + InvalidParenthesizedAssignment: _( + "Invalid parenthesized assignment pattern." + ), + InvalidPrivateFieldResolution: _<{ name: string }>( + ({ name }) => `Private name #${name} is not defined.` + ), + InvalidPropertyBindingPattern: _("Binding member expression."), + InvalidRecordProperty: _( + "Only properties and spread elements are allowed in record definitions." + ), + InvalidRestAssignmentPattern: _("Invalid rest operator's argument."), + LabelRedeclaration: _<{ label: string }>(({ label }) => + `Label '${label}' is already declared.` + ), + LetInLexicalBinding: _( + "'let' is not allowed to be used as a name in 'let' or 'const' declarations." + ), + LineTerminatorBeforeArrow: _("No line break is allowed before '=>'."), + MalformedRegExpFlags: _("Invalid regular expression flag."), + MissingClassName: _("A class name is required."), + MissingEqInAssignment: _( + "Only '=' operator can be used for specifying default value." + ), + MissingSemicolon: _("Missing semicolon."), + MissingPlugin: _<{ missingPlugin: string }>( + ({ missingPlugin }) => + `This experimental syntax requires enabling the parser plugin: "${missingPlugin}".` + ), + // FIXME: Would be nice to make this "missingPlugins" instead. + // Also), seems like we can drop the "(s)" from the message and just make it "s". + MissingOneOfPlugins: _<{ missingPlugin: string[] }>( + ({ missingPlugin }) => + `This experimental syntax requires enabling one of the following parser plugin(s): ${missingPlugin + .map((name) => JSON.stringify(name)) + .join("), ")}.` + ), + MissingUnicodeEscape: _("Expecting Unicode escape sequence \\uXXXX."), + MixingCoalesceWithLogical: _( + "Nullish coalescing operator(??) requires parens when mixing with logical operators." + ), + ModuleAttributeDifferentFromType: _( + "The only accepted module attribute is `type`." + ), + ModuleAttributeInvalidValue: _( + "Only string literals are allowed as module attribute values." + ), + ModuleAttributesWithDuplicateKeys: _<{ key: string }>( + ({ key }) => `Duplicate key "${key}" is not allowed in module attributes.` + ), + ModuleExportNameHasLoneSurrogate: + _ < + { surrogateCharCode: number }>(({ surrogateCharCode }) => + `An export name cannot include a lone surrogate), found '\\u${surrogateCharCode.toString( + 16 + )}'.` + ), + ModuleExportUndefined: _<{ moduleExportName: string }>(({ moduleExportName }) => + `Export '${moduleExportName}' 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." + ), + OverrideOnConstructor: _( + "'override' modifier cannot appear on a constructor declaration." + ), + ParamDupe: _("Argument name clash."), + PatternHasAccessor: _("Object pattern can't contain getter or setter."), + PatternHasMethod: _("Object pattern can't contain methods."), + // This error is only used by the smart-mix proposal + PipeBodyIsTighter: _<{ expressionDescription: string }>( + ({ expressionDescription }) => + `Unexpected ${expressionDescription} after pipeline body; any ${expressionDescription} expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence.` + ), + PipeTopicRequiresHackPipes: _( + 'Topic reference is used), but the pipelineOperator plugin was not passed a "proposal": _("hack" or "smart" option.' + ), + PipeTopicUnbound: _( + "Topic reference is unbound; it must be inside a pipe body." + ), + PipeTopicUnconfiguredToken: _<{ token: string }>(({ token }) => + `Invalid topic token ${token}. In order to use ${token} as a topic reference), the pipelineOperator plugin must be configured with { "proposal": _("hack"), "topicToken": _("${token}" }.` + ), + PipeTopicUnused: _( + "Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once." + ), + PipeUnparenthesizedBody: _<{ expressionDescription: string }>( + ({ expressionDescription }) => + `Hack-style pipe body cannot be an unparenthesized ${expressionDescription} expression; please wrap it in parentheses.` + ), + + // Messages whose codes start with “Pipeline” or “PrimaryTopic” + // are retained for backwards compatibility + // with the deprecated smart-mix pipe operator proposal plugin. + // They are subject to removal in a future major version. + 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: _( + 'Topic reference is used), but the pipelineOperator plugin was not passed a "proposal": _("hack" or "smart" option.' + ), + + PrivateInExpectedIn: _<{ name: string }>( + ({ name }) => + `Private names are only allowed in property accesses (\`obj.#${name}\`) or in \`in\` expressions (\`#${name} in obj\`).` + ), + PrivateNameRedeclaration: _<{ name: string }>( + ({ name }) => `Duplicate private name #${name}.` + ), + RecordExpressionBarIncorrectEndSyntaxType: _( + "Record expressions ending with '|}' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'." + ), + RecordExpressionBarIncorrectStartSyntaxType: _( + "Record expressions starting with '{|' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'." + ), + RecordExpressionHashIncorrectStartSyntaxType: _( + "Record expressions starting with '#{' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'hash'." + ), + RecordNoProto: _("'__proto__' is not allowed in Record expressions."), + 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."), + 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."), + TrailingDecorator: _("Decorators must be attached to a class element."), + TupleExpressionBarIncorrectEndSyntaxType: _( + "Tuple expressions ending with '|]' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'." + ), + TupleExpressionBarIncorrectStartSyntaxType: _( + "Tuple expressions starting with '[|' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'." + ), + TupleExpressionHashIncorrectStartSyntaxType: _( + "Tuple expressions starting with '#[' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'hash'." + ), + 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: _<{ keyword: string }>( + ({ keyword }) => `Unexpected keyword '${keyword}'.` + ), + 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 or class properties." + ), + UnexpectedNumericSeparator: _( + "A numeric separator is only allowed between two digits." + ), + UnexpectedPrivateField: _("Unexpected private name."), + UnexpectedReservedWord: _<{ reservedWord: string }>( + ({ reservedWord }) => `Unexpected reserved word '${reservedWord}'.` + ), + UnexpectedSuper: _("'super' is only allowed in object methods and classes."), + UnexpectedToken: _<{ + loc: Position, + expected?: string, + }>(({ loc: { line, column }, expected }) => + !!expected + ? `Unexpected token), expected "${expected}"` + : "Unexpected token" + ), + UnexpectedTokenUnaryExponentiation: _( + "Illegal expression. Wrap left hand side or entire exponentiation in parentheses." + ), + UnsupportedBind: _("Binding should be performed on object property."), + UnsupportedDecoratorExport: _( + "A decorated export must export a class declaration." + ), + UnsupportedDefaultExport: _( + "Only expressions), functions or classes are allowed as the `default` export." + ), + UnsupportedImport: _( + "`import` can only be used in `import()` or `import.meta`." + ), + UnsupportedMetaProperty: _<{ target: string, onlyValidProperty: string }>( + ({ target, onlyValidProperty }) => + "The only valid meta property for ${target} is ${target}.${onlyValidProperty}." + ), + UnsupportedParameterDecorator: _( + "Decorators cannot be used to decorate parameters." + ), + UnsupportedPropertyDecorator: _( + "Decorators cannot be used to decorate object literal properties." + ), + 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: _<{ name: string }>( + ({ name }) => `Identifier '${name}' has already been declared.` + ), + YieldBindingIdentifier: _( + "Can not use 'yield' as identifier inside a generator." + ), + YieldInParameter: _("Yield expression is not allowed in formal parameters."), + ZeroDigitNumericSeparator: _( + "Numeric separator can not be used after leading 0." + ), +})); diff --git a/packages/babel-parser/src/parse-error/strict-mode.js b/packages/babel-parser/src/parse-error/strict-mode.js new file mode 100644 index 000000000000..faa616bfa363 --- /dev/null +++ b/packages/babel-parser/src/parse-error/strict-mode.js @@ -0,0 +1,11 @@ +import { toParseErrorClasses } from "../parse-error"; + +export default toParseErrorClasses(_ => { + StrictDelete: _("Deleting local variable in strict mode."), + StrictEvalArguments: _<{ name: string }>(({ name }) => `Assigning to '${name}' in strict mode.`), + StrictEvalArgumentsBinding: _<{ name: string }>(({ name }) => `Binding '${name}' in strict mode.`), + StrictFunction: _("In strict mode code, functions can only be declared at top level or inside a block."), + StrictNumericEscape: _("The only valid numeric escape in strict mode is '\\0'."), + StrictOctalLiteral: _("Legacy octal literals are not allowed in strict mode."), + StrictWith: _("'with' in strict mode."), +}); diff --git a/packages/babel-parser/src/parser/error-message.js b/packages/babel-parser/src/parser/error-message.js deleted file mode 100644 index 3cdd01309785..000000000000 --- a/packages/babel-parser/src/parser/error-message.js +++ /dev/null @@ -1,259 +0,0 @@ -// @flow - -import { makeErrorTemplates, ErrorCodes } from "./error"; - -/* eslint sort-keys: "error" */ - -/** - * @module parser/error-message - */ - -// The Errors key follows https://cs.chromium.org/chromium/src/v8/src/common/message-template.h unless it does not exist -export const ErrorMessages = makeErrorTemplates( - { - AccessorIsGenerator: "A %0ter cannot be a generator.", - ArgumentsInClass: - "'arguments' is only allowed in functions and class methods.", - 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.", - AwaitBindingIdentifierInStaticBlock: - "Can not use 'await' as identifier inside a static block.", - AwaitExpressionFormalParameter: - "'await' is not allowed in async function parameters.", - AwaitNotInAsyncContext: - "'await' is only allowed within async functions and at the top levels of modules.", - AwaitNotInAsyncFunction: "'await' is only allowed within async functions.", - BadGetterArity: "A 'get' accesor must not have any formal parameters.", - BadSetterArity: "A 'set' accesor must have exactly one formal parameter.", - BadSetterRestParameter: - "A 'set' accesor 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'.", - ConstructorIsAccessor: "Class constructor may not be an accessor.", - 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.", - DecoratorStaticBlock: "Decorators can't be used with a static block.", - 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.", - ExportBindingIsString: - "A string literal cannot be used as an exported binding without `from`.\n- Did you mean `export { '%0' as '%1' } from 'some-module'`?", - ExportDefaultFromAsIdentifier: - "'from' is not allowed as an identifier after 'export default'.", - ForInOfLoopInitializer: - "'%0' loop variable declaration may not have an initializer.", - ForOfAsync: "The left-hand side of a for-of loop may not be 'async'.", - ForOfLet: "The left-hand side of a for-of loop may not start with 'let'.", - 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.", - ImportBindingIsString: - 'A string literal cannot be used as an imported binding.\n- Did you mean `import { "%0" as foo }`?', - ImportCallArgumentTrailingComma: - "Trailing comma is disallowed inside import(...) arguments.", - ImportCallArity: "`import()` requires exactly %0.", - ImportCallNotNewExpression: "Cannot use new with import(...).", - ImportCallSpreadArgument: "`...` is not allowed in `import()`.", - IncompatibleRegExpUVFlags: - "The 'u' and 'v' regular expression flags cannot be enabled at the same time.", - InvalidBigIntLiteral: "Invalid BigIntLiteral.", - InvalidCodePoint: "Code point out of bounds.", - InvalidCoverInitializedName: "Invalid shorthand property initializer.", - InvalidDecimal: "Invalid decimal.", - 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.", - InvalidOrMissingExponent: - "Floating-point numbers require a valid exponent after the 'e'.", - InvalidOrUnexpectedToken: "Unexpected character '%0'.", - InvalidParenthesizedAssignment: "Invalid parenthesized assignment pattern.", - InvalidPrivateFieldResolution: "Private name #%0 is not defined.", - InvalidPropertyBindingPattern: "Binding member expression.", - InvalidRecordProperty: - "Only properties and spread elements are allowed in record definitions.", - 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.", - LineTerminatorBeforeArrow: "No line break is allowed before '=>'.", - MalformedRegExpFlags: "Invalid regular expression flag.", - MissingClassName: "A class name is required.", - MissingEqInAssignment: - "Only '=' operator can be used for specifying default value.", - MissingSemicolon: "Missing semicolon.", - MissingUnicodeEscape: "Expecting Unicode escape sequence \\uXXXX.", - MixingCoalesceWithLogical: - "Nullish coalescing operator(??) requires parens when mixing with logical operators.", - ModuleAttributeDifferentFromType: - "The only accepted module attribute is `type`.", - ModuleAttributeInvalidValue: - "Only string literals are allowed as module attribute values.", - ModuleAttributesWithDuplicateKeys: - 'Duplicate key "%0" is not allowed in module attributes.', - ModuleExportNameHasLoneSurrogate: - "An export name cannot include a lone surrogate, found '\\u%0'.", - 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.", - OverrideOnConstructor: - "'override' modifier cannot appear on a constructor declaration.", - ParamDupe: "Argument name clash.", - PatternHasAccessor: "Object pattern can't contain getter or setter.", - PatternHasMethod: "Object pattern can't contain methods.", - // This error is only used by the smart-mix proposal - PipeBodyIsTighter: - "Unexpected %0 after pipeline body; any %0 expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence.", - PipeTopicRequiresHackPipes: - 'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" or "smart" option.', - PipeTopicUnbound: - "Topic reference is unbound; it must be inside a pipe body.", - PipeTopicUnconfiguredToken: - 'Invalid topic token %0. In order to use %0 as a topic reference, the pipelineOperator plugin must be configured with { "proposal": "hack", "topicToken": "%0" }.', - PipeTopicUnused: - "Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once.", - PipeUnparenthesizedBody: - "Hack-style pipe body cannot be an unparenthesized %0 expression; please wrap it in parentheses.", - - // Messages whose codes start with “Pipeline” or “PrimaryTopic” - // are retained for backwards compatibility - // with the deprecated smart-mix pipe operator proposal plugin. - // They are subject to removal in a future major version. - 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: - 'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" or "smart" option.', - - PrivateInExpectedIn: - "Private names are only allowed in property accesses (`obj.#%0`) or in `in` expressions (`#%0 in obj`).", - PrivateNameRedeclaration: "Duplicate private name #%0.", - RecordExpressionBarIncorrectEndSyntaxType: - "Record expressions ending with '|}' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'.", - RecordExpressionBarIncorrectStartSyntaxType: - "Record expressions starting with '{|' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'.", - RecordExpressionHashIncorrectStartSyntaxType: - "Record expressions starting with '#{' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'hash'.", - RecordNoProto: "'__proto__' is not allowed in Record expressions.", - 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.", - StrictNumericEscape: - "The only valid numeric escape in strict mode is '\\0'.", - StrictOctalLiteral: "Legacy octal literals are not allowed 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.", - TrailingDecorator: "Decorators must be attached to a class element.", - TupleExpressionBarIncorrectEndSyntaxType: - "Tuple expressions ending with '|]' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'.", - TupleExpressionBarIncorrectStartSyntaxType: - "Tuple expressions starting with '[|' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'.", - TupleExpressionHashIncorrectStartSyntaxType: - "Tuple expressions starting with '#[' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'hash'.", - 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 or class properties.", - UnexpectedNumericSeparator: - "A numeric separator is only allowed between two digits.", - UnexpectedPrivateField: "Unexpected private name.", - UnexpectedReservedWord: "Unexpected reserved word '%0'.", - UnexpectedSuper: "'super' is only allowed in object methods and classes.", - UnexpectedToken: "Unexpected token '%0'.", - UnexpectedTokenUnaryExponentiation: - "Illegal expression. Wrap left hand side or entire exponentiation in parentheses.", - UnsupportedBind: "Binding should be performed on object property.", - UnsupportedDecoratorExport: - "A decorated export must export a class declaration.", - 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.", - UnsupportedParameterDecorator: - "Decorators cannot be used to decorate parameters.", - UnsupportedPropertyDecorator: - "Decorators cannot be used to decorate object literal properties.", - 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 expression is not allowed in formal parameters.", - ZeroDigitNumericSeparator: - "Numeric separator can not be used after leading 0.", - }, - /* code */ ErrorCodes.SyntaxError, -); - -export const SourceTypeModuleErrorMessages = makeErrorTemplates( - { - ImportMetaOutsideModule: `import.meta may appear only with 'sourceType: "module"'`, - ImportOutsideModule: `'import' and 'export' may appear only with 'sourceType: "module"'`, - }, - /* code */ ErrorCodes.SourceTypeModuleError, -); diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 30fe64e00e92..8c6dbde4586a 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -3,7 +3,8 @@ /*:: declare var invariant; */ import type { Options } from "../options"; -import { Position, createPositionWithColumnOffset } from "../util/location"; +import { Position, SourceLocation, createPositionWithColumnOffset } from "../util/location"; +import CommentsParser from "../parser/comments"; import * as N from "../types"; import * as charCodes from "charcodes"; import { isIdentifierStart, isIdentifierChar } from "../util/identifier"; @@ -15,8 +16,7 @@ import { type TokenType, } from "./types"; import { type TokContext } from "./context"; -import ParserErrors, { Errors, type ErrorTemplate } from "../parser/error"; -import { SourceLocation } from "../util/location"; +import { Errors, ParseErrorClass, RaiseErrorProperties } from "../parse-error"; import { lineBreakG, isNewLine, @@ -126,7 +126,7 @@ export class Token { // ## Tokenizer -export default class Tokenizer extends ParserErrors { +export default class Tokenizer extends CommentsParser { // Forward-declarations // parser/util.js /*:: @@ -1742,6 +1742,32 @@ export default class Tokenizer extends ParserErrors { } } + raise< + ErrorProperties, + ParseErrorClass: Class>> + ( + SomeParseError: ParseErrorClass, + raiseProperties: RaiseProperties + ) : ParseError { + const { at, ...rest } = raiseProperties; + const loc = at instanceof Position ? at : at.loc; + const error = new SomeParseError({ ...rest, loc: at }); + + /*!SyntaxError.recoverable || */ + if (!this.options.errorRecovery) throw error; + if (!this.isLookahead) this.state.errors.push(error); + + return error; + } + + // Raise an unexpected token error. Can take the expected token type. + unexpected(loc?: Position | null, type?: TokenType): void { + throw this.raise(Errors.UnexpectedToken, { + expected: !!type ? tokenLabelName(type) : null, + at: loc != null ? loc : this.state.startLoc, + }); + } + // updateContext is used by the jsx plugin // eslint-disable-next-line no-unused-vars updateContext(prevType: TokenType): void {} From 9d5bf3a97254ce343073ebffba8f6b3ca3240b5f Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 9 Feb 2022 10:05:34 -0800 Subject: [PATCH 002/104] Begin updating everything. Reviewed by @tolmasky. --- .flowconfig | 1 + packages/babel-parser/src/parse-error.js | 13 +- .../babel-parser/src/parse-error/standard.js | 2 +- .../src/parse-error/strict-mode.js | 4 +- .../babel-parser/src/parser/expression.js | 138 +++-- packages/babel-parser/src/parser/lval.js | 24 +- packages/babel-parser/src/parser/statement.js | 139 +++--- packages/babel-parser/src/parser/util.js | 52 +- .../babel-parser/src/plugins/flow/index.js | 349 +++++++------ .../babel-parser/src/plugins/jsx/index.js | 86 ++-- .../babel-parser/src/plugins/placeholders.js | 25 +- .../src/plugins/typescript/index.js | 470 ++++++++++-------- packages/babel-parser/src/tokenizer/index.js | 114 +++-- packages/babel-parser/src/tokenizer/state.js | 11 +- 14 files changed, 769 insertions(+), 659 deletions(-) diff --git a/.flowconfig b/.flowconfig index 52db881e0d2e..82b0e8729823 100644 --- a/.flowconfig +++ b/.flowconfig @@ -23,6 +23,7 @@ lib/babel-packages.js.flow lib/package-json.js.flow [options] +experimental.enums=true include_warnings=true suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe suppress_comment= \\(.\\|\n\\)*\\$FlowIssue diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index df9f7e2333a0..af8c082b02c7 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -58,7 +58,7 @@ function fromToMessage(toMessage: ToMessage): Class> { }; } -export function toParseErrorClasses( +export function toParseErrorClasses( toClasses: (typeof toReasonCodelessParseErrorClass) => T ): T { // $FlowIgnore @@ -78,6 +78,17 @@ export type RaiseProperties = {| ...Origin, |}; + +export type DeferredErrorDescription>> = [ + T, + T["ErrorProperties"] +]; + +export type DeferredParseErrorMap>> = Map< + number, + DeferredErrorDescription +>; + import StandardErrors from "./parse-error/standard"; import StrictErrors from "./parse-error/strict-mode"; diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index aff5eaf055b5..94a43f70de66 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -98,7 +98,7 @@ export default toParseErrorClasses(_ => ({ ImportCallArgumentTrailingComma: _( "Trailing comma is disallowed inside import(...) arguments." ), - ImportCallArity: _<{ required: 1 | 1 }>( + ImportCallArity: _<{ required: 1 | 2 }>( ({ required }) => `\`import()\` requires exactly ${ required === 1 ? "one argument" : "one or two arguments" diff --git a/packages/babel-parser/src/parse-error/strict-mode.js b/packages/babel-parser/src/parse-error/strict-mode.js index faa616bfa363..50263d47dde6 100644 --- a/packages/babel-parser/src/parse-error/strict-mode.js +++ b/packages/babel-parser/src/parse-error/strict-mode.js @@ -2,8 +2,8 @@ import { toParseErrorClasses } from "../parse-error"; export default toParseErrorClasses(_ => { StrictDelete: _("Deleting local variable in strict mode."), - StrictEvalArguments: _<{ name: string }>(({ name }) => `Assigning to '${name}' in strict mode.`), - StrictEvalArgumentsBinding: _<{ name: string }>(({ name }) => `Binding '${name}' in strict mode.`), + StrictEvalArguments: _<{ binding: string }>(({ binding }) => `Assigning to '${binding}' in strict mode.`), + StrictEvalArgumentsBinding: _<{ binding: string }>(({ binding }) => `Binding '${binding}' in strict mode.`), StrictFunction: _("In strict mode code, functions can only be declared at top level or inside a block."), StrictNumericEscape: _("The only valid numeric escape in strict mode is '\\0'."), StrictOctalLiteral: _("Legacy octal literals are not allowed in strict mode."), diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index bb37f6a85094..ef40f2c8d23b 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -146,7 +146,7 @@ export default class ExpressionParser extends LValParser { if (name === "__proto__") { if (isRecord) { - this.raise(Errors.RecordNoProto, { node: key }); + this.raise(Errors.RecordNoProto, { at: key }); return; } if (protoRef.used) { @@ -157,7 +157,7 @@ export default class ExpressionParser extends LValParser { refExpressionErrors.doubleProtoLoc = key.loc.start; } } else { - this.raise(Errors.DuplicateProto, { node: key }); + this.raise(Errors.DuplicateProto, { at: key }); } } @@ -428,7 +428,7 @@ export default class ExpressionParser extends LValParser { !this.prodParam.hasIn || !this.match(tt._in) ) { - this.raise(Errors.PrivateInExpectedIn, { node: left }, value); + this.raise(Errors.PrivateInExpectedIn, { at: left, name: value }); } this.classScope.usePrivateName(value, left.loc.start); @@ -514,11 +514,10 @@ export default class ExpressionParser extends LValParser { case "smart": return this.withTopicBindingContext(() => { if (this.prodParam.hasYield && this.isContextual(tt._yield)) { - throw this.raise( - Errors.PipeBodyIsTighter, - { at: this.state.startLoc }, - this.state.value, - ); + throw this.raise(Errors.PipeBodyIsTighter, { + at: this.state.startLoc, + expressionDescription: this.state.value, + }); } return this.parseSmartPipelineBodyInStyle( this.parseExprOpBaseRightExpr(op, prec), @@ -560,11 +559,10 @@ export default class ExpressionParser extends LValParser { // TODO: Check how to handle type casts in Flow and TS once they are supported if (invalidHackPipeBodies.has(body.type) && !body.extra?.parenthesized) { - this.raise( - Errors.PipeUnparenthesizedBody, - { at: startLoc }, - invalidHackPipeBodies.get(body.type), - ); + this.raise(Errors.PipeUnparenthesizedBody, { + at: startLoc, + expressionDescription: invalidHackPipeBodies.get(body.type), + }); } if (!this.topicReferenceWasUsedInCurrentContext()) { // A Hack pipe body must use the topic reference at least once. @@ -577,7 +575,7 @@ export default class ExpressionParser extends LValParser { checkExponentialAfterUnary(node: N.AwaitExpression | N.UnaryExpression) { if (this.match(tt.exponent)) { this.raise(Errors.UnexpectedTokenUnaryExponentiation, { - node: node.argument, + at: node.argument, }); } } @@ -618,9 +616,9 @@ export default class ExpressionParser extends LValParser { const arg = node.argument; if (arg.type === "Identifier") { - this.raise(Errors.StrictDelete, { node }); + this.raise(Errors.StrictDelete, { at: node }); } else if (this.hasPropertyAsPrivateName(arg)) { - this.raise(Errors.DeletePrivateField, { node }); + this.raise(Errors.DeletePrivateField, { at: node }); } } @@ -638,7 +636,7 @@ export default class ExpressionParser extends LValParser { ? tokenCanStartExpression(type) : tokenCanStartExpression(type) && !this.match(tt.modulo); if (startsExpr && !this.isAmbiguousAwait()) { - this.raiseOverwrite(startLoc, Errors.AwaitNotInAsyncContext); + this.raiseOverwrite(Errors.AwaitNotInAsyncContext, { at: startLoc }); return this.parseAwait(startPos, startLoc); } } @@ -946,18 +944,18 @@ export default class ExpressionParser extends LValParser { } } if (node.arguments.length === 0 || node.arguments.length > 2) { - this.raise( - Errors.ImportCallArity, - { node }, - this.hasPlugin("importAssertions") || + this.raise(Errors.ImportCallArity, { + at: node, + required: + this.hasPlugin("importAssertions") || this.hasPlugin("moduleAttributes") - ? "one or two arguments" - : "one argument", - ); + ? 2 + : 1, + }); } else { for (const arg of node.arguments) { if (arg.type === "SpreadElement") { - this.raise(Errors.ImportCallSpreadArgument, { node: arg }); + this.raise(Errors.ImportCallSpreadArgument, { at: arg }); } } } @@ -1182,7 +1180,7 @@ export default class ExpressionParser extends LValParser { if (callee.type === "MemberExpression") { return this.finishNode(node, "BindExpression"); } else { - throw this.raise(Errors.UnsupportedBind, { node: callee }); + throw this.raise(Errors.UnsupportedBind, { at: callee }); } } @@ -1193,11 +1191,10 @@ export default class ExpressionParser extends LValParser { // We can throw a better error message and recover, rather than // just throwing "Unexpected token" (which is the default // behavior of this big switch statement). - this.raise( - Errors.PrivateInExpectedIn, - { at: this.state.startLoc }, - this.state.value, - ); + this.raise(Errors.PrivateInExpectedIn, { + at: this.state.startLoc, + name: this.state.value, + }); return this.parsePrivateName(); } @@ -1422,11 +1419,10 @@ export default class ExpressionParser extends LValParser { return this.finishNode(node, nodeType); } else { // The token does not match the plugin’s configuration. - throw this.raise( - Errors.PipeTopicUnconfiguredToken, - { at: startLoc }, - tokenLabelName(tokenType), - ); + throw this.raise(Errors.PipeTopicUnconfiguredToken, { + at: startLoc, + token: tokenLabelName(tokenType), + }); } } @@ -1512,12 +1508,12 @@ export default class ExpressionParser extends LValParser { !this.scope.allowDirectSuper && !this.options.allowSuperOutsideMethod ) { - this.raise(Errors.SuperNotAllowed, { node }); + this.raise(Errors.SuperNotAllowed, { at: node }); } else if ( !this.scope.allowSuper && !this.options.allowSuperOutsideMethod ) { - this.raise(Errors.UnexpectedSuper, { node }); + this.raise(Errors.UnexpectedSuper, { at: node }); } if ( @@ -1525,7 +1521,7 @@ export default class ExpressionParser extends LValParser { !this.match(tt.bracketL) && !this.match(tt.dot) ) { - this.raise(Errors.UnsupportedSuper, { node }); + this.raise(Errors.UnsupportedSuper, { at: node }); } return this.finishNode(node, "Super"); @@ -1589,12 +1585,11 @@ export default class ExpressionParser extends LValParser { node.property = this.parseIdentifier(true); if (node.property.name !== propertyName || containsEsc) { - this.raise( - Errors.UnsupportedMetaProperty, - { node: node.property }, - meta.name, - propertyName, - ); + this.raise(Errors.UnsupportedMetaProperty, { + at: node.property, + target: meta.name, + property: propertyName, + }); } return this.finishNode(node, "MetaProperty"); @@ -1608,7 +1603,7 @@ export default class ExpressionParser extends LValParser { if (this.isContextual(tt._meta)) { if (!this.inModule) { this.raise(SourceTypeModuleErrors.ImportMetaOutsideModule, { - node: id, + at: id, }); } this.sawUnambiguousESM = true; @@ -1822,7 +1817,7 @@ export default class ExpressionParser extends LValParser { const metaProp = this.parseMetaProperty(node, meta, "target"); if (!this.scope.inNonArrowFunction && !this.scope.inClass) { - this.raise(Errors.UnexpectedNewTarget, { node: metaProp }); + this.raise(Errors.UnexpectedNewTarget, { at: metaProp }); } return metaProp; @@ -1840,7 +1835,7 @@ export default class ExpressionParser extends LValParser { parseNew(node: N.Expression): N.NewExpression { node.callee = this.parseNoCallExpr(); if (node.callee.type === "Import") { - this.raise(Errors.ImportCallNotNewExpression, { node: node.callee }); + this.raise(Errors.ImportCallNotNewExpression, { at: node.callee }); } else if (this.isOptionalChain(node.callee)) { this.raise(Errors.OptionalChainingNoNew, { at: this.state.lastTokEndLoc, @@ -1964,7 +1959,7 @@ export default class ExpressionParser extends LValParser { !this.isObjectProperty(prop) && prop.type !== "SpreadElement" ) { - this.raise(Errors.InvalidRecordProperty, { node: prop }); + this.raise(Errors.InvalidRecordProperty, { at: prop }); } // $FlowIgnore @@ -2070,11 +2065,10 @@ export default class ExpressionParser extends LValParser { prop.kind = keyName; if (this.match(tt.star)) { isGenerator = true; - this.raise( - Errors.AccessorIsGenerator, - { at: this.state.curPosition() }, - keyName, - ); + this.raise(Errors.AccessorIsGenerator, { + at: this.state.curPosition(), + accessor: keyName, + }); this.next(); } this.parsePropertyName(prop); @@ -2115,7 +2109,7 @@ export default class ExpressionParser extends LValParser { if (params.length !== paramCount) { this.raise( method.kind === "get" ? Errors.BadGetterArity : Errors.BadSetterArity, - { node: method }, + { at: method }, ); } @@ -2123,7 +2117,7 @@ export default class ExpressionParser extends LValParser { method.kind === "set" && params[params.length - 1]?.type === "RestElement" ) { - this.raise(Errors.BadSetterRestParameter, { node: method }); + this.raise(Errors.BadSetterRestParameter, { at: method }); } } @@ -2467,15 +2461,14 @@ export default class ExpressionParser extends LValParser { if (hasStrictModeDirective && nonSimple) { // This logic is here to align the error location with the ESTree plugin. - const errorOrigin = + this.raise(Errors.IllegalLanguageModeDirective, { // $FlowIgnore - (node.kind === "method" || node.kind === "constructor") && + at: (node.kind === "method" || node.kind === "constructor") && // $FlowIgnore !!node.key - ? { at: node.key.loc.end } - : { node }; - - this.raise(Errors.IllegalLanguageModeDirective, errorOrigin); + ? node.key.loc.end + : node + }); } const strictModeChanged = !oldStrict && this.state.strict; @@ -2579,11 +2572,10 @@ export default class ExpressionParser extends LValParser { let elt; if (this.match(tt.comma)) { if (!allowEmpty) { - this.raise( - Errors.UnexpectedToken, - { at: this.state.curPosition() }, - ",", - ); + this.raise(Errors.UnexpectedToken, { + at: this.state.curPosition(), + found: "," + }); } elt = null; } else if (this.match(tt.ellipsis)) { @@ -2708,7 +2700,10 @@ export default class ExpressionParser extends LValParser { } if (checkKeywords && isKeyword(word)) { - this.raise(Errors.UnexpectedKeyword, { at: startLoc }, word); + this.raise(Errors.UnexpectedKeyword, { + at: startLoc, + keyword: word + }); return; } @@ -2719,7 +2714,10 @@ export default class ExpressionParser extends LValParser { : isStrictReservedWord; if (reservedTest(word, this.inModule)) { - this.raise(Errors.UnexpectedReservedWord, { at: startLoc }, word); + this.raise(Errors.UnexpectedReservedWord, { + at: startLoc, + reservedWord: word + }); } } @@ -2742,7 +2740,7 @@ export default class ExpressionParser extends LValParser { ); if (this.eat(tt.star)) { - this.raise(Errors.ObsoleteAwaitStar, { node }); + this.raise(Errors.ObsoleteAwaitStar, { at: node }); } if (!this.scope.inFunction && !this.options.allowAwaitOutsideFunction) { diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index cc867177009d..e01570c851f1 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -107,10 +107,10 @@ export default class LValParser extends NodeUtils { // A parenthesized member expression can be in LHS but not in pattern. // If the LHS is later interpreted as a pattern, `checkLVal` will throw for member expression binding // i.e. `([(a.b) = []] = []) => {}` - this.raise(Errors.InvalidParenthesizedAssignment, { node }); + this.raise(Errors.InvalidParenthesizedAssignment, { at: node }); } } else { - this.raise(Errors.InvalidParenthesizedAssignment, { node }); + this.raise(Errors.InvalidParenthesizedAssignment, { at: node }); } } @@ -208,11 +208,11 @@ export default class LValParser extends NodeUtils { prop.kind === "get" || prop.kind === "set" ? Errors.PatternHasAccessor : Errors.PatternHasMethod, - { node: prop.key }, + { at: prop.key }, ); /* eslint-enable @babel/development-internal/dry-error-messages */ } else if (prop.type === "SpreadElement" && !isLast) { - this.raise(Errors.RestTrailingComma, { node: prop }); + this.raise(Errors.RestTrailingComma, { at: prop }); } else { this.toAssignable(prop, isLHS); } @@ -256,7 +256,7 @@ export default class LValParser extends NodeUtils { if (elt) { this.toAssignable(elt, isLHS); if (elt.type === "RestElement") { - this.raise(Errors.RestTrailingComma, { node: elt }); + this.raise(Errors.RestTrailingComma, { at: elt }); } } } @@ -535,20 +535,19 @@ export default class LValParser extends NodeUtils { bindingType === BIND_NONE ? Errors.StrictEvalArguments : Errors.StrictEvalArgumentsBinding, - { node: expr }, - name, + { at: expr, binding: name }, ); } if (checkClashes) { if (checkClashes.has(name)) { - this.raise(Errors.ParamDupe, { node: expr }); + this.raise(Errors.ParamDupe, { at: expr }); } else { checkClashes.add(name); } } if (disallowLetBinding && name === "let") { - this.raise(Errors.LetInLexicalBinding, { node: expr }); + this.raise(Errors.LetInLexicalBinding, { at: expr }); } if (!(bindingType & BIND_NONE)) { this.scope.declareName(name, bindingType, expr.loc.start); @@ -559,7 +558,7 @@ export default class LValParser extends NodeUtils { case "MemberExpression": if (bindingType !== BIND_NONE) { this.raise(Errors.InvalidPropertyBindingPattern, { - node: expr, + at: expr, }); } break; @@ -628,8 +627,7 @@ export default class LValParser extends NodeUtils { bindingType === BIND_NONE ? Errors.InvalidLhs : Errors.InvalidLhsBinding, - { node: expr }, - contextDescription, + { at: expr, contextDescription }, ); } } @@ -641,7 +639,7 @@ export default class LValParser extends NodeUtils { node.argument.type !== "MemberExpression" ) { this.raise(Errors.InvalidRestAssignmentPattern, { - node: node.argument, + at: node.argument, }); } } diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 0f7cbec41aad..6d10d6b5cdce 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -218,7 +218,10 @@ export default class StatementParser extends ExpressionParser { this.scope.undefinedExports.size > 0 ) { for (const [name, loc] of Array.from(this.scope.undefinedExports)) { - this.raise(Errors.ModuleExportUndefined, { at: loc }, name); + this.raise(Errors.ModuleExportUndefined, { + at: loc, + exportBinding: name + }); } } return this.finishNode(program, "Program"); @@ -480,7 +483,7 @@ export default class StatementParser extends ExpressionParser { assertModuleNodeAllowed(node: N.Node): void { if (!this.options.allowImportExportEverywhere && !this.inModule) { - this.raise(SourceTypeModuleErrors.ImportOutsideModule, { node }); + this.raise(SourceTypeModuleErrors.ImportOutsideModule, { at: node }); } } @@ -608,11 +611,10 @@ export default class StatementParser extends ExpressionParser { } } if (i === this.state.labels.length) { - this.raise( - Errors.IllegalBreakContinue, - { node }, - isBreak ? "break" : "continue", - ); + this.raise(Errors.IllegalBreakContinue, { + at: node, + construct: isBreak ? "break" : "continue", + }); } } @@ -709,7 +711,7 @@ export default class StatementParser extends ExpressionParser { if (isForOf) { // Check for leading tokens that are forbidden in for-of loops: if (startsWithLet) { - this.raise(Errors.ForOfLet, { node: init }); + this.raise(Errors.ForOfLet, { at: init }); } if ( @@ -722,7 +724,7 @@ export default class StatementParser extends ExpressionParser { // parsed as an identifier. If it was parsed as the start of an async // arrow function (e.g. `for (async of => {} of []);`), the LVal check // further down will raise a more appropriate error. - this.raise(Errors.ForOfAsync, { node: init }); + this.raise(Errors.ForOfAsync, { at: init }); } } if (isForOf || this.match(tt._in)) { @@ -883,7 +885,7 @@ export default class StatementParser extends ExpressionParser { node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null; if (!node.handler && !node.finalizer) { - this.raise(Errors.NoCatchOrFinally, { node }); + this.raise(Errors.NoCatchOrFinally, { at: node }); } return this.finishNode(node, "TryStatement"); @@ -953,7 +955,10 @@ export default class StatementParser extends ExpressionParser { ): N.LabeledStatement { for (const label of this.state.labels) { if (label.name === maybeName) { - this.raise(Errors.LabelRedeclaration, { node: expr }, maybeName); + this.raise(Errors.LabelRedeclaration, { + at: expr, + label: maybeName + }); } } @@ -1161,15 +1166,17 @@ export default class StatementParser extends ExpressionParser { init.kind !== "var" || init.declarations[0].id.type !== "Identifier") ) { - this.raise( - Errors.ForInOfLoopInitializer, - { node: init }, - isForIn ? "for-in" : "for-of", - ); + this.raise(Errors.ForInOfLoopInitializer, { + at: init, + construct: isForIn ? "for-in" : "for-of", + }); } if (init.type === "AssignmentPattern") { - this.raise(Errors.InvalidLhs, { node: init }, "for-loop"); + this.raise(Errors.InvalidLhs, { + at: init, + construct: "for-loop" + }); } node.left = init; @@ -1219,21 +1226,19 @@ export default class StatementParser extends ExpressionParser { // `const` with no initializer is allowed in TypeScript. // It could be a declaration like `const x: number;`. if (!isTypescript) { - this.raise( - Errors.DeclarationMissingInitializer, - { at: this.state.lastTokEndLoc }, - "Const declarations", - ); + this.raise(Errors.DeclarationMissingInitializer, { + at: this.state.lastTokEndLoc, + contextDescription: "Const declarations" + }); } } else if ( decl.id.type !== "Identifier" && !(isFor && (this.match(tt._in) || this.isContextual(tt._of))) ) { - this.raise( - Errors.DeclarationMissingInitializer, - { at: this.state.lastTokEndLoc }, - "Complex binding patterns", - ); + this.raise(Errors.DeclarationMissingInitializer, { + at: this.state.lastTokEndLoc, + contextDescription: "Complex binding patterns", + }); } decl.init = null; } @@ -1444,7 +1449,7 @@ export default class StatementParser extends ExpressionParser { member.decorators && member.decorators.length > 0 ) { - this.raise(Errors.DecoratorConstructor, { node: member }); + this.raise(Errors.DecoratorConstructor, { at: member }); } } }); @@ -1554,7 +1559,7 @@ export default class StatementParser extends ExpressionParser { if (this.isNonstaticConstructor(publicMethod)) { this.raise(Errors.ConstructorIsGenerator, { - node: publicMethod.key, + at: publicMethod.key, }); } @@ -1594,10 +1599,10 @@ export default class StatementParser extends ExpressionParser { // TypeScript allows multiple overloaded constructor declarations. if (state.hadConstructor && !this.hasPlugin("typescript")) { - this.raise(Errors.DuplicateConstructor, { node: key }); + this.raise(Errors.DuplicateConstructor, { at: key }); } if (isConstructor && this.hasPlugin("typescript") && member.override) { - this.raise(Errors.OverrideOnConstructor, { node: key }); + this.raise(Errors.OverrideOnConstructor, { at: key }); } state.hadConstructor = true; allowsDirectSuper = state.hadSuperClass; @@ -1646,7 +1651,7 @@ export default class StatementParser extends ExpressionParser { ); } else { if (this.isNonstaticConstructor(publicMethod)) { - this.raise(Errors.ConstructorIsAsync, { node: publicMethod.key }); + this.raise(Errors.ConstructorIsAsync, { at: publicMethod.key }); } this.pushClassMethod( @@ -1676,7 +1681,7 @@ export default class StatementParser extends ExpressionParser { this.pushClassPrivateMethod(classBody, privateMethod, false, false); } else { if (this.isNonstaticConstructor(publicMethod)) { - this.raise(Errors.ConstructorIsAccessor, { node: publicMethod.key }); + this.raise(Errors.ConstructorIsAccessor, { at: publicMethod.key }); } this.pushClassMethod( classBody, @@ -1757,7 +1762,7 @@ export default class StatementParser extends ExpressionParser { this.state.labels = oldLabels; classBody.body.push(this.finishNode(member, "StaticBlock")); if (member.decorators?.length) { - this.raise(Errors.DecoratorStaticBlock, { node: member }); + this.raise(Errors.DecoratorStaticBlock, { at: member }); } } @@ -1768,7 +1773,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(Errors.ConstructorClassField, { node: prop.key }); + this.raise(Errors.ConstructorClassField, { at: prop.key }); } classBody.body.push(this.parseClassProperty(prop)); @@ -1800,7 +1805,7 @@ export default class StatementParser extends ExpressionParser { if (key.name === "constructor" || key.value === "constructor") { // Non-computed field, which is either an identifier named "constructor" // or a string literal named "constructor" - this.raise(Errors.ConstructorClassField, { node: key }); + this.raise(Errors.ConstructorClassField, { at: key }); } } @@ -2233,7 +2238,7 @@ export default class StatementParser extends ExpressionParser { !declaration.extra?.parenthesized ) { this.raise(Errors.ExportDefaultFromAsIdentifier, { - node: declaration, + at: declaration, }); } } @@ -2248,12 +2253,11 @@ export default class StatementParser extends ExpressionParser { if (!isFrom && specifier.local) { const { local } = specifier; if (local.type !== "Identifier") { - this.raise( - Errors.ExportBindingIsString, - { node: specifier }, - local.value, - exportedName, - ); + this.raise(Errors.ExportBindingIsString, { + at: specifier, + localBinding: local.value, + exportedBinding: exportedName, + }); } else { // check for keywords used as local names this.checkReservedWord(local.name, local.loc.start, true, false); @@ -2285,7 +2289,7 @@ export default class StatementParser extends ExpressionParser { // If node.declaration is a class, it will take all decorators in the current context. // Thus we should throw if we see non-empty decorators here. if (currentContextDecorators.length) { - throw this.raise(Errors.UnsupportedDecoratorExport, { node }); + throw this.raise(Errors.UnsupportedDecoratorExport, { at: node }); } } @@ -2325,8 +2329,7 @@ export default class StatementParser extends ExpressionParser { name === "default" ? Errors.DuplicateDefaultExport : Errors.DuplicateExport, - { node }, - name, + { at: node, name } ); } this.exportedIdentifiers.add(name); @@ -2389,11 +2392,10 @@ export default class StatementParser extends ExpressionParser { const result = this.parseStringLiteral(this.state.value); const surrogate = result.value.match(loneSurrogate); if (surrogate) { - this.raise( - Errors.ModuleExportNameHasLoneSurrogate, - { node: result }, - surrogate[0].charCodeAt(0).toString(16), - ); + this.raise(Errors.ModuleExportNameHasLoneSurrogate, { + at: result, + surrogateCharCode: surrogate[0].charCodeAt(0), + }); } return result; } @@ -2488,11 +2490,10 @@ export default class StatementParser extends ExpressionParser { // if a duplicate entry is found, throw an error // for now this logic will come into play only when someone declares `type` twice if (attrNames.has(keyName)) { - this.raise( - Errors.ModuleAttributesWithDuplicateKeys, - { at: this.state.startLoc }, - keyName, - ); + this.raise(Errors.ModuleAttributesWithDuplicateKeys, { + at: this.state.startLoc, + key: keyName, + }); } attrNames.add(keyName); if (this.match(tt.string)) { @@ -2537,18 +2538,17 @@ export default class StatementParser extends ExpressionParser { if (node.key.name !== "type") { this.raise( - Errors.ModuleAttributeDifferentFromType, - { node: node.key }, - node.key.name, - ); + Errors.ModuleAttributeDifferentFromType, { + at: node.key, + key: node.key.name, + }); } if (attributes.has(node.key.name)) { - this.raise( - Errors.ModuleAttributesWithDuplicateKeys, - { node: node.key }, - node.key.name, - ); + this.raise(Errors.ModuleAttributesWithDuplicateKeys, { + at: node.key, + key: node.key.name, + }); } attributes.add(node.key.name); this.expect(tt.colon); @@ -2659,11 +2659,10 @@ export default class StatementParser extends ExpressionParser { } else { const { imported } = specifier; if (importedIsString) { - throw this.raise( - Errors.ImportBindingIsString, - { node: specifier }, - imported.value, - ); + throw this.raise(Errors.ImportBindingIsString, { + at: specifier, + importedBinding: imported.value, + }); } this.checkReservedWord(imported.name, specifier.loc.start, true, true); if (!specifier.local) { diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index f9bad51b28ee..472a2da2fb79 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -19,8 +19,7 @@ import ProductionParameterHandler, { PARAM_AWAIT, PARAM, } from "../util/production-parameter"; -import { Errors, type ErrorTemplate, ErrorCodes } from "./error"; -import type { ParsingError } from "./error"; +import { Errors, ParseError } from "../parse-error"; import type { PluginConfig } from "./base"; /*:: import type ScopeHandler from "../util/scope"; @@ -98,11 +97,13 @@ export default class UtilParser extends Tokenizer { // Asserts that following token is given contextual keyword. - expectContextual(token: TokenType, template?: ErrorTemplate): void { + expectContextual( + token: TokenType, + ParseErrorClass?: Class> + ): void { if (!this.eatContextual(token)) { - if (template != null) { - /* eslint-disable @babel/development-internal/dry-error-messages */ - throw this.raise(template, { at: this.state.startLoc }); + if (ParseErrorClass != null) { + throw this.raise(ParseErrorClass, { at: this.state.startLoc }); } throw this.unexpected(null, token); } @@ -150,41 +151,6 @@ export default class UtilParser extends Tokenizer { this.eat(type) || this.unexpected(loc, type); } - // Throws if the current token and the prev one are separated by a space. - assertNoSpace(message: string = "Unexpected space."): void { - if (this.state.start > this.state.lastTokEndLoc.index) { - /* eslint-disable @babel/development-internal/dry-error-messages */ - this.raise( - { - code: ErrorCodes.SyntaxError, - reasonCode: "UnexpectedSpace", - template: message, - }, - { at: this.state.lastTokEndLoc }, - /* eslint-enable @babel/development-internal/dry-error-messages */ - ); - } - } - - // Raise an unexpected token error. Can take the expected token type - // instead of a message string. - - unexpected(loc?: ?Position, type?: ?TokenType): empty { - /* eslint-disable @babel/development-internal/dry-error-messages */ - throw this.raise( - { - code: ErrorCodes.SyntaxError, - reasonCode: "UnexpectedToken", - template: - type != null - ? `Unexpected token, expected "${tokenLabelName(type)}"` - : "Unexpected token", - }, - { at: loc != null ? loc : this.state.startLoc }, - ); - /* eslint-enable @babel/development-internal/dry-error-messages */ - } - getPluginNamesFromConfigs(pluginConfigs: Array): Array { return pluginConfigs.map(c => { if (typeof c === "string") { @@ -228,7 +194,7 @@ export default class UtilParser extends Tokenizer { oldState: State = this.state.clone(), ): | TryParse - | TryParse + | TryParse | TryParse { const abortSignal: { node: T | null } = { node: null }; try { @@ -245,7 +211,7 @@ export default class UtilParser extends Tokenizer { this.state.tokensLength = failState.tokensLength; return { node, - error: (failState.errors[oldState.errors.length]: ParsingError), + error: (failState.errors[oldState.errors.length]: SyntaxError), thrown: false, aborted: false, failState, diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index da51b8135e93..9dd6c4bbba60 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -33,7 +33,7 @@ import { SCOPE_OTHER, } from "../../util/scopeflags"; import type { ExpressionErrors } from "../../parser/util"; -import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error"; +import { Errors, toParseErrorClasses } from "../../parse-error"; import { cloneIdentifier } from "../../parser/node"; const reservedTypes = new Set([ @@ -57,29 +57,49 @@ 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 = makeErrorTemplates( - { - 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.", - DeclareClassElement: - "The `declare` modifier can only appear on class fields.", - DeclareClassFieldInitializer: - "Initializers are not allowed in fields with the `declare` modifier.", - 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`.", +const FlowErrors = toParseErrorClasses( + _ => ({ + 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: _<{ reservedType: string }>( + ({ reservedType }) => `Cannot overwrite reserved type ${reservedType}.` + ), + DeclareClassElement: _( + "The `declare` modifier can only appear on class fields." + ), + DeclareClassFieldInitializer: _( + "Initializers are not allowed in fields with the `declare` modifier." + ), + DuplicateDeclareModuleExports: _( + "Duplicate `declare module.exports` statement." + ), + EnumBooleanMemberNotInitialized: _<{ + memberName: string, + enumName: string, + }>( + ({ memberName, enumName }) => + `Boolean enum members need to be initialized. Use either \`${memberName} = true,\` or \`${memberName} = false,\` in enum \`${enumName}\`.` + ), + EnumDuplicateMemberName: _<{ memberName: string, enumName: string }>( + ({ memberName, enumName }) => + `Enum member names need to be unique, but the name \`${memberName}\` has already been used before in enum \`${enumName}\`.` + ), + EnumInconsistentMemberValues: _<{ enumName: string }>( + ({ enumName }) => + `Enum \`${enumName}\` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers.` + ), + EnumInvalidExplicitType: _<{ invalidEnumType: string, enumName: string }>( + ({ invalidEnumType, enumName }) => + `Enum type \`${invalidEnumType}\` is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.` + ), + EnumInvalidExplicitTypeUnknownSupplied: _<{ enumName: string }>( + ({ enumName }) => + `Supplied enum type is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.` + ), EnumInvalidMemberInitializerPrimaryType: "Enum `%0` has type `%2`, so the initializer of `%1` needs to be a %2 literal.", EnumInvalidMemberInitializerSymbolType: @@ -92,61 +112,88 @@ const FlowErrors = makeErrorTemplates( "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`.", - GetterMayNotHaveThisParam: "A getter cannot have a `this` parameter.", - 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.", - PatternIsOptional: - "A binding pattern parameter cannot be optional in an implementation signature.", - SetterMayNotHaveThisParam: "A setter cannot have a `this` parameter.", - SpreadVariance: "Spread properties cannot have variance.", - ThisParamAnnotationRequired: - "A type annotation is required for the `this` parameter.", - ThisParamBannedInConstructor: - "Constructors cannot have a `this` parameter; constructors don't bind `this` like other functions.", - ThisParamMayNotBeOptional: "The `this` parameter cannot be optional.", - ThisParamMustBeFirst: - "The `this` parameter must be the first function parameter.", - ThisParamNoDefault: "The `this` parameter may not have a default value.", - 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.", - 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.", - UnexpectedTypeParameterBeforeAsyncArrowFunction: - "Type parameters must come after the async keyword, e.g. instead of ` async () => {}`, use `async () => {}`.", - 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.", - }, - /* code */ ErrorCodes.SyntaxError, - /* syntaxPlugin */ "flow", + GetterMayNotHaveThisParam: _("A getter cannot have a `this` parameter."), + 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." + ), + PatternIsOptional: _( + "A binding pattern parameter cannot be optional in an implementation signature." + ), + SetterMayNotHaveThisParam: _("A setter cannot have a `this` parameter."), + SpreadVariance: _("Spread properties cannot have variance."), + ThisParamAnnotationRequired: _( + "A type annotation is required for the `this` parameter." + ), + ThisParamBannedInConstructor: _( + "Constructors cannot have a `this` parameter; constructors don't bind `this` like other functions." + ), + ThisParamMayNotBeOptional: _("The `this` parameter cannot be optional."), + ThisParamMustBeFirst: _( + "The `this` parameter must be the first function parameter." + ), + ThisParamNoDefault: _("The `this` parameter may not have a default value."), + 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: _<{ reservedType: string }>( + ({ reservedType }) => `Unexpected reserved type ${reservedType}.` + ), + UnexpectedReservedUnderscore: _( + "`_` is only allowed as a type argument to call or new." + ), + 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." + ), + UnexpectedTypeParameterBeforeAsyncArrowFunction: _( + "Type parameters must come after the async keyword, e.g. instead of ` async () => {}`, use `async () => {}`." + ), + UnsupportedDeclareExportKind: _<{ + unsupported: string, + suggestion: string, + }>( + ({ unsupported, suggestion }) => + `\`declare export ${unsupported}\` is not supported. Use \`${suggestion}\` instead.` + ), + UnsupportedStatementInDeclareModule: _( + "Only declares and type imports are allowed inside declare module." + ), + UnterminatedFlowComment: _("Unterminated flow-comment."), + }), + /* syntaxPlugin */ "flow" ); /* eslint-disable sort-keys */ @@ -450,19 +497,19 @@ export default (superClass: Class): Class => if (isEsModuleType(bodyElement)) { if (kind === "CommonJS") { this.raise(FlowErrors.AmbiguousDeclareModuleKind, { - node: bodyElement, + at: bodyElement, }); } kind = "ES"; } else if (bodyElement.type === "DeclareModuleExports") { if (hasModuleExport) { this.raise(FlowErrors.DuplicateDeclareModuleExports, { - node: bodyElement, + at: bodyElement, }); } if (kind === "ES") { this.raise(FlowErrors.AmbiguousDeclareModuleKind, { - node: bodyElement, + at: bodyElement, }); } kind = "CommonJS"; @@ -501,14 +548,11 @@ export default (superClass: Class): Class => !insideModule) ) { const label = this.state.value; - const suggestion = exportSuggestions[label]; - - throw this.raise( - FlowErrors.UnsupportedDeclareExportKind, - { at: this.state.startLoc }, - label, - suggestion, - ); + throw this.raise(FlowErrors.UnsupportedDeclareExportKind, { + at: this.state.startLoc, + unsupported: label, + suggestion: exportSuggestions[label], + }); } if ( @@ -675,10 +719,10 @@ export default (superClass: Class): Class => this.raise( declaration ? FlowErrors.AssignReservedType - : FlowErrors.UnexpectedReservedType, - { at: startLoc }, - word, - ); + : FlowErrors.UnexpectedReservedType, { + at: startLoc, + reservedType: word, + }); } flowParseRestrictedIdentifier( @@ -1154,7 +1198,7 @@ export default (superClass: Class): Class => }); } if (variance) { - this.raise(FlowErrors.InexactVariance, { node: variance }); + this.raise(FlowErrors.InexactVariance, { at: variance }); } return null; @@ -1169,7 +1213,7 @@ export default (superClass: Class): Class => this.unexpected(protoStartLoc); } if (variance) { - this.raise(FlowErrors.SpreadVariance, { node: variance }); + this.raise(FlowErrors.SpreadVariance, { at: variance }); } node.argument = this.flowParseType(); @@ -1205,7 +1249,7 @@ export default (superClass: Class): Class => node.value.this ) { this.raise(FlowErrors.ThisParamBannedInConstructor, { - node: node.value.this, + at: node.value.this, }); } } else { @@ -1240,7 +1284,7 @@ export default (superClass: Class): Class => property.kind === "get" ? FlowErrors.GetterMayNotHaveThisParam : FlowErrors.SetterMayNotHaveThisParam, - { node: property.value.this }, + { at: property.value.this }, ); } @@ -1249,12 +1293,12 @@ export default (superClass: Class): Class => property.kind === "get" ? Errors.BadGetterArity : Errors.BadSetterArity, - { node: property }, + { at: property }, ); } if (property.kind === "set" && property.value.rest) { - this.raise(Errors.BadSetterRestParameter, { node: property }); + this.raise(Errors.BadSetterRestParameter, { at: property }); } } @@ -1336,13 +1380,13 @@ export default (superClass: Class): Class => if (lh.type === tt.colon || lh.type === tt.question) { if (isThis && !first) { - this.raise(FlowErrors.ThisParamMustBeFirst, { node }); + this.raise(FlowErrors.ThisParamMustBeFirst, { at: node }); } name = this.parseIdentifier(isThis); if (this.eat(tt.question)) { optional = true; if (isThis) { - this.raise(FlowErrors.ThisParamMayNotBeOptional, { node }); + this.raise(FlowErrors.ThisParamMayNotBeOptional, { at: node }); } } typeAnnotation = this.flowParseTypeInitialiser(); @@ -2214,7 +2258,7 @@ export default (superClass: Class): Class => this.raise(FlowErrors.DeclareClassElement, { at: startLoc }); } else if (member.value) { this.raise(FlowErrors.DeclareClassFieldInitializer, { - node: member.value, + at: member.value, }); } } @@ -2230,11 +2274,10 @@ export default (superClass: Class): Class => // Allow @@iterator and @@asyncIterator as a identifier only inside type if (!this.isIterator(word) || !this.state.inType) { - this.raise( - Errors.InvalidIdentifier, - { at: this.state.curPosition() }, - fullWord, - ); + this.raise(Errors.InvalidIdentifier, { + at: this.state.curPosition(), + identifier: fullWord, + }); } this.finishToken(tt.name, fullWord); @@ -2312,7 +2355,7 @@ export default (superClass: Class): Class => (exprList.length > 1 || !isParenthesizedExpr) ) { this.raise(FlowErrors.TypeCastInPattern, { - node: expr.typeAnnotation, + at: expr.typeAnnotation, }); } } @@ -2422,7 +2465,7 @@ export default (superClass: Class): Class => if (method.params && isConstructor) { const params = method.params; if (params.length > 0 && this.isThisParam(params[0])) { - this.raise(FlowErrors.ThisParamBannedInConstructor, { node: method }); + this.raise(FlowErrors.ThisParamBannedInConstructor, { at: method }); } // estree support } else if ( @@ -2433,7 +2476,7 @@ export default (superClass: Class): Class => ) { const params = method.value.params; if (params.length > 0 && this.isThisParam(params[0])) { - this.raise(FlowErrors.ThisParamBannedInConstructor, { node: method }); + this.raise(FlowErrors.ThisParamBannedInConstructor, { at: method }); } } } @@ -2483,9 +2526,9 @@ export default (superClass: Class): Class => if (params.length > 0) { const param = params[0]; if (this.isThisParam(param) && method.kind === "get") { - this.raise(FlowErrors.GetterMayNotHaveThisParam, { node: param }); + this.raise(FlowErrors.GetterMayNotHaveThisParam, { at: param }); } else if (this.isThisParam(param)) { - this.raise(FlowErrors.SetterMayNotHaveThisParam, { node: param }); + this.raise(FlowErrors.SetterMayNotHaveThisParam, { at: param }); } } } @@ -2540,10 +2583,10 @@ export default (superClass: Class): Class => parseAssignableListItemTypes(param: N.Pattern): N.Pattern { if (this.eat(tt.question)) { if (param.type !== "Identifier") { - this.raise(FlowErrors.PatternIsOptional, { node: param }); + this.raise(FlowErrors.PatternIsOptional, { at: param }); } if (this.isThisParam(param)) { - this.raise(FlowErrors.ThisParamMayNotBeOptional, { node: param }); + this.raise(FlowErrors.ThisParamMayNotBeOptional, { at: param }); } ((param: any): N.Identifier).optional = true; @@ -2551,11 +2594,11 @@ export default (superClass: Class): Class => if (this.match(tt.colon)) { param.typeAnnotation = this.flowParseTypeAnnotation(); } else if (this.isThisParam(param)) { - this.raise(FlowErrors.ThisParamAnnotationRequired, { node: param }); + this.raise(FlowErrors.ThisParamAnnotationRequired, { at: param }); } if (this.match(tt.eq) && this.isThisParam(param)) { - this.raise(FlowErrors.ThisParamNoDefault, { node: param }); + this.raise(FlowErrors.ThisParamNoDefault, { at: param }); } this.resetEndLocation(param); @@ -2575,7 +2618,7 @@ export default (superClass: Class): Class => node.right.start < node.typeAnnotation.start ) { this.raise(FlowErrors.TypeBeforeInitializer, { - node: node.typeAnnotation, + at: node.typeAnnotation, }); } @@ -2687,11 +2730,10 @@ export default (superClass: Class): Class => } else { if (importedIsString) { /*:: invariant(firstIdent instanceof N.StringLiteral) */ - throw this.raise( - Errors.ImportBindingIsString, - { node: specifier }, - firstIdent.value, - ); + throw this.raise(Errors.ImportBindingIsString, { + at: specifier, + importBinding: firstIdent.value, + }); } /*:: invariant(firstIdent instanceof N.Node) */ specifier.imported = firstIdent; @@ -2710,7 +2752,7 @@ export default (superClass: Class): Class => if (isInTypeOnlyImport && specifierIsTypeImport) { this.raise(FlowErrors.ImportTypeShorthandOnlyInPureImport, { - node: specifier, + at: specifier, }); } @@ -2885,7 +2927,7 @@ export default (superClass: Class): Class => /*:: invariant(typeParameters) */ this.raise( FlowErrors.UnexpectedTypeParameterBeforeAsyncArrowFunction, - { node: typeParameters }, + { at: typeParameters }, ); } @@ -2918,7 +2960,7 @@ export default (superClass: Class): Class => /*:: invariant(typeParameters) */ throw this.raise(FlowErrors.UnexpectedTokenAfterTypeParameter, { - node: typeParameters, + at: typeParameters, }); } @@ -2993,7 +3035,7 @@ export default (superClass: Class): Class => // ensure the `this` param is first, if it exists for (let i = 0; i < node.params.length; i++) { if (this.isThisParam(node.params[i]) && i > 0) { - this.raise(FlowErrors.ThisParamMustBeFirst, { node: node.params[i] }); + this.raise(FlowErrors.ThisParamMustBeFirst, { at: node.params[i] }); } } @@ -3257,12 +3299,11 @@ export default (superClass: Class): Class => loc: Position, { enumName, memberName }: { enumName: string, memberName: string }, ): void { - this.raise( - FlowErrors.EnumBooleanMemberNotInitialized, - { at: loc }, + this.raise(FlowErrors.EnumBooleanMemberNotInitialized, { + at: loc, memberName, enumName, - ); + }); } flowEnumErrorInvalidExplicitType( @@ -3276,9 +3317,7 @@ export default (superClass: Class): Class => suppliedType === null ? FlowErrors.EnumInvalidExplicitTypeUnknownSupplied : FlowErrors.EnumInvalidExplicitType, - { at: loc }, - enumName, - suppliedType, + { at: loc, enumName, suppliedType }, ); } @@ -3294,34 +3333,32 @@ export default (superClass: Class): Class => : explicitType === "symbol" ? FlowErrors.EnumInvalidMemberInitializerSymbolType : FlowErrors.EnumInvalidMemberInitializerUnknownType, - { at: loc }, + { at: loc, enumName, memberName, explicitType, - ); + }); } flowEnumErrorNumberMemberNotInitialized( loc: Position, { enumName, memberName }: { enumName: string, memberName: string }, ): void { - this.raise( - FlowErrors.EnumNumberMemberNotInitialized, - { at: loc }, + this.raise(FlowErrors.EnumNumberMemberNotInitialized, { + at: loc, enumName, memberName, - ); + }); } flowEnumErrorStringMemberInconsistentlyInitailized( node: N.Node, { enumName }: { enumName: string }, ): void { - this.raise( - FlowErrors.EnumStringMemberInconsistentlyInitailized, - { node }, + this.raise(FlowErrors.EnumStringMemberInconsistentlyInitailized, { + at: node, enumName, - ); + }); } flowEnumMemberInit(): EnumMemberInit { @@ -3417,22 +3454,19 @@ export default (superClass: Class): Class => continue; } if (/^[a-z]/.test(memberName)) { - this.raise( - FlowErrors.EnumInvalidMemberName, - { node: id }, + this.raise(FlowErrors.EnumInvalidMemberName, { + at: id, memberName, - // suggestion - memberName[0].toUpperCase() + memberName.slice(1), + suggestion: memberName[0].toUpperCase() + memberName.slice(1), enumName, - ); + }); } if (seenNames.has(memberName)) { - this.raise( - FlowErrors.EnumDuplicateMemberName, - { node: id }, + this.raise(FlowErrors.EnumDuplicateMemberName, { + at: id, memberName, enumName, - ); + }); } seenNames.add(memberName); const context = { enumName, explicitType, memberName }; @@ -3634,11 +3668,10 @@ export default (superClass: Class): Class => this.expect(tt.braceR); return this.finishNode(node, "EnumNumberBody"); } else { - this.raise( - FlowErrors.EnumInconsistentMemberValues, - { at: nameLoc }, + this.raise(FlowErrors.EnumInconsistentMemberValues, { + at: nameLoc, enumName, - ); + }); return empty(); } } diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index f65b28476cd0..af7236561006 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -20,27 +20,41 @@ import * as N from "../../types"; import { isIdentifierChar, isIdentifierStart } from "../../util/identifier"; import type { Position } from "../../util/location"; import { isNewLine } from "../../util/whitespace"; -import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error"; +import { Errors, toParseErrorClasses } from "../../parse-error"; /* eslint sort-keys: "error" */ -const JsxErrors = makeErrorTemplates( - { - AttributeIsEmpty: - "JSX attributes must only be assigned a non-empty expression.", - MissingClosingTagElement: - "Expected corresponding JSX closing tag for <%0>.", - MissingClosingTagFragment: "Expected corresponding JSX closing tag for <>.", - UnexpectedSequenceExpression: - "Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?", - 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 <>...?", - }, - /* code */ ErrorCodes.SyntaxError, - /* syntaxPlugin */ "jsx", +const JsxErrors = toParseErrorClasses( + _ => ({ + AttributeIsEmpty: _( + "JSX attributes must only be assigned a non-empty expression." + ), + MissingClosingTagElement: _<{ openingTagName: string }>( + ({ openingTagName }) => + `Expected corresponding JSX closing tag for <${openingTagName}>.` + ), + MissingClosingTagFragment: _( + "Expected corresponding JSX closing tag for <>." + ), + UnexpectedSequenceExpression: _( + "Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?" + ), + 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 <>...?" + ), + // FIXME: Unify with Errors.UnexpectedToken + UnexpectedToken: _<{ found: string, HTMLEntity: string }>( + ({ found, HTMLEntity }) => + `Unexpected token \`${found}\`. Did you mean \`${HTMLEntity}\` or \`{'${found}'}\`?` + ), + }), + /* syntaxPlugin */ "jsx" ); + + /* eslint-disable sort-keys */ function isFragment(object: ?N.JSXElement): boolean { @@ -113,17 +127,11 @@ export default (superClass: Class): Class => case charCodes.greaterThan: case charCodes.rightCurlyBrace: if (process.env.BABEL_8_BREAKING) { - const htmlEntity = - ch === charCodes.rightCurlyBrace ? "}" : ">"; - const char = this.input[this.state.pos]; - this.raise( - { - code: ErrorCodes.SyntaxError, - reasonCode: "UnexpectedToken", - template: `Unexpected token \`${char}\`. Did you mean \`${htmlEntity}\` or \`{'${char}'}\`?`, - }, - { at: this.state.curPosition() }, - ); + this.raise(JsxErrors.UnexpectedToken, { + at: this.state.curPosition(), + found: this.input[this.state.pos], + HTMLEntity: ch === charCodes.rightCurlyBrace ? "}" : ">" + }); } /* falls through */ @@ -507,24 +515,20 @@ export default (superClass: Class): Class => node: closingElement, }); } else if (!isFragment(openingElement) && isFragment(closingElement)) { - this.raise( - JsxErrors.MissingClosingTagElement, - // $FlowIgnore - { node: closingElement }, - getQualifiedJSXName(openingElement.name), - ); + this.raise(JsxErrors.MissingClosingTagElement, { + at: closingElement, + openingElement: getQualifiedJSXName(openingElement.name), + }); } else if (!isFragment(openingElement) && !isFragment(closingElement)) { if ( // $FlowIgnore getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name) ) { - this.raise( - JsxErrors.MissingClosingTagElement, - // $FlowIgnore - { node: closingElement }, - getQualifiedJSXName(openingElement.name), - ); + this.raise(JsxErrors.MissingClosingTagElement, { + at: closingElement, + openingElement: getQualifiedJSXName(openingElement.name), + }); } } } diff --git a/packages/babel-parser/src/plugins/placeholders.js b/packages/babel-parser/src/plugins/placeholders.js index c89f395f3345..f4161d68dfb6 100644 --- a/packages/babel-parser/src/plugins/placeholders.js +++ b/packages/babel-parser/src/plugins/placeholders.js @@ -8,7 +8,7 @@ import * as charCodes from "charcodes"; import { tokenLabelName, tt } from "../tokenizer/types"; import type Parser from "../parser"; import * as N from "../types"; -import { makeErrorTemplates, ErrorCodes } from "../parser/error"; +import { Errors, toParseErrorClasses } from "../parse-error"; export type PlaceholderTypes = | "Identifier" @@ -50,11 +50,11 @@ type NodeOf = $Switch< type MaybePlaceholder = NodeOf; // | Placeholder /* eslint sort-keys: "error" */ -const PlaceholderErrors = makeErrorTemplates( - { - ClassNameIsRequired: "A class name is required.", - }, - /* code */ ErrorCodes.SyntaxError, +const PlaceholderErrors = toParseErrorClasses( + _ => ({ + ClassNameIsRequired: _("A class name is required."), + UnexpectedSpace: _("Unexpected space in placeholder.") + }), /* syntaxPlugin */ "placeholders", ); /* eslint-disable sort-keys */ @@ -67,13 +67,13 @@ export default (superClass: Class): Class => if (this.match(tt.placeholder)) { const node = this.startNode(); this.next(); - this.assertNoSpace("Unexpected space in placeholder."); + this.assertNoSpace(); // We can't use this.parseIdentifier because // we don't want nested placeholders. node.name = super.parseIdentifier(/* liberal */ true); - this.assertNoSpace("Unexpected space in placeholder."); + this.assertNoSpace(); this.expect(tt.placeholder); return this.finishPlaceholder(node, expectedNode); } @@ -367,4 +367,13 @@ export default (superClass: Class): Class => super.parseImportSource(...arguments) ); } + + // Throws if the current token and the prev one are separated by a space. + assertNoSpace(): void { + if (this.state.start > this.state.lastTokEndLoc.index) { + this.raise(PlaceholderErrors.UnexpectedSpace, { + at: this.state.lastTokEndLoc + }); + } + } }; diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index fae6d2fbeb36..0b099b202c73 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -37,12 +37,7 @@ import TypeScriptScopeHandler from "./scope"; import * as charCodes from "charcodes"; import type { ExpressionErrors } from "../../parser/util"; import { PARAM } from "../../util/production-parameter"; -import { - Errors, - makeErrorTemplates, - type ErrorTemplate, - ErrorCodes, -} from "../../parser/error"; +import { Errors, toParseErrorClasses } from "../../parse-error"; import { cloneIdentifier } from "../../parser/node"; type TsModifier = @@ -75,108 +70,156 @@ type ParsingContext = | "TypeParametersOrArguments"; /* eslint sort-keys: "error" */ -const TSErrors = makeErrorTemplates( - { - AbstractMethodHasImplementation: - "Method '%0' cannot have an implementation because it is marked abstract.", - AbstractPropertyHasInitializer: - "Property '%0' cannot have an initializer because it is marked abstract.", - AccesorCannotDeclareThisParameter: - "'get' and 'set' accessors cannot declare 'this' parameters.", - AccesorCannotHaveTypeParameters: "An accessor cannot have type parameters.", - ClassMethodHasDeclare: "Class methods cannot have the 'declare' modifier.", - ClassMethodHasReadonly: - "Class methods cannot have the 'readonly' modifier.", - ConstructorHasTypeParameters: - "Type parameters cannot appear on a constructor declaration.", - DeclareAccessor: "'declare' is not allowed in %0ters.", - DeclareClassFieldHasInitializer: - "Initializers are not allowed in ambient contexts.", - DeclareFunctionHasImplementation: - "An implementation cannot be declared in ambient contexts.", - DuplicateAccessibilityModifier: "Accessibility modifier already seen.", - DuplicateModifier: "Duplicate modifier: '%0'.", - EmptyHeritageClauseType: "'%0' list cannot be empty.", - EmptyTypeArguments: "Type argument list cannot be empty.", - EmptyTypeParameters: "Type parameter list cannot be empty.", - ExpectedAmbientAfterExportDeclare: - "'export declare' must be followed by an ambient declaration.", - ImportAliasHasImportType: "An import alias can not use 'import type'.", - IncompatibleModifiers: "'%0' modifier cannot be used with '%1' modifier.", - IndexSignatureHasAbstract: - "Index signatures cannot have the 'abstract' modifier.", - IndexSignatureHasAccessibility: - "Index signatures cannot have an accessibility modifier ('%0').", - IndexSignatureHasDeclare: - "Index signatures cannot have the 'declare' modifier.", - IndexSignatureHasOverride: - "'override' modifier cannot appear on an index signature.", - IndexSignatureHasStatic: - "Index signatures cannot have the 'static' modifier.", - InvalidModifierOnTypeMember: - "'%0' modifier cannot appear on a type member.", - InvalidModifiersOrder: "'%0' modifier must precede '%1' modifier.", - InvalidTupleMemberLabel: - "Tuple members must be labeled with a simple identifier.", - MissingInterfaceName: - "'interface' declarations must be followed by an identifier.", - MixedLabeledAndUnlabeledElements: - "Tuple members must all have names or all not have names.", - NonAbstractClassHasAbstractMethod: - "Abstract methods can only appear within an abstract class.", - NonClassMethodPropertyHasAbstractModifer: - "'abstract' modifier can only appear on a class, method, or property declaration.", - OptionalTypeBeforeRequired: - "A required element cannot follow an optional element.", - OverrideNotInSubClass: - "This member cannot have an 'override' modifier because its containing class does not extend another class.", - 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').", - ReadonlyForMethodSignature: - "'readonly' modifier can only appear on a property declaration or index signature.", - ReservedArrowTypeParam: - "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `() => ...`.", - ReservedTypeAssertion: - "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead.", - SetAccesorCannotHaveOptionalParameter: - "A 'set' accessor cannot have an optional parameter.", - SetAccesorCannotHaveRestParameter: - "A 'set' accessor cannot have rest parameter.", - SetAccesorCannotHaveReturnType: - "A 'set' accessor cannot have a return type annotation.", - SingleTypeParameterWithoutTrailingComma: - "Single type parameter %0 should have a trailing comma. Example usage: <%0,>.", - StaticBlockCannotHaveModifier: - "Static class blocks cannot have any modifier.", - TypeAnnotationAfterAssign: - "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`.", - TypeImportCannotSpecifyDefaultAndNamed: - "A type-only import can specify a default import or named bindings, but not both.", - TypeModifierIsUsedInTypeExports: - "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.", - TypeModifierIsUsedInTypeImports: - "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.", - UnexpectedParameterModifier: - "A parameter property is only allowed in a constructor implementation.", - 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.", - }, - /* code */ ErrorCodes.SyntaxError, - /* syntaxPlugin */ "typescript", +const TSErrors = toParseErrorClasses( + _ => ({ + AbstractMethodHasImplementation: _<{ methodName: string }>(({ methodName }) => + `Method '${methodName}' cannot have an implementation because it is marked abstract.` + ), + AbstractPropertyHasInitializer: _<{ propertyName: string }>(({ propertyName }) => + `Property '${propertyName}' cannot have an initializer because it is marked abstract.` + ), + AccesorCannotDeclareThisParameter: _( + "'get' and 'set' accessors cannot declare 'this' parameters." + ), + AccesorCannotHaveTypeParameters: _( + "An accessor cannot have type parameters." + ), + ClassMethodHasDeclare: _( + "Class methods cannot have the 'declare' modifier." + ), + ClassMethodHasReadonly: _( + "Class methods cannot have the 'readonly' modifier." + ), + ConstructorHasTypeParameters: _( + "Type parameters cannot appear on a constructor declaration." + ), + DeclareAccessor: _<{ accessorKind: "getter" | "setter" }>(({ accessorKind }) => `'declare' is not allowed in ${accessorKind}s.`), + DeclareClassFieldHasInitializer: _( + "Initializers are not allowed in ambient contexts." + ), + DeclareFunctionHasImplementation: _( + "An implementation cannot be declared in ambient contexts." + ), + DuplicateAccessibilityModifier: _<{ modifier: N.Accessibility }>(({ modifier }) => "Accessibility modifier ${modifier} already seen."), + DuplicateModifier: _<{ modifier: TsModifier }>(({ modifier }) => `Duplicate modifier: '${modifier}'.`), + EmptyHeritageClauseType: _<{ descriptor: string }>(({ descriptor }) => `'${descriptor}' list cannot be empty.`), + EmptyTypeArguments: _("Type argument list cannot be empty."), + EmptyTypeParameters: _("Type parameter list cannot be empty."), + ExpectedAmbientAfterExportDeclare: _( + "'export declare' must be followed by an ambient declaration." + ), + ImportAliasHasImportType: _("An import alias can not use 'import type'."), + IncompatibleModifiers: _<{ modifiers: [TsModifier, TsModifier] }>(({ modifiers }) => + `'${modifiers[0]}' modifier cannot be used with '${modifiers[1]}' modifier.` + ), + IndexSignatureHasAbstract: _( + "Index signatures cannot have the 'abstract' modifier." + ), + IndexSignatureHasAccessibility: _<{ modifier: N.Accessibility }>(({ modifier }) => + `Index signatures cannot have an accessibility modifier ('${modifier}').` + ), + IndexSignatureHasDeclare: _( + "Index signatures cannot have the 'declare' modifier." + ), + IndexSignatureHasOverride: _( + "'override' modifier cannot appear on an index signature." + ), + IndexSignatureHasStatic: _( + "Index signatures cannot have the 'static' modifier." + ), + InvalidModifierOnTypeMember: _<{ modifier: TsModifier }>(({ modifier }) => + `'${modifier}' modifier cannot appear on a type member.` + ), + InvalidModifiersOrder: _<{ orderedModifiers: [TsModifier, TsModifier] }>(({ orderedModifiers }) => `'${orderedModifiers[0]}' modifier must precede '${orderedModifiers[1]}' modifier.`), + InvalidTupleMemberLabel: _( + "Tuple members must be labeled with a simple identifier." + ), + MissingInterfaceName: _( + "'interface' declarations must be followed by an identifier." + ), + MixedLabeledAndUnlabeledElements: _( + "Tuple members must all have names or all not have names." + ), + NonAbstractClassHasAbstractMethod: _( + "Abstract methods can only appear within an abstract class." + ), + NonClassMethodPropertyHasAbstractModifer: _( + "'abstract' modifier can only appear on a class, method, or property declaration." + ), + OptionalTypeBeforeRequired: _( + "A required element cannot follow an optional element." + ), + OverrideNotInSubClass: _( + "This member cannot have an 'override' modifier because its containing class does not extend another class." + ), + PatternIsOptional: _( + "A binding pattern parameter cannot be optional in an implementation signature." + ), + PrivateElementHasAbstract: _( + "Private elements cannot have the 'abstract' modifier." + ), + PrivateElementHasAccessibility: _<{ modifier: N.Accessibility }>(({ modifier }) => + `Private elements cannot have an accessibility modifier ('${modifier}').` + ), + ReadonlyForMethodSignature: _( + "'readonly' modifier can only appear on a property declaration or index signature." + ), + ReservedArrowTypeParam: _( + "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `() => ...`." + ), + ReservedTypeAssertion: _( + "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead." + ), + SetAccesorCannotHaveOptionalParameter: _( + "A 'set' accessor cannot have an optional parameter." + ), + SetAccesorCannotHaveRestParameter: _( + "A 'set' accessor cannot have rest parameter." + ), + SetAccesorCannotHaveReturnType: _( + "A 'set' accessor cannot have a return type annotation." + ), + SingleTypeParameterWithoutTrailingComma: _<{ name: string }>(({ name }) => + `Single type parameter ${name} should have a trailing comma. Example usage: <${name},>.` + ), + StaticBlockCannotHaveModifier: _( + "Static class blocks cannot have any modifier." + ), + TypeAnnotationAfterAssign: _( + "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`." + ), + TypeImportCannotSpecifyDefaultAndNamed: _( + "A type-only import can specify a default import or named bindings, but not both." + ), + TypeModifierIsUsedInTypeExports: _( + "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement." + ), + TypeModifierIsUsedInTypeImports: _( + "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement." + ), + UnexpectedParameterModifier: _( + "A parameter property is only allowed in a constructor implementation." + ), + 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: _<{ unsupportedParameterType: string }>(({ unsupportedParameterType }) => + `Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got ${unsupportedParameterType}.` + ), + }), + /* syntaxPlugin */ "typescript" ); + /* eslint-disable sort-keys */ // Doesn't handle "void" or "null" because those are keywords, not identifiers. @@ -272,27 +315,29 @@ export default (superClass: Class): Class => /** Parses a list of modifiers, in any order. * If you need a specific order, you must call this function multiple times: - * this.tsParseModifiers(node, ["public"]); - * this.tsParseModifiers(node, ["abstract", "readonly"]); + * this.tsParseModifiers({ modified: node, allowedModifiers: ["public"] }); + * this.tsParseModifiers({ modified: node, allowedModifiers: ["abstract", "readonly"] }); */ - tsParseModifiers( + tsParseModifiers({ + modified, + allowedModifiers, + disallowedModifiers, + stopOnStartOfClassStaticBlock, + }: { modified: { [key: TsModifier]: ?true, accessibility?: N.Accessibility, }, allowedModifiers: TsModifier[], disallowedModifiers?: TsModifier[], - errorTemplate?: ErrorTemplate, stopOnStartOfClassStaticBlock?: boolean, - ): void { + }): void { const enforceOrder = (loc, modifier, before, after) => { if (modifier === before && modified[after]) { - this.raise( - TSErrors.InvalidModifiersOrder, - { at: loc }, - before, - after, - ); + this.raise(TSErrors.InvalidModifiersOrder, { + at: loc, + orderedModifiers: [before, after], + }); } }; const incompatible = (loc, modifier, mod1, mod2) => { @@ -300,7 +345,10 @@ export default (superClass: Class): Class => (modified[mod1] && modifier === mod2) || (modified[mod2] && modifier === mod1) ) { - this.raise(TSErrors.IncompatibleModifiers, { at: loc }, mod1, mod2); + this.raise(TSErrors.IncompatibleModifiers, { + at: loc, + modifiers: [mod1, mod2], + }); } }; @@ -317,6 +365,7 @@ export default (superClass: Class): Class => if (modified.accessibility) { this.raise(TSErrors.DuplicateAccessibilityModifier, { at: startLoc, + modifier }); } else { enforceOrder(startLoc, modifier, modifier, "override"); @@ -327,7 +376,7 @@ export default (superClass: Class): Class => } } else { if (Object.hasOwnProperty.call(modified, modifier)) { - this.raise(TSErrors.DuplicateModifier, { at: startLoc }, modifier); + this.raise(TSErrors.DuplicateModifier, { at: startLoc, modifier }); } else { enforceOrder(startLoc, modifier, "static", "readonly"); enforceOrder(startLoc, modifier, "static", "override"); @@ -341,8 +390,10 @@ export default (superClass: Class): Class => } if (disallowedModifiers?.includes(modifier)) { - // $FlowIgnore - this.raise(errorTemplate, { at: startLoc }, modifier); + this.raise(TSErrors.InvalidModifierOnTypeMember, { + at: startLoc, + modifier + }); } } } @@ -567,7 +618,7 @@ export default (superClass: Class): Class => refTrailingCommaPos, ); if (node.params.length === 0) { - this.raise(TSErrors.EmptyTypeParameters, { node }); + this.raise(TSErrors.EmptyTypeParameters, { at: node }); } if (refTrailingCommaPos.value !== -1) { this.addExtra(node, "trailingComma", refTrailingCommaPos.value); @@ -621,11 +672,10 @@ export default (superClass: Class): Class => pattern.type !== "ObjectPattern" && pattern.type !== "ArrayPattern" ) { - this.raise( - TSErrors.UnsupportedSignatureParameterKind, - { node: pattern }, - pattern.type, - ); + this.raise(TSErrors.UnsupportedSignatureParameterKind, { + at: pattern, + unsupportedParameterType: pattern.type, + }); } return (pattern: any); }, @@ -689,7 +739,7 @@ export default (superClass: Class): Class => if (this.match(tt.parenL) || this.match(tt.lt)) { if (readonly) { - this.raise(TSErrors.ReadonlyForMethodSignature, { node }); + this.raise(TSErrors.ReadonlyForMethodSignature, { at: node }); } const method: N.TsMethodSignature = nodeAny; if (method.kind && this.match(tt.lt)) { @@ -740,7 +790,7 @@ export default (superClass: Class): Class => } if (method[returnTypeKey]) { this.raise(TSErrors.SetAccesorCannotHaveReturnType, { - node: method[returnTypeKey], + at: method[returnTypeKey], }); } } else { @@ -778,10 +828,10 @@ export default (superClass: Class): Class => } } - this.tsParseModifiers( - node, - ["readonly"], - [ + this.tsParseModifiers({ + modified: node, + allowedModifiers: ["readonly"], + disallowedModifiers: [ "declare", "abstract", "private", @@ -790,8 +840,7 @@ export default (superClass: Class): Class => "static", "override", ], - TSErrors.InvalidModifierOnTypeMember, - ); + }); const idx = this.tsTryParseIndexSignature(node); if (idx) { @@ -910,7 +959,7 @@ export default (superClass: Class): Class => !(type === "TSNamedTupleMember" && elementNode.optional) ) { this.raise(TSErrors.OptionalTypeBeforeRequired, { - node: elementNode, + at: elementNode, }); } @@ -931,7 +980,7 @@ export default (superClass: Class): Class => labeledElements = labeledElements ?? isLabeled; if (labeledElements !== isLabeled) { this.raise(TSErrors.MixedLabeledAndUnlabeledElements, { - node: elementNode, + at: elementNode, }); } }); @@ -960,7 +1009,7 @@ export default (superClass: Class): Class => ) { labeledNode.label = (type.typeName: N.Identifier); } else { - this.raise(TSErrors.InvalidTupleMemberLabel, { node: type }); + this.raise(TSErrors.InvalidTupleMemberLabel, { at: type }); // This produces an invalid AST, but at least we don't drop // nodes representing the invalid source. // $FlowIgnore @@ -1161,7 +1210,7 @@ export default (superClass: Class): Class => case "TSArrayType": return; default: - this.raise(TSErrors.UnexpectedReadonly, { node }); + this.raise(TSErrors.UnexpectedReadonly, { at: node }); } } @@ -1390,11 +1439,10 @@ export default (superClass: Class): Class => } if (containsEsc) { - this.raise( - Errors.InvalidEscapedReservedWord, - { at: this.state.lastTokStartLoc }, - "asserts", - ); + this.raise(Errors.InvalidEscapedReservedWord, { + at: this.state.lastTokStartLoc, + reservedWord: "asserts", + }); } return true; @@ -1476,11 +1524,10 @@ export default (superClass: Class): Class => ); if (!delimitedList.length) { - this.raise( - TSErrors.EmptyHeritageClauseType, - { at: originalStartLoc }, + this.raise(TSErrors.EmptyHeritageClauseType, { + at: originalStartLoc, descriptor, - ); + }); } return delimitedList; @@ -1708,7 +1755,7 @@ export default (superClass: Class): Class => moduleReference.type !== "TSExternalModuleReference" ) { this.raise(TSErrors.ImportAliasHasImportType, { - node: moduleReference, + at: moduleReference, }); } node.moduleReference = moduleReference; @@ -1997,7 +2044,7 @@ export default (superClass: Class): Class => }), ); if (node.params.length === 0) { - this.raise(TSErrors.EmptyTypeArguments, { node }); + this.raise(TSErrors.EmptyTypeArguments, { at: node }); } this.expect(tt.gt); return this.finishNode(node, "TSTypeParameterInstantiation"); @@ -2029,13 +2076,16 @@ export default (superClass: Class): Class => let override = false; if (allowModifiers !== undefined) { const modified = {}; - this.tsParseModifiers(modified, [ - "public", - "private", - "protected", - "override", - "readonly", - ]); + this.tsParseModifiers({ + modified, + allowedModifiers: [ + "public", + "private", + "protected", + "override", + "readonly", + ] + }); accessibility = modified.accessibility; override = modified.override; readonly = modified.readonly; @@ -2059,7 +2109,7 @@ export default (superClass: Class): Class => if (readonly) pp.readonly = readonly; if (override) pp.override = override; if (elt.type !== "Identifier" && elt.type !== "AssignmentPattern") { - this.raise(TSErrors.UnsupportedParameterPropertyKind, { node: pp }); + this.raise(TSErrors.UnsupportedParameterPropertyKind, { at: pp }); } pp.parameter = ((elt: any): N.Identifier | N.AssignmentPattern); return this.finishNode(pp, "TSParameterProperty"); @@ -2092,7 +2142,7 @@ export default (superClass: Class): Class => return; } if (bodilessType === "TSDeclareFunction" && this.state.isAmbientContext) { - this.raise(TSErrors.DeclareFunctionHasImplementation, { node }); + this.raise(TSErrors.DeclareFunctionHasImplementation, { at: node }); if ( // $FlowIgnore node.declare @@ -2119,7 +2169,7 @@ export default (superClass: Class): Class => items.forEach(node => { if (node?.type === "TSTypeCastExpression") { this.raise(TSErrors.UnexpectedTypeAnnotation, { - node: node.typeAnnotation, + at: node.typeAnnotation, }); } }); @@ -2366,7 +2416,7 @@ export default (superClass: Class): Class => importNode.specifiers[0].type === "ImportDefaultSpecifier" ) { this.raise(TSErrors.TypeImportCannotSpecifyDefaultAndNamed, { - node: importNode, + at: importNode, }); } @@ -2491,13 +2541,11 @@ export default (superClass: Class): Class => "readonly", "static", ]; - this.tsParseModifiers( - member, - modifiers, - /* disallowedModifiers */ undefined, - /* errorTemplate */ undefined, - /* stopOnStartOfClassStaticBlock */ true, - ); + this.tsParseModifiers({ + modified: member, + allowedModifiers: modifiers, + stopOnStartOfClassStaticBlock: true, + }); const callParseClassMemberWithIsStatic = () => { if (this.tsIsStartOfStaticBlocks()) { @@ -2536,20 +2584,19 @@ export default (superClass: Class): Class => classBody.body.push(idx); if ((member: any).abstract) { - this.raise(TSErrors.IndexSignatureHasAbstract, { node: member }); + this.raise(TSErrors.IndexSignatureHasAbstract, { at: member }); } if ((member: any).accessibility) { - this.raise( - TSErrors.IndexSignatureHasAccessibility, - { node: member }, - (member: any).accessibility, - ); + this.raise(TSErrors.IndexSignatureHasAccessibility, { + at: member, + modifier: (member: any).accessibility, + }); } if ((member: any).declare) { - this.raise(TSErrors.IndexSignatureHasDeclare, { node: member }); + this.raise(TSErrors.IndexSignatureHasDeclare, { at: member }); } if ((member: any).override) { - this.raise(TSErrors.IndexSignatureHasOverride, { node: member }); + this.raise(TSErrors.IndexSignatureHasOverride, { at: member }); } return; @@ -2557,13 +2604,13 @@ export default (superClass: Class): Class => if (!this.state.inAbstractClass && (member: any).abstract) { this.raise(TSErrors.NonAbstractClassHasAbstractMethod, { - node: member, + at: member, }); } if ((member: any).override) { if (!state.hadSuperClass) { - this.raise(TSErrors.OverrideNotInSubClass, { node: member }); + this.raise(TSErrors.OverrideNotInSubClass, { at: member }); } } @@ -2579,11 +2626,11 @@ export default (superClass: Class): Class => if (optional) methodOrProp.optional = true; if ((methodOrProp: any).readonly && this.match(tt.parenL)) { - this.raise(TSErrors.ClassMethodHasReadonly, { node: methodOrProp }); + this.raise(TSErrors.ClassMethodHasReadonly, { at: methodOrProp }); } if ((methodOrProp: any).declare && this.match(tt.parenL)) { - this.raise(TSErrors.ClassMethodHasDeclare, { node: methodOrProp }); + this.raise(TSErrors.ClassMethodHasDeclare, { at: methodOrProp }); } } @@ -2757,13 +2804,12 @@ export default (superClass: Class): Class => } if (node.abstract && this.match(tt.eq)) { const { key } = node; - this.raise( - TSErrors.AbstractPropertyHasInitializer, - { at: this.state.startLoc }, - key.type === "Identifier" && !node.computed + this.raise(TSErrors.AbstractPropertyHasInitializer, { + at: this.state.startLoc, + propertyName: key.type === "Identifier" && !node.computed ? key.name : `[${this.input.slice(key.start, key.end)}]`, - ); + }); } return super.parseClassProperty(node); @@ -2772,18 +2818,17 @@ export default (superClass: Class): Class => parseClassPrivateProperty( node: N.ClassPrivateProperty, ): N.ClassPrivateProperty { - // $FlowIgnore + // $FlowIgnore if (node.abstract) { - this.raise(TSErrors.PrivateElementHasAbstract, { node }); + this.raise(TSErrors.PrivateElementHasAbstract, { at: node }); } // $FlowIgnore if (node.accessibility) { - this.raise( - TSErrors.PrivateElementHasAccessibility, - { node }, - node.accessibility, - ); + this.raise(TSErrors.PrivateElementHasAccessibility, { + at: node, + modifier: node.accessibility, + }); } this.parseClassPropertyAnnotation(node); @@ -2801,13 +2846,16 @@ export default (superClass: Class): Class => const typeParameters = this.tsTryParseTypeParameters(); if (typeParameters && isConstructor) { this.raise(TSErrors.ConstructorHasTypeParameters, { - node: typeParameters, + at: typeParameters, }); } // $FlowIgnore if (method.declare && (method.kind === "get" || method.kind === "set")) { - this.raise(TSErrors.DeclareAccessor, { node: method }, method.kind); + this.raise(TSErrors.DeclareAccessor, { + at: method, + accessorKind: method.kind + }); } if (typeParameters) method.typeParameters = typeParameters; super.pushClassMethod( @@ -2974,15 +3022,12 @@ export default (superClass: Class): Class => }, state); if (invalidSingleType) { - this.raise( - TSErrors.SingleTypeParameterWithoutTrailingComma, - { - at: createPositionWithColumnOffset(invalidSingleType.loc.end, 1), - }, - process.env.BABEL_8_BREAKING + this.raise(TSErrors.SingleTypeParameterWithoutTrailingComma, { + at: createPositionWithColumnOffset(invalidSingleType.loc.end, 1), + name: process.env.BABEL_8_BREAKING ? invalidSingleType.name.name : invalidSingleType.name, - ); + }); } /*:: invariant(arrow.node != null) */ @@ -3040,7 +3085,7 @@ export default (superClass: Class): Class => !node.extra?.trailingComma && this.getPluginOption("typescript", "disallowAmbiguousJSXLike") ) { - this.raise(TSErrors.ReservedArrowTypeParam, { node }); + this.raise(TSErrors.ReservedArrowTypeParam, { at: node }); } } @@ -3085,7 +3130,7 @@ export default (superClass: Class): Class => !this.state.isAmbientContext && !this.state.inType ) { - this.raise(TSErrors.PatternIsOptional, { node: param }); + this.raise(TSErrors.PatternIsOptional, { at: param }); } ((param: any): N.Identifier).optional = true; @@ -3162,7 +3207,7 @@ export default (superClass: Class): Class => contextDescription !== "parenthesized expression" && !expr.extra?.parenthesized ) { - this.raise(Errors.InvalidLhs, { node: expr }, contextDescription); + this.raise(Errors.InvalidLhs, { at: expr, contextDescription }); break; } this.checkLVal(expr.expression, "parenthesized expression", ...args); @@ -3240,7 +3285,7 @@ export default (superClass: Class): Class => node.right.start < node.typeAnnotation.start ) { this.raise(TSErrors.TypeAnnotationAfterAssign, { - node: node.typeAnnotation, + at: node.typeAnnotation, }); } @@ -3296,7 +3341,7 @@ export default (superClass: Class): Class => exprList[i] = this.typeCastToParameter(expr); } else { this.raise(TSErrors.UnexpectedTypeCastInParameter, { - node: expr, + at: expr, }); } break; @@ -3404,7 +3449,7 @@ export default (superClass: Class): Class => if (!this.hasFollowingLineBreak()) { node.abstract = true; this.raise(TSErrors.NonClassMethodPropertyHasAbstractModifer, { - node, + at: node, }); this.next(); return this.tsParseInterfaceDeclaration( @@ -3424,13 +3469,12 @@ export default (superClass: Class): Class => : !!method.body; if (hasBody) { const { key } = method; - this.raise( - TSErrors.AbstractMethodHasImplementation, - { node: method }, - key.type === "Identifier" && !method.computed + this.raise(TSErrors.AbstractMethodHasImplementation, { + at: method, + methodName: key.type === "Identifier" && !method.computed ? key.name : `[${this.input.slice(key.start, key.end)}]`, - ); + }); } } return method; diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 8c6dbde4586a..5d3b51f1efc4 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -16,7 +16,7 @@ import { type TokenType, } from "./types"; import { type TokContext } from "./context"; -import { Errors, ParseErrorClass, RaiseErrorProperties } from "../parse-error"; +import { Errors, ParseError, RaiseErrorProperties } from "../parse-error"; import { lineBreakG, isNewLine, @@ -24,7 +24,7 @@ import { skipWhiteSpace, } from "../util/whitespace"; import State from "./state"; -import type { LookaheadState } from "./state"; +import type { LookaheadState, DeferredStrictErrorClass } from "./state"; const VALID_REGEX_FLAGS = new Set([ charCodes.lowercaseG, @@ -1071,11 +1071,10 @@ export default class Tokenizer extends CommentsParser { } } - throw this.raise( - Errors.InvalidOrUnexpectedToken, - { at: this.state.curPosition() }, - String.fromCodePoint(code), - ); + throw this.raise(Errors.InvalidOrUnexpectedToken, { + at: this.state.curPosition(), + found: String.fromCodePoint(code) + }); } finishOp(type: TokenType, size: number): void { @@ -1240,11 +1239,10 @@ export default class Tokenizer extends CommentsParser { if (this.options.errorRecovery && val <= 9) { val = 0; - this.raise( - Errors.InvalidDigit, - { at: this.state.curPosition() }, + this.raise(Errors.InvalidDigit, { + at: this.state.curPosition(), radix, - ); + }); } else if (forceLen) { val = 0; invalid = true; @@ -1273,12 +1271,11 @@ export default class Tokenizer extends CommentsParser { this.state.pos += 2; // 0x const val = this.readInt(radix); if (val == null) { - this.raise( - Errors.InvalidDigit, + this.raise(Errors.InvalidDigit, { // Numeric literals can't have newlines, so this is safe to do. - { at: createPositionWithColumnOffset(startLoc, 2) }, + at: createPositionWithColumnOffset(startLoc, 2), radix, - ); + }); } const next = this.input.charCodeAt(this.state.pos); @@ -1326,7 +1323,7 @@ export default class Tokenizer extends CommentsParser { if (hasLeadingZero) { const integer = this.input.slice(start, this.state.pos); - this.recordStrictModeErrors(Errors.StrictOctalLiteral, startLoc); + this.recordStrictModeErrors(Errors.StrictOctalLiteral, { at: startLoc }); if (!this.state.strict) { // disallow numeric separators in non octal decimals and legacy octal likes const underscorePos = integer.indexOf("_"); @@ -1541,14 +1538,26 @@ export default class Tokenizer extends CommentsParser { } } - recordStrictModeErrors(message: ErrorTemplate, loc: Position) { - if (this.state.strict && !this.state.strictErrors.has(loc.index)) { - this.raise(message, { at: loc }); +// ErrorProperties, +// ParseErrorClass: Class>> +/* + recordStrictModeErrors< + ErrorProperties, + T: DeferredStrictErrorClass>( + ParseErrorClass: T, + raiseProperties: RaiseErrorProperties, + ) { + const { at } = raiseProperties; + const loc = at instanceof Position ? at : at.loc.start; + const index = loc.index; + + if (this.state.strict && !this.state.strictErrors.has(index)) { + this.raise(ParseErrorClass, raiseProperties); } else { - this.state.strictErrors.set(loc.index, { loc, message }); + this.state.strictErrors.set(index, [ParseErrorClass, raiseProperties]); } } - +*/ // Used to read escaped characters readEscapedChar(inTemplate: boolean): string | null { const throwOnInvalid = !inTemplate; @@ -1592,12 +1601,11 @@ export default class Tokenizer extends CommentsParser { if (inTemplate) { return null; } else { - this.recordStrictModeErrors( - Errors.StrictNumericEscape, + this.recordStrictModeErrors(Errors.StrictNumericEscape, { // We immediately follow a "\\", and we're an 8 or a 9, so we must // be on the same line. - createPositionWithColumnOffset(this.state.curPosition(), -1), - ); + at: createPositionWithColumnOffset(this.state.curPosition(), -1), + }); } // fall through default: @@ -1631,7 +1639,9 @@ export default class Tokenizer extends CommentsParser { if (inTemplate) { return null; } else { - this.recordStrictModeErrors(Errors.StrictNumericEscape, codePos); + this.recordStrictModeErrors(Errors.StrictNumericEscape, { + at: codePos + }); } } @@ -1734,32 +1744,64 @@ export default class Tokenizer extends CommentsParser { checkKeywordEscapes(): void { const { type } = this.state; if (tokenIsKeyword(type) && this.state.containsEsc) { - this.raise( - Errors.InvalidEscapedReservedWord, - { at: this.state.startLoc }, - tokenLabelName(type), - ); + this.raise(Errors.InvalidEscapedReservedWord, { + at: this.state.startLoc, + reservedWord: tokenLabelName(type), + }); } } raise< ErrorProperties, - ParseErrorClass: Class>> + T: Class>> ( - SomeParseError: ParseErrorClass, - raiseProperties: RaiseProperties + ParseErrorClass: T, + raiseProperties: RaiseErrorProperties ) : ParseError { const { at, ...rest } = raiseProperties; const loc = at instanceof Position ? at : at.loc; - const error = new SomeParseError({ ...rest, loc: at }); + const error = new ParseErrorClass({ ...rest, loc: at }); - /*!SyntaxError.recoverable || */ if (!this.options.errorRecovery) throw error; if (!this.isLookahead) this.state.errors.push(error); return error; } +/* + /** + * Raise a parsing error on given position pos. If errorRecovery is true, + * it will first search current errors and overwrite the error thrown on the exact + * position before with the new error message. If errorRecovery is false, it + * fallbacks to `raise`. + * + * @param {number} pos + * @param {string} errorTemplate + * @param {...any} params + * @returns {(Error | empty)} + * @memberof ParserError + */ + raiseOverwrite>>( + ParseErrorClass: T, + raiseProperties: RaiseErrorProperties + ): ParseError | empty { + const { at, ...rest } = raiseProperties; + const loc = at instanceof Position ? at : at.loc; + const pos = loc.index; + const errors = this.state.errors; + + for (let i = errors.length - 1; i >= 0; i--) { + const error = errors[i]; + if (error.pos === pos) { + return (errors[i] = new ParseErrorClass({ ...rest, loc })); + } + if (error.pos < pos) break; + } + + return this.raise(ParseErrorClass, raiseProperties); + } + + // Raise an unexpected token error. Can take the expected token type. unexpected(loc?: Position | null, type?: TokenType): void { throw this.raise(Errors.UnexpectedToken, { diff --git a/packages/babel-parser/src/tokenizer/state.js b/packages/babel-parser/src/tokenizer/state.js index ac9a04187e7a..6c885285a437 100644 --- a/packages/babel-parser/src/tokenizer/state.js +++ b/packages/babel-parser/src/tokenizer/state.js @@ -7,7 +7,12 @@ import { Position } from "../util/location"; import { types as ct, type TokContext } from "./context"; import { tt, type TokenType } from "./types"; -import type { ErrorData, ParsingError } from "../parser/error"; +import type { ParseError, DeferredParseErrorMap } from "../parse-error"; +import StrictErrors from "../parse-error/strict-mode"; + +export type DeferredStrictErrorClass = + | typeof StrictErrors.StrictNumericEscape + | typeof StrictErrors.StrictOctalLiteral; type TopicContextState = { // When a topic binding has been currently established, @@ -45,7 +50,7 @@ export default class State { this.startLoc = this.endLoc = new Position(startLine, startColumn, 0); } - errors: ParsingError[] = []; + errors: ParseError[] = []; // Used to signify the start of a potential arrow function potentialArrowAt: number = -1; @@ -140,7 +145,7 @@ export default class State { // todo(JLHwung): set strictErrors to null and avoid recording string errors // after a non-directive is parsed - strictErrors: Map = new Map(); + strictErrors: DeferredParseErrorMap = new Map(); // Tokens length in token store tokensLength: number = 0; From 11794cbed735a03be7bf1be307037b38d72e3d10 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 9 Feb 2022 13:14:27 -0800 Subject: [PATCH 003/104] Convert more. Reviewed by @tolmasky. --- lib/babel-packages.js.flow | 4 +- packages/babel-parser/src/parse-error.js | 13 +- packages/babel-parser/src/parser/error.js | 159 ------------------ .../babel-parser/src/parser/expression.js | 14 +- packages/babel-parser/src/parser/lval.js | 9 +- packages/babel-parser/src/parser/statement.js | 2 +- packages/babel-parser/src/parser/util.js | 38 +---- packages/babel-parser/src/plugins/estree.js | 2 +- .../babel-parser/src/plugins/flow/index.js | 3 - .../babel-parser/src/plugins/jsx/index.js | 3 - .../babel-parser/src/plugins/placeholders.js | 3 - .../src/plugins/typescript/index.js | 3 - packages/babel-parser/src/tokenizer/index.js | 57 ++++--- packages/babel-parser/src/tokenizer/state.js | 4 +- packages/babel-parser/src/types.js | 4 +- packages/babel-parser/src/util/class-scope.js | 21 ++- .../babel-parser/src/util/expression-scope.js | 79 +++++---- packages/babel-parser/src/util/scope.js | 20 ++- 18 files changed, 129 insertions(+), 309 deletions(-) delete mode 100644 packages/babel-parser/src/parser/error.js diff --git a/lib/babel-packages.js.flow b/lib/babel-packages.js.flow index dab37130a96a..bffca14e73d5 100644 --- a/lib/babel-packages.js.flow +++ b/lib/babel-packages.js.flow @@ -39,7 +39,7 @@ declare module "@babel/helper-optimise-call-expression" { declare export default function optimiseCallExpression( callee: BabelNodeExpression, thisNode: BabelNodeExpression, - args: $Readonly>, + args: $ReadOnly>, optional: boolean ): BabelNodeCallExpression | BabelNodeOptionalCallExpression; } @@ -154,7 +154,7 @@ declare module "@babel/helper-validator-option" { } declare function findSuggestion( str: string, - arr: $ReadonlyArray + arr: $ReadOnlyArray ): string; } diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index af8c082b02c7..f88a6a9c7774 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -12,7 +12,7 @@ export enum ParseErrorCode { type ToMessage = (self: ErrorProperties) => string; -class ParseError extends SyntaxError { +export class ParseError extends SyntaxError { static reasonCode: string; static toMessage: ToMessage; @@ -78,17 +78,6 @@ export type RaiseProperties = {| ...Origin, |}; - -export type DeferredErrorDescription>> = [ - T, - T["ErrorProperties"] -]; - -export type DeferredParseErrorMap>> = Map< - number, - DeferredErrorDescription ->; - import StandardErrors from "./parse-error/standard"; import StrictErrors from "./parse-error/strict-mode"; diff --git a/packages/babel-parser/src/parser/error.js b/packages/babel-parser/src/parser/error.js deleted file mode 100644 index db9e3de14d19..000000000000 --- a/packages/babel-parser/src/parser/error.js +++ /dev/null @@ -1,159 +0,0 @@ -// @flow -/* eslint sort-keys: "error" */ -import { type Position } from "../util/location"; -import CommentsParser from "./comments"; -import { type ErrorCode, ErrorCodes } from "./error-codes"; -import { type Node } from "../types"; - -// This function is used to raise exceptions on parse errors. It -// takes an offset integer (into the current `input`) to indicate -// the location of the error, attaches the position to the end -// of the error message, and then raises a `SyntaxError` with that -// message. - -type ErrorContext = { - pos: number, - loc: Position, - missingPlugin?: Array, - code?: string, - reasonCode?: String, -}; -export type ParsingError = SyntaxError & ErrorContext; - -export type ErrorTemplate = { - code: ErrorCode, - template: string, - reasonCode: string, -}; -export type ErrorTemplates = { - [key: string]: ErrorTemplate, -}; - -type Origin = {| node: Node |} | {| at: Position |}; - -type SyntaxPlugin = - | "flow" - | "typescript" - | "jsx" - | "placeholders" - | typeof undefined; - -function keepReasonCodeCompat(reasonCode: string, syntaxPlugin: SyntaxPlugin) { - if (!process.env.BABEL_8_BREAKING) { - // For consistency in TypeScript and Flow error codes - if (syntaxPlugin === "flow" && reasonCode === "PatternIsOptional") { - return "OptionalBindingPattern"; - } - } - return reasonCode; -} - -export function makeErrorTemplates( - messages: { - [key: string]: string, - }, - code: ErrorCode, - syntaxPlugin?: SyntaxPlugin, -): ErrorTemplates { - const templates: ErrorTemplates = {}; - Object.keys(messages).forEach(reasonCode => { - templates[reasonCode] = Object.freeze({ - code, - reasonCode: keepReasonCodeCompat(reasonCode, syntaxPlugin), - template: messages[reasonCode], - }); - }); - return Object.freeze(templates); -} - -export { ErrorCodes }; -export { - ErrorMessages as Errors, - SourceTypeModuleErrorMessages as SourceTypeModuleErrors, -} from "./error-message"; - -export type raiseFunction = (ErrorTemplate, Origin, ...any) => void; -export type ErrorData = {| message: ErrorTemplate, loc: Position |}; - -export default class ParserError extends CommentsParser { - // Forward-declaration: defined in tokenizer/index.js - /*:: - +isLookahead: boolean; - */ - - raise( - { code, reasonCode, template }: ErrorTemplate, - origin: Origin, - ...params: any - ): Error | empty { - return this.raiseWithData( - origin.node ? origin.node.loc.start : origin.at, - { code, reasonCode }, - template, - ...params, - ); - } - - /** - * Raise a parsing error on given position pos. If errorRecovery is true, - * it will first search current errors and overwrite the error thrown on the exact - * position before with the new error message. If errorRecovery is false, it - * fallbacks to `raise`. - * - * @param {number} pos - * @param {string} errorTemplate - * @param {...any} params - * @returns {(Error | empty)} - * @memberof ParserError - */ - raiseOverwrite( - loc: Position, - { code, template }: ErrorTemplate, - ...params: any - ): Error | empty { - const pos = loc.index; - const message = - template.replace(/%(\d+)/g, (_, i: number) => params[i]) + - ` (${loc.line}:${loc.column})`; - if (this.options.errorRecovery) { - const errors = this.state.errors; - for (let i = errors.length - 1; i >= 0; i--) { - const error = errors[i]; - if (error.pos === pos) { - return Object.assign(error, { message }); - } else if (error.pos < pos) { - break; - } - } - } - return this._raise({ code, loc, pos }, message); - } - - raiseWithData( - loc: Position, - data?: { - missingPlugin?: Array, - code?: string, - }, - errorTemplate: string, - ...params: any - ): Error | empty { - const pos = loc.index; - const message = - errorTemplate.replace(/%(\d+)/g, (_, i: number) => params[i]) + - ` (${loc.line}:${loc.column})`; - return this._raise(Object.assign(({ loc, pos }: Object), data), message); - } - - _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; - } else { - throw err; - } - } -} diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index ef40f2c8d23b..808a36256333 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -67,8 +67,7 @@ import { newAsyncArrowScope, newExpressionScope, } from "../util/expression-scope"; -import { Errors, SourceTypeModuleErrors } from "./error"; -import type { ParsingError } from "./error"; +import { Errors, ParseError, SourceTypeModuleErrors } from "../parse-error"; import { setInnerComments } from "./comments"; import { cloneIdentifier } from "./node"; @@ -262,7 +261,7 @@ export default class ExpressionParser extends LValParser { // the typescript and flow plugins. setOptionalParametersError( refExpressionErrors: ExpressionErrors, - resultError?: ParsingError, + resultError?: ParseError, ) { refExpressionErrors.optionalParametersLoc = resultError?.loc ?? this.state.startLoc; @@ -2688,10 +2687,7 @@ export default class ExpressionParser extends LValParser { return; } - this.expressionScope.recordAsyncArrowParametersError( - Errors.AwaitBindingIdentifier, - startLoc, - ); + this.expressionScope.recordAsyncArrowParametersError({ at: startLoc }); } else if (word === "arguments") { if (this.scope.inClassAndNotInNonArrowFunction) { this.raise(Errors.ArgumentsInClass, { at: startLoc }); @@ -2735,8 +2731,8 @@ export default class ExpressionParser extends LValParser { const node = this.startNodeAt(startPos, startLoc); this.expressionScope.recordParameterInitializerError( - node.loc.start, Errors.AwaitExpressionFormalParameter, + { at: node }, ); if (this.eat(tt.star)) { @@ -2784,8 +2780,8 @@ export default class ExpressionParser extends LValParser { const node = this.startNode(); this.expressionScope.recordParameterInitializerError( - node.loc.start, Errors.YieldInParameter, + { at: node } ); this.next(); diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index e01570c851f1..6c0ad7850173 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -28,7 +28,7 @@ import { import { NodeUtils } from "./node"; import { type BindingTypes, BIND_NONE } from "../util/scopeflags"; import { ExpressionErrors } from "./util"; -import { Errors } from "./error"; +import { Errors } from "../parse-error"; const unwrapParenthesizedExpression = (node: Node): Node => { return node.type === "ParenthesizedExpression" @@ -99,10 +99,7 @@ export default class LValParser extends NodeUtils { // i.e. `([(a) = []] = []) => {}` // see also `recordParenthesizedIdentifierError` signature in packages/babel-parser/src/util/expression-scope.js if (parenthesized.type === "Identifier") { - this.expressionScope.recordParenthesizedIdentifierError( - Errors.InvalidParenthesizedAssignment, - node.loc.start, - ); + this.expressionScope.recordParenthesizedIdentifierError({ at: node }); } else if (parenthesized.type !== "MemberExpression") { // A parenthesized member expression can be in LHS but not in pattern. // If the LHS is later interpreted as a pattern, `checkLVal` will throw for member expression binding @@ -203,14 +200,12 @@ export default class LValParser extends NodeUtils { isLHS: boolean, ) { if (prop.type === "ObjectMethod") { - /* eslint-disable @babel/development-internal/dry-error-messages */ this.raise( prop.kind === "get" || prop.kind === "set" ? Errors.PatternHasAccessor : Errors.PatternHasMethod, { at: prop.key }, ); - /* eslint-enable @babel/development-internal/dry-error-messages */ } else if (prop.type === "SpreadElement" && !isLast) { this.raise(Errors.RestTrailingComma, { at: prop }); } else { diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 6d10d6b5cdce..4ff3861d47a5 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -10,7 +10,7 @@ import { getExportedToken, } from "../tokenizer/types"; import ExpressionParser from "./expression"; -import { Errors, SourceTypeModuleErrors } from "./error"; +import { Errors, SourceTypeModuleErrors } from "../parse-error"; import { isIdentifierChar, isIdentifierStart } from "../util/identifier"; import { lineBreak } from "../util/whitespace"; import * as charCodes from "charcodes"; diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index 472a2da2fb79..2f2e1bdba107 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -161,32 +161,6 @@ export default class UtilParser extends Tokenizer { }); } - expectPlugin(pluginConfig: PluginConfig, loc?: ?Position): true { - if (!this.hasPlugin(pluginConfig)) { - throw this.raiseWithData( - loc != null ? loc : this.state.startLoc, - { missingPlugin: this.getPluginNamesFromConfigs([pluginConfig]) }, - `This experimental syntax requires enabling the parser plugin: ${JSON.stringify( - pluginConfig, - )}.`, - ); - } - - return true; - } - - expectOnePlugin(pluginConfigs: Array): void { - if (!pluginConfigs.some(c => this.hasPlugin(c))) { - throw this.raiseWithData( - this.state.startLoc, - { missingPlugin: this.getPluginNamesFromConfigs(pluginConfigs) }, - `This experimental syntax requires enabling one of the following parser plugin(s): ${pluginConfigs - .map(c => JSON.stringify(c)) - .join(", ")}.`, - ); - } - } - // tryParse will clone parser state. // It is expensive and should be used with cautions tryParse>( @@ -194,7 +168,7 @@ export default class UtilParser extends Tokenizer { oldState: State = this.state.clone(), ): | TryParse - | TryParse + | TryParse, boolean, false, State> | TryParse { const abortSignal: { node: T | null } = { node: null }; try { @@ -211,7 +185,7 @@ export default class UtilParser extends Tokenizer { this.state.tokensLength = failState.tokensLength; return { node, - error: (failState.errors[oldState.errors.length]: SyntaxError), + error: (failState.errors[oldState.errors.length]: ParseError), thrown: false, aborted: false, failState, @@ -228,7 +202,7 @@ export default class UtilParser extends Tokenizer { } catch (error) { const failState = this.state; this.state = oldState; - if (error instanceof SyntaxError) { + if (error instanceof ParseError) { return { node: null, error, thrown: true, aborted: false, failState }; } if (error === abortSignal) { @@ -360,16 +334,16 @@ export default class UtilParser extends Tokenizer { const oldScope = this.scope; const ScopeHandler = this.getScopeHandler(); - this.scope = new ScopeHandler(this.raise.bind(this), this.inModule); + this.scope = new ScopeHandler(this, inModule); const oldProdParam = this.prodParam; this.prodParam = new ProductionParameterHandler(); const oldClassScope = this.classScope; - this.classScope = new ClassScopeHandler(this.raise.bind(this)); + this.classScope = new ClassScopeHandler(this); const oldExpressionScope = this.expressionScope; - this.expressionScope = new ExpressionScopeHandler(this.raise.bind(this)); + this.expressionScope = new ExpressionScopeHandler(this); return () => { // Revert state diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index 5c8a1aa3c23f..725babe64a76 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -6,7 +6,7 @@ import type { ExpressionErrors } from "../parser/util"; import * as N from "../types"; import type { Node as NodeType, NodeBase, File } from "../types"; import type { Position } from "../util/location"; -import { Errors } from "../parser/error"; +import { Errors } from "../parse-error"; const { defineProperty } = Object; const toUnenumerable = (object, key) => diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 9dd6c4bbba60..a3d67d3f1a97 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -2,9 +2,6 @@ /*:: declare var invariant; */ -// Error messages are colocated with the plugin. -/* eslint-disable @babel/development-internal/dry-error-messages */ - import type Parser from "../../parser"; import { tokenIsIdentifier, diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index af7236561006..34217d1989f7 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -1,8 +1,5 @@ // @flow -// Error messages are colocated with the plugin. -/* eslint-disable @babel/development-internal/dry-error-messages */ - import * as charCodes from "charcodes"; import XHTMLEntities from "./xhtml"; diff --git a/packages/babel-parser/src/plugins/placeholders.js b/packages/babel-parser/src/plugins/placeholders.js index f4161d68dfb6..5e4169913462 100644 --- a/packages/babel-parser/src/plugins/placeholders.js +++ b/packages/babel-parser/src/plugins/placeholders.js @@ -1,8 +1,5 @@ // @flow -// Error messages are colocated with the plugin. -/* eslint-disable @babel/development-internal/dry-error-messages */ - import * as charCodes from "charcodes"; import { tokenLabelName, tt } from "../tokenizer/types"; diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 0b099b202c73..82b5cf67b642 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -2,9 +2,6 @@ /*:: declare var invariant; */ -// Error messages are colocated with the plugin. -/* eslint-disable @babel/development-internal/dry-error-messages */ - import type State from "../../tokenizer/state"; import { tokenIsIdentifier, diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 5d3b51f1efc4..d82ff7ba6071 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -16,7 +16,7 @@ import { type TokenType, } from "./types"; import { type TokContext } from "./context"; -import { Errors, ParseError, RaiseErrorProperties } from "../parse-error"; +import { Errors, ParseError, RaiseProperties } from "../parse-error"; import { lineBreakG, isNewLine, @@ -286,9 +286,8 @@ export default class Tokenizer extends CommentsParser { // after a "use strict" directive. Strict mode will be set at parse // time for any literals that occur after the next node of the strict // directive. - this.state.strictErrors.forEach(({ message, loc }) => - /* eslint-disable @babel/development-internal/dry-error-messages */ - this.raise(message, { at: loc }), + this.state.strictErrors.forEach(([ParseErrorClass, at]) => + this.raise(ParseErrorClass, { at }), ); this.state.strictErrors.clear(); } @@ -1538,26 +1537,19 @@ export default class Tokenizer extends CommentsParser { } } -// ErrorProperties, -// ParseErrorClass: Class>> -/* - recordStrictModeErrors< - ErrorProperties, - T: DeferredStrictErrorClass>( - ParseErrorClass: T, - raiseProperties: RaiseErrorProperties, + recordStrictModeErrors( + ParseErrorClass: DeferredStrictErrorClass, + { at } : { at: Position }, ) { - const { at } = raiseProperties; - const loc = at instanceof Position ? at : at.loc.start; - const index = loc.index; + const index = at.index; if (this.state.strict && !this.state.strictErrors.has(index)) { - this.raise(ParseErrorClass, raiseProperties); + this.raise(ParseErrorClass, { at }); } else { - this.state.strictErrors.set(index, [ParseErrorClass, raiseProperties]); + this.state.strictErrors.set(index, [ParseErrorClass, at]); } } -*/ + // Used to read escaped characters readEscapedChar(inTemplate: boolean): string | null { const throwOnInvalid = !inTemplate; @@ -1756,7 +1748,7 @@ export default class Tokenizer extends CommentsParser { T: Class>> ( ParseErrorClass: T, - raiseProperties: RaiseErrorProperties + raiseProperties: RaiseProperties ) : ParseError { const { at, ...rest } = raiseProperties; const loc = at instanceof Position ? at : at.loc; @@ -1783,7 +1775,7 @@ export default class Tokenizer extends CommentsParser { */ raiseOverwrite>>( ParseErrorClass: T, - raiseProperties: RaiseErrorProperties + raiseProperties: RaiseProperties ): ParseError | empty { const { at, ...rest } = raiseProperties; const loc = at instanceof Position ? at : at.loc; @@ -1801,6 +1793,9 @@ export default class Tokenizer extends CommentsParser { return this.raise(ParseErrorClass, raiseProperties); } + // updateContext is used by the jsx plugin + // eslint-disable-next-line no-unused-vars + updateContext(prevType: TokenType): void {} // Raise an unexpected token error. Can take the expected token type. unexpected(loc?: Position | null, type?: TokenType): void { @@ -1810,7 +1805,23 @@ export default class Tokenizer extends CommentsParser { }); } - // updateContext is used by the jsx plugin - // eslint-disable-next-line no-unused-vars - updateContext(prevType: TokenType): void {} + expectPlugin(pluginName: string, loc?: Position): true { + if (this.hasPlugin(pluginName)) { + return true; + } + + throw this.raise(Errors.MissingPlugin, { + at: loc != null ? loc : this.state.startLoc, + missingPlugin: pluginName + }); + } + + expectOnePlugin(pluginNames: string[]): void { + if (!pluginNames.some(name => this.hasPlugin(name))) { + throw this.raise(Errors.MissingOneOfPlugins, { + at: this.state.startLoc, + missingPlugin: pluginNames + }); + } + } } diff --git a/packages/babel-parser/src/tokenizer/state.js b/packages/babel-parser/src/tokenizer/state.js index 6c885285a437..6e7260464781 100644 --- a/packages/babel-parser/src/tokenizer/state.js +++ b/packages/babel-parser/src/tokenizer/state.js @@ -7,7 +7,7 @@ import { Position } from "../util/location"; import { types as ct, type TokContext } from "./context"; import { tt, type TokenType } from "./types"; -import type { ParseError, DeferredParseErrorMap } from "../parse-error"; +import type { ParseError } from "../parse-error"; import StrictErrors from "../parse-error/strict-mode"; export type DeferredStrictErrorClass = @@ -145,7 +145,7 @@ export default class State { // todo(JLHwung): set strictErrors to null and avoid recording string errors // after a non-directive is parsed - strictErrors: DeferredParseErrorMap = new Map(); + strictErrors: Map = new Map(); // Tokens length in token store tokensLength: number = 0; diff --git a/packages/babel-parser/src/types.js b/packages/babel-parser/src/types.js index 5fb402aadbbb..e6b53e381722 100644 --- a/packages/babel-parser/src/types.js +++ b/packages/babel-parser/src/types.js @@ -4,7 +4,7 @@ import type { SourceType } from "./options"; import type { Token } from "./tokenizer"; import type { SourceLocation } from "./util/location"; import type { PlaceholderTypes } from "./plugins/placeholders"; -import type { ParsingError } from "./parser/error"; +import type { ParseError } from "./parse-error"; /* * If making any changes to the AST, update: @@ -159,7 +159,7 @@ export type DecimalLiteral = NodeBase & { export type ParserOutput = { comments: $ReadOnlyArray, - errors: Array, + errors: Array>, tokens?: $ReadOnlyArray, }; // Programs diff --git a/packages/babel-parser/src/util/class-scope.js b/packages/babel-parser/src/util/class-scope.js index db2c95273859..d89e41598a36 100644 --- a/packages/babel-parser/src/util/class-scope.js +++ b/packages/babel-parser/src/util/class-scope.js @@ -6,7 +6,8 @@ import { type ClassElementTypes, } from "./scopeflags"; import { Position } from "./location"; -import { Errors, type raiseFunction } from "../parser/error"; +import { Errors } from "../parse-error"; +import Tokenizer from "../tokenizer"; export class ClassScope { // A list of private named declared in the current class @@ -21,12 +22,12 @@ export class ClassScope { } export default class ClassScopeHandler { + parser: Tokenizer; stack: Array = []; - declare raise: raiseFunction; undefinedPrivateNames: Map = new Map(); - constructor(raise: raiseFunction) { - this.raise = raise; + constructor(parser: Tokenizer) { + this.parser = parser; } current(): ClassScope { @@ -52,7 +53,10 @@ export default class ClassScopeHandler { current.undefinedPrivateNames.set(name, loc); } } else { - this.raise(Errors.InvalidPrivateFieldResolution, { at: loc }, name); + this.parser.raise(Errors.InvalidPrivateFieldResolution, { + at: loc, + name, + }); } } } @@ -87,7 +91,7 @@ export default class ClassScopeHandler { } if (redefined) { - this.raise(Errors.PrivateNameRedeclaration, { at: loc }, name); + this.parser.raise(Errors.PrivateNameRedeclaration, { at: loc, name }); } privateNames.add(name); @@ -104,7 +108,10 @@ export default class ClassScopeHandler { classScope.undefinedPrivateNames.set(name, loc); } else { // top-level - this.raise(Errors.InvalidPrivateFieldResolution, { at: loc }, name); + this.parser.raise(Errors.InvalidPrivateFieldResolution, { + at: loc, + name + }); } } } diff --git a/packages/babel-parser/src/util/expression-scope.js b/packages/babel-parser/src/util/expression-scope.js index 2cc38db8c121..5a81e0fd1b61 100644 --- a/packages/babel-parser/src/util/expression-scope.js +++ b/packages/babel-parser/src/util/expression-scope.js @@ -1,7 +1,9 @@ // @flow -import type { ErrorData, ErrorTemplate, raiseFunction } from "../parser/error"; +import { Errors, ParseError } from "../parse-error"; import { Position } from "./location"; +import type { Node } from "../types"; +import Tokenizer from "../tokenizer"; /*:: declare var invariant; */ /** @@ -74,27 +76,48 @@ class ExpressionScope { } } +type ArrowHeadParsingParameterInitializerErrorClass = + | typeof Errors.AwaitExpressionFormalParameter + | typeof Errors.YieldInParameter + +type ArrowHeadParsingDeclarationErrorClass = + | ArrowHeadParsingParameterInitializerErrorClass + | typeof Errors.InvalidParenthesizedAssignment + | typeof Errors.AwaitBindingIdentifier; + class ArrowHeadParsingScope extends ExpressionScope { - errors: Map = new Map(); + declarationErrors: Map< + ArrowHeadParsingDeclarationErrorClass, + [Class>, Position] + > = new Map(); constructor(type: 1 | 2) { super(type); } - recordDeclarationError(message: ErrorTemplate, loc: Position) { - this.errors.set(loc.index, { message, loc }); + recordDeclarationError( + ParsingError: T, + { at }: { at: Position } + ) { + const index = at.index; + + this.declarationErrors.set(index, [ParsingError, at]); } - clearDeclarationError(loc: Position) { - this.errors.delete(loc.index); + clearDeclarationError(index: number) { + this.declarationErrors.delete(index); } - iterateErrors(iterator: (data: ErrorData) => void) { - this.errors.forEach(iterator); + iterateErrors( + iterator: ([T, Position]) => void + ) { + this.declarationErrors.forEach(iterator); } } + export default class ExpressionScopeHandler { + parser: Tokenizer; stack: Array = [new ExpressionScope()]; - declare raise: raiseFunction; - constructor(raise: raiseFunction) { - this.raise = raise; + + constructor(parser: Tokenizer) { + this.parser = parser; } enter(scope: ExpressionScope) { this.stack.push(scope); @@ -115,16 +138,17 @@ export default class ExpressionScopeHandler { * @memberof ExpressionScopeHandler */ recordParameterInitializerError( - loc: Position, - template: ErrorTemplate, + ParseErrorClass: ArrowHeadParsingParameterInitializerErrorClass, + { at: node } : { at: Node } ): void { + const origin = { at: node.loc.start }; const { stack } = this; let i = stack.length - 1; let scope: ExpressionScope = stack[i]; while (!scope.isCertainlyParameterDeclaration()) { if (scope.canBeArrowParameterDeclaration()) { /*:: invariant(scope instanceof ArrowHeadParsingScope) */ - scope.recordDeclarationError(template, loc); + scope.recordDeclarationError(ParseErrorClass, origin); } else { /*:: invariant(scope.type == kExpression) */ // Type-Expression is the boundary where initializer error can populate to @@ -132,8 +156,7 @@ export default class ExpressionScopeHandler { } scope = stack[--i]; } - /* eslint-disable @babel/development-internal/dry-error-messages */ - this.raise(template, { at: loc }); + this.parser.raise(ParseErrorClass, origin); } /** @@ -157,17 +180,15 @@ export default class ExpressionScopeHandler { * @returns {void} * @memberof ExpressionScopeHandler */ - recordParenthesizedIdentifierError( - template: ErrorTemplate, - loc: Position, - ): void { + recordParenthesizedIdentifierError({ at: node } : { at: Node }): void { const { stack } = this; const scope: ExpressionScope = stack[stack.length - 1]; + const origin = { at: node.loc.start }; if (scope.isCertainlyParameterDeclaration()) { - this.raise(template, { at: loc }); + this.parser.raise(Errors.InvalidParenthesizedAssignment, origin); } else if (scope.canBeArrowParameterDeclaration()) { /*:: invariant(scope instanceof ArrowHeadParsingScope) */ - scope.recordDeclarationError(template, loc); + scope.recordDeclarationError(Errors.InvalidParenthesizedAssignment, origin); } else { return; } @@ -182,17 +203,14 @@ export default class ExpressionScopeHandler { * @param {ErrorTemplate} template * @memberof ExpressionScopeHandler */ - recordAsyncArrowParametersError( - template: ErrorTemplate, - loc: Position, - ): void { + recordAsyncArrowParametersError({ at }: { at: Position }): void { const { stack } = this; let i = stack.length - 1; let scope: ExpressionScope = stack[i]; while (scope.canBeArrowParameterDeclaration()) { if (scope.type === kMaybeAsyncArrowParameterDeclaration) { /*:: invariant(scope instanceof ArrowHeadParsingScope) */ - scope.recordDeclarationError(template, loc); + scope.recordDeclarationError(Errors.AwaitBindingIdentifier, { at }); } scope = stack[--i]; } @@ -203,15 +221,14 @@ export default class ExpressionScopeHandler { const currentScope = stack[stack.length - 1]; if (!currentScope.canBeArrowParameterDeclaration()) return; /*:: invariant(currentScope instanceof ArrowHeadParsingScope) */ - currentScope.iterateErrors(({ message, loc }) => { - /* eslint-disable @babel/development-internal/dry-error-messages */ - this.raise(message, { at: loc }); + currentScope.iterateErrors(([ParseErrorClass, loc]) => { + this.parser.raise(ParseErrorClass, { at: loc }); // iterate from parent scope let i = stack.length - 2; let scope = stack[i]; while (scope.canBeArrowParameterDeclaration()) { /*:: invariant(scope instanceof ArrowHeadParsingScope) */ - scope.clearDeclarationError(loc); + scope.clearDeclarationError(loc.index); scope = stack[--i]; } }); diff --git a/packages/babel-parser/src/util/scope.js b/packages/babel-parser/src/util/scope.js index 573fb50228b2..5c88a2bfa87e 100644 --- a/packages/babel-parser/src/util/scope.js +++ b/packages/babel-parser/src/util/scope.js @@ -18,7 +18,8 @@ import { } from "./scopeflags"; import { Position } from "./location"; import * as N from "../types"; -import { Errors, type raiseFunction } from "../parser/error"; +import { Errors } from "../parse-error"; +import Tokenizer from "../tokenizer"; // Start an AST node, attaching a start offset. export class Scope { @@ -38,13 +39,14 @@ export class Scope { // The functions in this module keep track of declared variables in the // current scope in order to detect duplicate variable names. export default class ScopeHandler { + + parser: Tokenizer; scopeStack: Array = []; - declare raise: raiseFunction; - declare inModule: boolean; + inModule: boolean; undefinedExports: Map = new Map(); - constructor(raise: raiseFunction, inModule: boolean) { - this.raise = raise; + constructor(parser: Tokenizer, inModule: boolean) { + this.parser = parser; this.inModule = inModule; } @@ -103,7 +105,7 @@ export default class ScopeHandler { treatFunctionsAsVarInScope(scope: IScope): boolean { return !!( scope.flags & SCOPE_FUNCTION || - (!this.inModule && scope.flags & SCOPE_PROGRAM) + (!this.parser.inModule && scope.flags & SCOPE_PROGRAM) ); } @@ -131,13 +133,13 @@ export default class ScopeHandler { if (scope.flags & SCOPE_VAR) break; } } - if (this.inModule && scope.flags & SCOPE_PROGRAM) { + if (this.parser.inModule && scope.flags & SCOPE_PROGRAM) { this.undefinedExports.delete(name); } } maybeExportDefined(scope: IScope, name: string) { - if (this.inModule && scope.flags & SCOPE_PROGRAM) { + if (this.parser.inModule && scope.flags & SCOPE_PROGRAM) { this.undefinedExports.delete(name); } } @@ -149,7 +151,7 @@ export default class ScopeHandler { loc: Position, ) { if (this.isRedeclaredInScope(scope, name, bindingType)) { - this.raise(Errors.VarRedeclaration, { at: loc }, name); + this.parser.raise(Errors.VarRedeclaration, { at: loc, name }); } } From ee2037fe127cc3e0ba41bd7835186b3a88d1b647 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 9 Feb 2022 20:12:36 -0800 Subject: [PATCH 004/104] Add more. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 20 +++-- .../babel-parser/src/parse-error/module.js | 7 ++ .../babel-parser/src/parser/expression.js | 6 +- packages/babel-parser/src/parser/statement.js | 4 +- .../babel-parser/src/plugins/flow/index.js | 86 ++++++++----------- .../babel-parser/src/plugins/jsx/index.js | 14 +-- .../babel-parser/src/plugins/placeholders.js | 2 +- .../src/plugins/typescript/index.js | 5 +- packages/babel-parser/src/tokenizer/index.js | 2 +- packages/babel-parser/src/tokenizer/state.js | 2 +- .../babel-parser/src/util/expression-scope.js | 4 +- 11 files changed, 76 insertions(+), 76 deletions(-) create mode 100644 packages/babel-parser/src/parse-error/module.js diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index f88a6a9c7774..48f367df541a 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -24,10 +24,10 @@ export class ParseError extends SyntaxError { loc: Position; pos: number; - constructor(properties: {| + constructor(properties: { ...ErrorProperties, loc: Position, - |}): ParseError { + }): ParseError { super(); return ObjectAssign(this, { @@ -59,25 +59,31 @@ function fromToMessage(toMessage: ToMessage): Class> { } export function toParseErrorClasses( - toClasses: (typeof toReasonCodelessParseErrorClass) => T + toClasses: (typeof toReasonCodelessParseErrorClass) => T, + { syntaxPlugin, code } : Object = {} ): T { // $FlowIgnore return Object.fromEntries( Object.entries(toClasses(toReasonCodelessParseErrorClass)).map( ([reasonCode, ParseErrorClass]) => // $FlowIgnore - Object.assign(ParseErrorClass, { reasonCode }) + Object.assign(ParseErrorClass, + { reasonCode }, + !!syntaxPlugin && { syntaxPlugin }, + !!code && { code }) ) ); } -type Origin = {| at: Position |} | {| at: Node |}; -export type RaiseProperties = {| - ...ErrorProperties, +type Origin = {| at: Position | Node |}; +export type RaiseProperties = {| + ...$Exact, ...Origin, |}; +export { default as ModuleErrors } from "./parse-error/module"; + import StandardErrors from "./parse-error/standard"; import StrictErrors from "./parse-error/strict-mode"; diff --git a/packages/babel-parser/src/parse-error/module.js b/packages/babel-parser/src/parse-error/module.js new file mode 100644 index 000000000000..823f409ac073 --- /dev/null +++ b/packages/babel-parser/src/parse-error/module.js @@ -0,0 +1,7 @@ +import { ErrorCodes, toParseErrorClasses } from "../parse-error"; + +export default toParseErrorClasses(_ => { + ImportMetaOutsideModule: _(`import.meta may appear only with 'sourceType: "module"'`), + ImportOutsideModule: _(`'import' and 'export' may appear only with 'sourceType: "module"'`), + { code: ErrorCodes.SourceTypeModuleError } +}); diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 808a36256333..013609c64245 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -67,7 +67,7 @@ import { newAsyncArrowScope, newExpressionScope, } from "../util/expression-scope"; -import { Errors, ParseError, SourceTypeModuleErrors } from "../parse-error"; +import { Errors, ModuleErrors, ParseError } from "../parse-error"; import { setInnerComments } from "./comments"; import { cloneIdentifier } from "./node"; @@ -1601,9 +1601,7 @@ export default class ExpressionParser extends LValParser { if (this.isContextual(tt._meta)) { if (!this.inModule) { - this.raise(SourceTypeModuleErrors.ImportMetaOutsideModule, { - at: id, - }); + this.raise(ModuleErrors.ImportMetaOutsideModule, { at: id }); } this.sawUnambiguousESM = true; } diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 4ff3861d47a5..290c5e350483 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -10,7 +10,7 @@ import { getExportedToken, } from "../tokenizer/types"; import ExpressionParser from "./expression"; -import { Errors, SourceTypeModuleErrors } from "../parse-error"; +import { Errors, ModuleErrors } from "../parse-error"; import { isIdentifierChar, isIdentifierStart } from "../util/identifier"; import { lineBreak } from "../util/whitespace"; import * as charCodes from "charcodes"; @@ -483,7 +483,7 @@ export default class StatementParser extends ExpressionParser { assertModuleNodeAllowed(node: N.Node): void { if (!this.options.allowImportExportEverywhere && !this.inModule) { - this.raise(SourceTypeModuleErrors.ImportOutsideModule, { at: node }); + this.raise(ModuleErrors.ImportOutsideModule, { at: node }); } } diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index a3d67d3f1a97..b1e292b8f20d 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -97,18 +97,19 @@ const FlowErrors = toParseErrorClasses( ({ enumName }) => `Supplied enum type is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.` ), - 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`.", + EnumInvalidMemberInitializerPrimaryType: _<{ enumName: string, memberName: string, + explicitType: string }>(({ enumName, memberName, explicitType }) => + `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of \`${memberName}\` needs to be a ${explicitType} literal.`), + EnumInvalidMemberInitializerSymbolType: _<{ enumName: string, memberName: string, explicitType: string }>(({ enumName, memberName }) => + `Symbol enum members cannot be initialized. Use \`${memberName},\` in enum \`${enumName}\`.`), + EnumInvalidMemberInitializerUnknownType: _<{ enumName: string, memberName: string, explicitType: null }>(({ enumName, memberName }) => + `The enum member initializer for \`${memberName}\` needs to be a literal (either a boolean, number, or string) in enum \`${enumName}\`.`), + EnumInvalidMemberName: _<{ enumName: string, memberName: string, suggestion: string }>(({ enumName, memberName, suggestion }) => + `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${enumName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`), + EnumNumberMemberNotInitialized: _<{ enumName: string, memberName: string }>(({ enumName, memberName }) => + `Number enum members need to be initialized, e.g. \`${memberName} = 1\` in enum \`${enumName}\`.`), + EnumStringMemberInconsistentlyInitailized: _<{ enumName: string }>(({ enumName }) => + `String enum members need to consistently either all use initializers, or use no initializers, in enum \`${enumName}\`.`), GetterMayNotHaveThisParam: _("A getter cannot have a `this` parameter."), 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." @@ -190,7 +191,7 @@ const FlowErrors = toParseErrorClasses( ), UnterminatedFlowComment: _("Unterminated flow-comment."), }), - /* syntaxPlugin */ "flow" + { syntaxPlugin: "flow" } ); /* eslint-disable sort-keys */ @@ -3303,21 +3304,6 @@ export default (superClass: Class): Class => }); } - flowEnumErrorInvalidExplicitType( - loc: Position, - { - enumName, - suppliedType, - }: { enumName: string, suppliedType: null | string }, - ) { - return this.raise( - suppliedType === null - ? FlowErrors.EnumInvalidExplicitTypeUnknownSupplied - : FlowErrors.EnumInvalidExplicitType, - { at: loc, enumName, suppliedType }, - ); - } - flowEnumErrorInvalidMemberInitializer( loc: Position, { enumName, explicitType, memberName }: EnumContext, @@ -3557,32 +3543,32 @@ export default (superClass: Class): Class => }: { enumName: string, }): EnumExplicitType { - if (this.eatContextual(tt._of)) { - if (!tokenIsIdentifier(this.state.type)) { - throw this.flowEnumErrorInvalidExplicitType(this.state.startLoc, { - enumName, - suppliedType: null, - }); - } + if (!this.eatContextual(tt._of)) return null; - const { value } = this.state; - this.next(); + if (!tokenIsIdentifier(this.state.type)) { + throw this.raise(FlowErrors.EnumInvalidExplicitTypeUnknownSupplied, { + at: this.state.startLoc, + enumName, + }); + } - if ( - value !== "boolean" && - value !== "number" && - value !== "string" && - value !== "symbol" - ) { - this.flowEnumErrorInvalidExplicitType(this.state.startLoc, { - enumName, - suppliedType: value, - }); - } + const { value } = this.state; + this.next(); - return value; + if ( + value !== "boolean" && + value !== "number" && + value !== "string" && + value !== "symbol" + ) { + this.raise(FlowErrors.EnumInvalidExplicitType, { + at: this.state.startLoc, + enumName, + invalidEnumType: value + }); } - return null; + + return value; } flowEnumBody(node: N.Node, id: N.Node): N.Node { diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index 34217d1989f7..f8ff5bf1afc1 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -48,7 +48,7 @@ const JsxErrors = toParseErrorClasses( `Unexpected token \`${found}\`. Did you mean \`${HTMLEntity}\` or \`{'${found}'}\`?` ), }), - /* syntaxPlugin */ "jsx" + { syntaxPlugin: "jsx" } ); @@ -323,7 +323,7 @@ export default (superClass: Class): Class => this.next(); node = this.jsxParseExpressionContainer(node, tc.j_oTag); if (node.expression.type === "JSXEmptyExpression") { - this.raise(JsxErrors.AttributeIsEmpty, { node }); + this.raise(JsxErrors.AttributeIsEmpty, { at: node }); } return node; @@ -378,7 +378,7 @@ export default (superClass: Class): Class => !expression.extra?.parenthesized ) { this.raise(JsxErrors.UnexpectedSequenceExpression, { - node: expression.expressions[1], + at: expression.expressions[1], }); } } @@ -509,12 +509,13 @@ export default (superClass: Class): Class => closingElement !== null ) { this.raise(JsxErrors.MissingClosingTagFragment, { - node: closingElement, + at: closingElement, }); } else if (!isFragment(openingElement) && isFragment(closingElement)) { this.raise(JsxErrors.MissingClosingTagElement, { + // $FlowIgnore at: closingElement, - openingElement: getQualifiedJSXName(openingElement.name), + openingTagName: getQualifiedJSXName(openingElement.name), }); } else if (!isFragment(openingElement) && !isFragment(closingElement)) { if ( @@ -523,8 +524,9 @@ export default (superClass: Class): Class => getQualifiedJSXName(openingElement.name) ) { this.raise(JsxErrors.MissingClosingTagElement, { + // $FlowIgnore at: closingElement, - openingElement: getQualifiedJSXName(openingElement.name), + openingTagName: getQualifiedJSXName(openingElement.name), }); } } diff --git a/packages/babel-parser/src/plugins/placeholders.js b/packages/babel-parser/src/plugins/placeholders.js index 5e4169913462..74c044817d73 100644 --- a/packages/babel-parser/src/plugins/placeholders.js +++ b/packages/babel-parser/src/plugins/placeholders.js @@ -52,7 +52,7 @@ const PlaceholderErrors = toParseErrorClasses( ClassNameIsRequired: _("A class name is required."), UnexpectedSpace: _("Unexpected space in placeholder.") }), - /* syntaxPlugin */ "placeholders", + { syntaxPlugin: "placeholders" } ); /* eslint-disable sort-keys */ diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 82b5cf67b642..1cf7c8cc039e 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -90,7 +90,8 @@ const TSErrors = toParseErrorClasses( ConstructorHasTypeParameters: _( "Type parameters cannot appear on a constructor declaration." ), - DeclareAccessor: _<{ accessorKind: "getter" | "setter" }>(({ accessorKind }) => `'declare' is not allowed in ${accessorKind}s.`), + // kind? + DeclareAccessor: _<{ accessorKind: "get" | "set" }>(({ accessorKind }) => `'declare' is not allowed in ${accessorKind}ters.`), DeclareClassFieldHasInitializer: _( "Initializers are not allowed in ambient contexts." ), @@ -214,7 +215,7 @@ const TSErrors = toParseErrorClasses( `Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got ${unsupportedParameterType}.` ), }), - /* syntaxPlugin */ "typescript" + { syntaxPlugin: "typescript" } ); /* eslint-disable sort-keys */ diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index d82ff7ba6071..47a5ba25c7f5 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -16,7 +16,7 @@ import { type TokenType, } from "./types"; import { type TokContext } from "./context"; -import { Errors, ParseError, RaiseProperties } from "../parse-error"; +import { Errors, ParseError, type RaiseProperties } from "../parse-error"; import { lineBreakG, isNewLine, diff --git a/packages/babel-parser/src/tokenizer/state.js b/packages/babel-parser/src/tokenizer/state.js index 6e7260464781..3e3b7010814b 100644 --- a/packages/babel-parser/src/tokenizer/state.js +++ b/packages/babel-parser/src/tokenizer/state.js @@ -145,7 +145,7 @@ export default class State { // todo(JLHwung): set strictErrors to null and avoid recording string errors // after a non-directive is parsed - strictErrors: Map = new Map(); + strictErrors: Map = new Map(); // Tokens length in token store tokensLength: number = 0; diff --git a/packages/babel-parser/src/util/expression-scope.js b/packages/babel-parser/src/util/expression-scope.js index 5a81e0fd1b61..f4662eebbb97 100644 --- a/packages/babel-parser/src/util/expression-scope.js +++ b/packages/babel-parser/src/util/expression-scope.js @@ -87,8 +87,8 @@ type ArrowHeadParsingDeclarationErrorClass = class ArrowHeadParsingScope extends ExpressionScope { declarationErrors: Map< - ArrowHeadParsingDeclarationErrorClass, - [Class>, Position] + number, + [ArrowHeadParsingDeclarationErrorClass, Position] > = new Map(); constructor(type: 1 | 2) { super(type); From 6a339a1eb45321f408d9be7f2bfa93b5a5653749 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Thu, 10 Feb 2022 12:13:42 -0800 Subject: [PATCH 005/104] Fix. Reviewed by @tolmasky. --- packages/babel-parser/src/tokenizer/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 47a5ba25c7f5..5fe780182393 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -1751,8 +1751,8 @@ export default class Tokenizer extends CommentsParser { raiseProperties: RaiseProperties ) : ParseError { const { at, ...rest } = raiseProperties; - const loc = at instanceof Position ? at : at.loc; - const error = new ParseErrorClass({ ...rest, loc: at }); + const loc = at instanceof Position ? at : at.loc.start; + const error = new ParseErrorClass({ ...rest, loc }); if (!this.options.errorRecovery) throw error; if (!this.isLookahead) this.state.errors.push(error); @@ -1778,7 +1778,7 @@ export default class Tokenizer extends CommentsParser { raiseProperties: RaiseProperties ): ParseError | empty { const { at, ...rest } = raiseProperties; - const loc = at instanceof Position ? at : at.loc; + const loc = at instanceof Position ? at : at.loc.start; const pos = loc.index; const errors = this.state.errors; From bb7d02bcff3e9f5116e05c9843ada34924451b87 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Thu, 10 Feb 2022 12:41:22 -0800 Subject: [PATCH 006/104] Just switch everything to exact types. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 9 ++--- .../babel-parser/src/plugins/flow/index.js | 37 ++++++++++--------- .../babel-parser/src/plugins/jsx/index.js | 4 +- .../src/plugins/typescript/index.js | 24 ++++++------ 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 48f367df541a..53ebdf0e7ede 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -1,7 +1,7 @@ // @flow import { Position } from "./util/location"; -import type { Node } from "./types"; +import type { NodeBase } from "./types"; const { assign: ObjectAssign } = Object; @@ -76,10 +76,9 @@ export function toParseErrorClasses( } -type Origin = {| at: Position | Node |}; -export type RaiseProperties = {| - ...$Exact, - ...Origin, +export type RaiseProperties = {| + ...ErrorProperties, + at: Position | NodeBase, |}; export { default as ModuleErrors } from "./parse-error/module"; diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index b1e292b8f20d..067420b6a1fa 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -62,7 +62,7 @@ const FlowErrors = toParseErrorClasses( 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: _<{ reservedType: string }>( + AssignReservedType: _<{| reservedType: string |}>( ({ reservedType }) => `Cannot overwrite reserved type ${reservedType}.` ), DeclareClassElement: _( @@ -74,41 +74,41 @@ const FlowErrors = toParseErrorClasses( DuplicateDeclareModuleExports: _( "Duplicate `declare module.exports` statement." ), - EnumBooleanMemberNotInitialized: _<{ + EnumBooleanMemberNotInitialized: _<{| memberName: string, enumName: string, - }>( + |}>( ({ memberName, enumName }) => `Boolean enum members need to be initialized. Use either \`${memberName} = true,\` or \`${memberName} = false,\` in enum \`${enumName}\`.` ), - EnumDuplicateMemberName: _<{ memberName: string, enumName: string }>( + EnumDuplicateMemberName: _<{| memberName: string, enumName: string |}>( ({ memberName, enumName }) => `Enum member names need to be unique, but the name \`${memberName}\` has already been used before in enum \`${enumName}\`.` ), - EnumInconsistentMemberValues: _<{ enumName: string }>( + EnumInconsistentMemberValues: _<{| enumName: string |}>( ({ enumName }) => `Enum \`${enumName}\` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers.` ), - EnumInvalidExplicitType: _<{ invalidEnumType: string, enumName: string }>( + EnumInvalidExplicitType: _<{| invalidEnumType: string, enumName: string |}>( ({ invalidEnumType, enumName }) => `Enum type \`${invalidEnumType}\` is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.` ), - EnumInvalidExplicitTypeUnknownSupplied: _<{ enumName: string }>( + EnumInvalidExplicitTypeUnknownSupplied: _<{| enumName: string |}>( ({ enumName }) => `Supplied enum type is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.` ), - EnumInvalidMemberInitializerPrimaryType: _<{ enumName: string, memberName: string, - explicitType: string }>(({ enumName, memberName, explicitType }) => + EnumInvalidMemberInitializerPrimaryType: _<{| enumName: string, memberName: string, + explicitType: string |}>(({ enumName, memberName, explicitType }) => `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of \`${memberName}\` needs to be a ${explicitType} literal.`), - EnumInvalidMemberInitializerSymbolType: _<{ enumName: string, memberName: string, explicitType: string }>(({ enumName, memberName }) => + EnumInvalidMemberInitializerSymbolType: _<{| enumName: string, memberName: string, explicitType: string |}>(({ enumName, memberName }) => `Symbol enum members cannot be initialized. Use \`${memberName},\` in enum \`${enumName}\`.`), - EnumInvalidMemberInitializerUnknownType: _<{ enumName: string, memberName: string, explicitType: null }>(({ enumName, memberName }) => + EnumInvalidMemberInitializerUnknownType: _<{| enumName: string, memberName: string, explicitType: string |}>(({ enumName, memberName }) => `The enum member initializer for \`${memberName}\` needs to be a literal (either a boolean, number, or string) in enum \`${enumName}\`.`), - EnumInvalidMemberName: _<{ enumName: string, memberName: string, suggestion: string }>(({ enumName, memberName, suggestion }) => + EnumInvalidMemberName: _<{| enumName: string, memberName: string, suggestion: string |}>(({ enumName, memberName, suggestion }) => `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${enumName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`), - EnumNumberMemberNotInitialized: _<{ enumName: string, memberName: string }>(({ enumName, memberName }) => + EnumNumberMemberNotInitialized: _<{| enumName: string, memberName: string |}>(({ enumName, memberName }) => `Number enum members need to be initialized, e.g. \`${memberName} = 1\` in enum \`${enumName}\`.`), - EnumStringMemberInconsistentlyInitailized: _<{ enumName: string }>(({ enumName }) => + EnumStringMemberInconsistentlyInitailized: _<{| enumName: string |}>(({ enumName }) => `String enum members need to consistently either all use initializers, or use no initializers, in enum \`${enumName}\`.`), GetterMayNotHaveThisParam: _("A getter cannot have a `this` parameter."), ImportTypeShorthandOnlyInPureImport: _( @@ -158,7 +158,7 @@ const FlowErrors = toParseErrorClasses( UnexpectedExplicitInexactInObject: _( "Explicit inexact syntax must appear at the end of an inexact object." ), - UnexpectedReservedType: _<{ reservedType: string }>( + UnexpectedReservedType: _<{| reservedType: string |}>( ({ reservedType }) => `Unexpected reserved type ${reservedType}.` ), UnexpectedReservedUnderscore: _( @@ -179,10 +179,10 @@ const FlowErrors = toParseErrorClasses( UnexpectedTypeParameterBeforeAsyncArrowFunction: _( "Type parameters must come after the async keyword, e.g. instead of ` async () => {}`, use `async () => {}`." ), - UnsupportedDeclareExportKind: _<{ + UnsupportedDeclareExportKind: _<{| unsupported: string, suggestion: string, - }>( + |}>( ({ unsupported, suggestion }) => `\`declare export ${unsupported}\` is not supported. Use \`${suggestion}\` instead.` ), @@ -3319,7 +3319,8 @@ export default (superClass: Class): Class => { at: loc, enumName, memberName, - explicitType, + // FIXME: ? + explicitType: "hi"/*explicitType || "unknown"*/, }); } diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index f8ff5bf1afc1..1921f55d5671 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -25,7 +25,7 @@ const JsxErrors = toParseErrorClasses( AttributeIsEmpty: _( "JSX attributes must only be assigned a non-empty expression." ), - MissingClosingTagElement: _<{ openingTagName: string }>( + MissingClosingTagElement: _<{| openingTagName: string |}>( ({ openingTagName }) => `Expected corresponding JSX closing tag for <${openingTagName}>.` ), @@ -43,7 +43,7 @@ const JsxErrors = toParseErrorClasses( "Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...?" ), // FIXME: Unify with Errors.UnexpectedToken - UnexpectedToken: _<{ found: string, HTMLEntity: string }>( + UnexpectedToken: _<{| found: string, HTMLEntity: string |}>( ({ found, HTMLEntity }) => `Unexpected token \`${found}\`. Did you mean \`${HTMLEntity}\` or \`{'${found}'}\`?` ), diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 1cf7c8cc039e..418e2df5ab33 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -69,10 +69,10 @@ type ParsingContext = /* eslint sort-keys: "error" */ const TSErrors = toParseErrorClasses( _ => ({ - AbstractMethodHasImplementation: _<{ methodName: string }>(({ methodName }) => + AbstractMethodHasImplementation: _<{| methodName: string |}>(({ methodName }) => `Method '${methodName}' cannot have an implementation because it is marked abstract.` ), - AbstractPropertyHasInitializer: _<{ propertyName: string }>(({ propertyName }) => + AbstractPropertyHasInitializer: _<{| propertyName: string |}>(({ propertyName }) => `Property '${propertyName}' cannot have an initializer because it is marked abstract.` ), AccesorCannotDeclareThisParameter: _( @@ -91,29 +91,29 @@ const TSErrors = toParseErrorClasses( "Type parameters cannot appear on a constructor declaration." ), // kind? - DeclareAccessor: _<{ accessorKind: "get" | "set" }>(({ accessorKind }) => `'declare' is not allowed in ${accessorKind}ters.`), + DeclareAccessor: _<{| accessorKind: "get" | "set" |}>(({ accessorKind }) => `'declare' is not allowed in ${accessorKind}ters.`), DeclareClassFieldHasInitializer: _( "Initializers are not allowed in ambient contexts." ), DeclareFunctionHasImplementation: _( "An implementation cannot be declared in ambient contexts." ), - DuplicateAccessibilityModifier: _<{ modifier: N.Accessibility }>(({ modifier }) => "Accessibility modifier ${modifier} already seen."), - DuplicateModifier: _<{ modifier: TsModifier }>(({ modifier }) => `Duplicate modifier: '${modifier}'.`), - EmptyHeritageClauseType: _<{ descriptor: string }>(({ descriptor }) => `'${descriptor}' list cannot be empty.`), + DuplicateAccessibilityModifier: _<{| modifier: N.Accessibility |}>(({ modifier }) => "Accessibility modifier ${modifier} already seen."), + DuplicateModifier: _<{| modifier: TsModifier |}>(({ modifier }) => `Duplicate modifier: '${modifier}'.`), + EmptyHeritageClauseType: _<{| descriptor: string |}>(({ descriptor }) => `'${descriptor}' list cannot be empty.`), EmptyTypeArguments: _("Type argument list cannot be empty."), EmptyTypeParameters: _("Type parameter list cannot be empty."), ExpectedAmbientAfterExportDeclare: _( "'export declare' must be followed by an ambient declaration." ), ImportAliasHasImportType: _("An import alias can not use 'import type'."), - IncompatibleModifiers: _<{ modifiers: [TsModifier, TsModifier] }>(({ modifiers }) => + IncompatibleModifiers: _<{| modifiers: [TsModifier, TsModifier] |}>(({ modifiers }) => `'${modifiers[0]}' modifier cannot be used with '${modifiers[1]}' modifier.` ), IndexSignatureHasAbstract: _( "Index signatures cannot have the 'abstract' modifier." ), - IndexSignatureHasAccessibility: _<{ modifier: N.Accessibility }>(({ modifier }) => + IndexSignatureHasAccessibility: _<{| modifier: N.Accessibility |}>(({ modifier }) => `Index signatures cannot have an accessibility modifier ('${modifier}').` ), IndexSignatureHasDeclare: _( @@ -125,10 +125,10 @@ const TSErrors = toParseErrorClasses( IndexSignatureHasStatic: _( "Index signatures cannot have the 'static' modifier." ), - InvalidModifierOnTypeMember: _<{ modifier: TsModifier }>(({ modifier }) => + InvalidModifierOnTypeMember: _<{| modifier: TsModifier |}>(({ modifier }) => `'${modifier}' modifier cannot appear on a type member.` ), - InvalidModifiersOrder: _<{ orderedModifiers: [TsModifier, TsModifier] }>(({ orderedModifiers }) => `'${orderedModifiers[0]}' modifier must precede '${orderedModifiers[1]}' modifier.`), + InvalidModifiersOrder: _<{| orderedModifiers: [TsModifier, TsModifier] |}>(({ orderedModifiers }) => `'${orderedModifiers[0]}' modifier must precede '${orderedModifiers[1]}' modifier.`), InvalidTupleMemberLabel: _( "Tuple members must be labeled with a simple identifier." ), @@ -156,7 +156,7 @@ const TSErrors = toParseErrorClasses( PrivateElementHasAbstract: _( "Private elements cannot have the 'abstract' modifier." ), - PrivateElementHasAccessibility: _<{ modifier: N.Accessibility }>(({ modifier }) => + PrivateElementHasAccessibility: _<{| modifier: N.Accessibility |}>(({ modifier }) => `Private elements cannot have an accessibility modifier ('${modifier}').` ), ReadonlyForMethodSignature: _( @@ -211,7 +211,7 @@ const TSErrors = toParseErrorClasses( UnsupportedParameterPropertyKind: _( "A parameter property may not be declared using a binding pattern." ), - UnsupportedSignatureParameterKind: _<{ unsupportedParameterType: string }>(({ unsupportedParameterType }) => + UnsupportedSignatureParameterKind: _<{| unsupportedParameterType: string |}>(({ unsupportedParameterType }) => `Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got ${unsupportedParameterType}.` ), }), From 50e259ab87b9abee61b489970950f45d6477522c Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Thu, 10 Feb 2022 12:52:57 -0800 Subject: [PATCH 007/104] Style fixes. Reviewed by @tolmasky. --- .../babel-parser/src/parse-error/standard.js | 220 +++++++++--------- .../babel-parser/src/parser/expression.js | 19 +- packages/babel-parser/src/parser/statement.js | 17 +- packages/babel-parser/src/parser/util.js | 2 +- .../babel-parser/src/plugins/flow/index.js | 152 +++++++----- .../babel-parser/src/plugins/jsx/index.js | 20 +- .../babel-parser/src/plugins/placeholders.js | 6 +- .../src/plugins/typescript/index.js | 158 +++++++------ packages/babel-parser/src/tokenizer/index.js | 31 +-- packages/babel-parser/src/tokenizer/state.js | 4 +- packages/babel-parser/src/util/class-scope.js | 2 +- .../babel-parser/src/util/expression-scope.js | 18 +- packages/babel-parser/src/util/scope.js | 1 - 13 files changed, 355 insertions(+), 295 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index 94a43f70de66..2332456eb97b 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -5,217 +5,220 @@ import { toParseErrorClasses } from "../parse-error"; import { type TokenType, tokenLabelName } from "../tokenizer/types"; export default toParseErrorClasses(_ => ({ - - AccessorIsGenerator: _<{ accessor: string }>(({ accessor }) => `A ${accessor} cannot be a generator`), + AccessorIsGenerator: _<{ accessor: string }>( + ({ accessor }) => `A ${accessor} cannot be a generator`, + ), ArgumentsInClass: _( - "'arguments' is only allowed in functions and class methods." + "'arguments' is only allowed in functions and class methods.", ), AsyncFunctionInSingleStatementContext: _( - "Async functions can only be declared at the top level or inside a block." + "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." + "Can not use 'await' as identifier inside an async function.", ), AwaitBindingIdentifierInStaticBlock: _( - "Can not use 'await' as identifier inside a static block." + "Can not use 'await' as identifier inside a static block.", ), AwaitExpressionFormalParameter: _( - "'await' is not allowed in async function parameters." + "'await' is not allowed in async function parameters.", ), AwaitNotInAsyncContext: _( - "'await' is only allowed within async functions and at the top levels of modules." + "'await' is only allowed within async functions and at the top levels of modules.", ), AwaitNotInAsyncFunction: _("'await' is only allowed within async functions."), BadGetterArity: _("A 'get' accesor must not have any formal parameters."), BadSetterArity: _("A 'set' accesor must have exactly one formal parameter."), BadSetterRestParameter: _( - "A 'set' accesor function argument must not be a rest parameter." + "A 'set' accesor 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'." + "Classes may not have a private field named '#constructor'.", ), ConstructorIsAccessor: _("Class constructor may not be an accessor."), ConstructorIsAsync: _("Constructor can't be an async function."), ConstructorIsGenerator: _("Constructor can't be a generator."), DeclarationMissingInitializer: _<{ declaration: string }>( - ({ declaration }) => `${declaration}' require an initialization value.` + ({ declaration }) => `${declaration}' 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." + "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 { ... }'?" + "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." + "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."), DecoratorStaticBlock: _("Decorators can't be used with a static block."), DeletePrivateField: _("Deleting a private field is not allowed."), DestructureNamedImport: _( - "ES2015 named imports do not destructure. Use another statement for destructuring after the import." + "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: _<{| export: string |}>( ({ export: name }) => - `\`${name}\` has already been exported. Exported identifiers must be unique.` + `\`${name}\` 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."), - ExportBindingIsString: _<{ localBinding: string, exportBinding: string }>(({ localBinding, exportBinding }) => - "A string literal cannot be used as an exported binding without `from`.\n- Did you mean `export { '${localBinding}' as '{exportBinding}' } from 'some-module'`?" + ExportBindingIsString: _<{ localBinding: string, exportBinding: string }>( + ({ localBinding, exportBinding }) => + "A string literal cannot be used as an exported binding without `from`.\n- Did you mean `export { '${localBinding}' as '{exportBinding}' } from 'some-module'`?", ), ExportDefaultFromAsIdentifier: _( - "'from' is not allowed as an identifier after 'export default'." + "'from' is not allowed as an identifier after 'export default'.", ), - ForInOfLoopInitializer: _<{ construct: "for-in" | "for-of" }>(({ construct }) => - `'${construct}' loop variable declaration may not have an initializer.` + ForInOfLoopInitializer: _<{ construct: "for-in" | "for-of" }>( + ({ construct }) => + `'${construct}' loop variable declaration may not have an initializer.`, ), ForOfAsync: _("The left-hand side of a for-of loop may not be 'async'."), ForOfLet: _("The left-hand side of a for-of loop may not start with 'let'."), GeneratorInSingleStatementContext: _( - "Generators can only be declared at the top level or inside a block." + "Generators can only be declared at the top level or inside a block.", ), - IllegalBreakContinue: _<{ construct: "break" | "continue" }>(({ construct }) => - `Unsyntactic ${construct}.` + IllegalBreakContinue: _<{ construct: "break" | "continue" }>( + ({ construct }) => `Unsyntactic ${construct}.`, ), IllegalLanguageModeDirective: _( - "Illegal 'use strict' directive in function with non-simple parameter list." + "Illegal 'use strict' directive in function with non-simple parameter list.", ), IllegalReturn: _("'return' outside of function."), - ImportBindingIsString: _<{ importBinding: string }>(({ importBinding }) => - `A string literal cannot be used as an imported binding.\n- Did you mean \`import { "${importBinding}" as foo }\`?` + ImportBindingIsString: _<{ importBinding: string }>( + ({ importBinding }) => + `A string literal cannot be used as an imported binding.\n- Did you mean \`import { "${importBinding}" as foo }\`?`, ), ImportCallArgumentTrailingComma: _( - "Trailing comma is disallowed inside import(...) arguments." + "Trailing comma is disallowed inside import(...) arguments.", ), ImportCallArity: _<{ required: 1 | 2 }>( ({ required }) => `\`import()\` requires exactly ${ required === 1 ? "one argument" : "one or two arguments" - }.` + }.`, ), ImportCallNotNewExpression: _("Cannot use new with import(...)."), ImportCallSpreadArgument: _("`...` is not allowed in `import()`."), IncompatibleRegExpUVFlags: _( - "The 'u' and 'v' regular expression flags cannot be enabled at the same time." + "The 'u' and 'v' regular expression flags cannot be enabled at the same time.", ), InvalidBigIntLiteral: _("Invalid BigIntLiteral."), InvalidCodePoint: _("Code point out of bounds."), InvalidCoverInitializedName: _("Invalid shorthand property initializer."), InvalidDecimal: _("Invalid decimal."), InvalidDigit: _<{ radix: number }>( - ({ radix }) => `Expected number in radix ${radix}.` + ({ radix }) => `Expected number in radix ${radix}.`, ), InvalidEscapeSequence: _("Bad character escape sequence."), InvalidEscapeSequenceTemplate: _("Invalid escape sequence in template."), InvalidEscapedReservedWord: _<{ keyword: string }>( - ({ keyword }) => `Escape sequence in keyword ${keyword}.` + ({ keyword }) => `Escape sequence in keyword ${keyword}.`, ), InvalidIdentifier: _<{ identifier: string }>( - ({ identifier }) => `Invalid identifier ${identifier}.` + ({ identifier }) => `Invalid identifier ${identifier}.`, ), InvalidLhs: _<{ contextDescription: string }>( ({ contextDescription }) => - `Invalid left-hand side in ${contextDescription}.` + `Invalid left-hand side in ${contextDescription}.`, ), InvalidLhsBinding: _<{ contextDescription: string }>( ({ contextDescription }) => - `Binding invalid left-hand side in ${contextDescription}.` + `Binding invalid left-hand side in ${contextDescription}.`, ), InvalidNumber: _("Invalid number."), InvalidOrMissingExponent: _( - "Floating-point numbers require a valid exponent after the 'e'." + "Floating-point numbers require a valid exponent after the 'e'.", ), InvalidOrUnexpectedToken: _<{ found: string }>( - ({ found }) => `Unexpected character '${found}'.` + ({ found }) => `Unexpected character '${found}'.`, ), InvalidParenthesizedAssignment: _( - "Invalid parenthesized assignment pattern." + "Invalid parenthesized assignment pattern.", ), InvalidPrivateFieldResolution: _<{ name: string }>( - ({ name }) => `Private name #${name} is not defined.` + ({ name }) => `Private name #${name} is not defined.`, ), InvalidPropertyBindingPattern: _("Binding member expression."), InvalidRecordProperty: _( - "Only properties and spread elements are allowed in record definitions." + "Only properties and spread elements are allowed in record definitions.", ), InvalidRestAssignmentPattern: _("Invalid rest operator's argument."), - LabelRedeclaration: _<{ label: string }>(({ label }) => - `Label '${label}' is already declared.` + LabelRedeclaration: _<{ label: string }>( + ({ label }) => `Label '${label}' is already declared.`, ), LetInLexicalBinding: _( - "'let' is not allowed to be used as a name in 'let' or 'const' declarations." + "'let' is not allowed to be used as a name in 'let' or 'const' declarations.", ), LineTerminatorBeforeArrow: _("No line break is allowed before '=>'."), MalformedRegExpFlags: _("Invalid regular expression flag."), MissingClassName: _("A class name is required."), MissingEqInAssignment: _( - "Only '=' operator can be used for specifying default value." + "Only '=' operator can be used for specifying default value.", ), MissingSemicolon: _("Missing semicolon."), MissingPlugin: _<{ missingPlugin: string }>( ({ missingPlugin }) => - `This experimental syntax requires enabling the parser plugin: "${missingPlugin}".` + `This experimental syntax requires enabling the parser plugin: "${missingPlugin}".`, ), // FIXME: Would be nice to make this "missingPlugins" instead. // Also), seems like we can drop the "(s)" from the message and just make it "s". MissingOneOfPlugins: _<{ missingPlugin: string[] }>( ({ missingPlugin }) => `This experimental syntax requires enabling one of the following parser plugin(s): ${missingPlugin - .map((name) => JSON.stringify(name)) - .join("), ")}.` + .map(name => JSON.stringify(name)) + .join("), ")}.`, ), MissingUnicodeEscape: _("Expecting Unicode escape sequence \\uXXXX."), MixingCoalesceWithLogical: _( - "Nullish coalescing operator(??) requires parens when mixing with logical operators." + "Nullish coalescing operator(??) requires parens when mixing with logical operators.", ), ModuleAttributeDifferentFromType: _( - "The only accepted module attribute is `type`." + "The only accepted module attribute is `type`.", ), ModuleAttributeInvalidValue: _( - "Only string literals are allowed as module attribute values." + "Only string literals are allowed as module attribute values.", ), ModuleAttributesWithDuplicateKeys: _<{ key: string }>( - ({ key }) => `Duplicate key "${key}" is not allowed in module attributes.` + ({ key }) => `Duplicate key "${key}" is not allowed in module attributes.`, ), - ModuleExportNameHasLoneSurrogate: - _ < - { surrogateCharCode: number }>(({ surrogateCharCode }) => + ModuleExportNameHasLoneSurrogate: _<{ surrogateCharCode: number }>( + ({ surrogateCharCode }) => `An export name cannot include a lone surrogate), found '\\u${surrogateCharCode.toString( - 16 - )}'.` - ), - ModuleExportUndefined: _<{ moduleExportName: string }>(({ moduleExportName }) => - `Export '${moduleExportName}' is not defined.` + 16, + )}'.`, + ), + ModuleExportUndefined: _<{ moduleExportName: string }>( + ({ moduleExportName }) => `Export '${moduleExportName}' 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." + "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." + "'await*' has been removed from the async functions proposal. Use Promise.all() instead.", ), OptionalChainingNoNew: _( - "Constructors in/after an Optional Chain are not allowed." + "Constructors in/after an Optional Chain are not allowed.", ), OptionalChainingNoTemplate: _( - "Tagged Template Literals are not allowed in optionalChain." + "Tagged Template Literals are not allowed in optionalChain.", ), OverrideOnConstructor: _( - "'override' modifier cannot appear on a constructor declaration." + "'override' modifier cannot appear on a constructor declaration.", ), ParamDupe: _("Argument name clash."), PatternHasAccessor: _("Object pattern can't contain getter or setter."), @@ -223,23 +226,24 @@ export default toParseErrorClasses(_ => ({ // This error is only used by the smart-mix proposal PipeBodyIsTighter: _<{ expressionDescription: string }>( ({ expressionDescription }) => - `Unexpected ${expressionDescription} after pipeline body; any ${expressionDescription} expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence.` + `Unexpected ${expressionDescription} after pipeline body; any ${expressionDescription} expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence.`, ), PipeTopicRequiresHackPipes: _( - 'Topic reference is used), but the pipelineOperator plugin was not passed a "proposal": _("hack" or "smart" option.' + 'Topic reference is used), but the pipelineOperator plugin was not passed a "proposal": _("hack" or "smart" option.', ), PipeTopicUnbound: _( - "Topic reference is unbound; it must be inside a pipe body." + "Topic reference is unbound; it must be inside a pipe body.", ), - PipeTopicUnconfiguredToken: _<{ token: string }>(({ token }) => - `Invalid topic token ${token}. In order to use ${token} as a topic reference), the pipelineOperator plugin must be configured with { "proposal": _("hack"), "topicToken": _("${token}" }.` + PipeTopicUnconfiguredToken: _<{ token: string }>( + ({ token }) => + `Invalid topic token ${token}. In order to use ${token} as a topic reference), the pipelineOperator plugin must be configured with { "proposal": _("hack"), "topicToken": _("${token}" }.`, ), PipeTopicUnused: _( - "Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once." + "Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once.", ), PipeUnparenthesizedBody: _<{ expressionDescription: string }>( ({ expressionDescription }) => - `Hack-style pipe body cannot be an unparenthesized ${expressionDescription} expression; please wrap it in parentheses.` + `Hack-style pipe body cannot be an unparenthesized ${expressionDescription} expression; please wrap it in parentheses.`, ), // Messages whose codes start with “Pipeline” or “PrimaryTopic” @@ -247,134 +251,132 @@ export default toParseErrorClasses(_ => ({ // with the deprecated smart-mix pipe operator proposal plugin. // They are subject to removal in a future major version. PipelineBodyNoArrow: _( - 'Unexpected arrow "=>" after pipeline body; arrow function in pipeline body must be parenthesized.' + 'Unexpected arrow "=>" after pipeline body; arrow function in pipeline body must be parenthesized.', ), PipelineBodySequenceExpression: _( - "Pipeline body may not be a comma-separated sequence expression." + "Pipeline body may not be a comma-separated sequence expression.", ), PipelineHeadSequenceExpression: _( - "Pipeline head should not be a comma-separated sequence expression." + "Pipeline head should not be a comma-separated sequence expression.", ), PipelineTopicUnused: _( - "Pipeline is in topic style but does not use topic reference." + "Pipeline is in topic style but does not use topic reference.", ), PrimaryTopicNotAllowed: _( - "Topic reference was used in a lexical context without topic binding." + "Topic reference was used in a lexical context without topic binding.", ), PrimaryTopicRequiresSmartPipeline: _( - 'Topic reference is used), but the pipelineOperator plugin was not passed a "proposal": _("hack" or "smart" option.' + 'Topic reference is used), but the pipelineOperator plugin was not passed a "proposal": _("hack" or "smart" option.', ), PrivateInExpectedIn: _<{ name: string }>( ({ name }) => - `Private names are only allowed in property accesses (\`obj.#${name}\`) or in \`in\` expressions (\`#${name} in obj\`).` + `Private names are only allowed in property accesses (\`obj.#${name}\`) or in \`in\` expressions (\`#${name} in obj\`).`, ), PrivateNameRedeclaration: _<{ name: string }>( - ({ name }) => `Duplicate private name #${name}.` + ({ name }) => `Duplicate private name #${name}.`, ), RecordExpressionBarIncorrectEndSyntaxType: _( - "Record expressions ending with '|}' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'." + "Record expressions ending with '|}' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'.", ), RecordExpressionBarIncorrectStartSyntaxType: _( - "Record expressions starting with '{|' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'." + "Record expressions starting with '{|' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'.", ), RecordExpressionHashIncorrectStartSyntaxType: _( - "Record expressions starting with '#{' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'hash'." + "Record expressions starting with '#{' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'hash'.", ), RecordNoProto: _("'__proto__' is not allowed in Record expressions."), 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." + "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."), 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?" + "`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."), TrailingDecorator: _("Decorators must be attached to a class element."), TupleExpressionBarIncorrectEndSyntaxType: _( - "Tuple expressions ending with '|]' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'." + "Tuple expressions ending with '|]' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'.", ), TupleExpressionBarIncorrectStartSyntaxType: _( - "Tuple expressions starting with '[|' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'." + "Tuple expressions starting with '[|' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'.", ), TupleExpressionHashIncorrectStartSyntaxType: _( - "Tuple expressions starting with '#[' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'hash'." + "Tuple expressions starting with '#[' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'hash'.", ), UnexpectedArgumentPlaceholder: _("Unexpected argument placeholder."), UnexpectedAwaitAfterPipelineBody: _( - 'Unexpected "await" after pipeline body; await must have parentheses in minimal proposal.' + '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." + "'import' and 'export' may only appear at the top level.", ), UnexpectedKeyword: _<{ keyword: string }>( - ({ keyword }) => `Unexpected keyword '${keyword}'.` + ({ keyword }) => `Unexpected keyword '${keyword}'.`, ), UnexpectedLeadingDecorator: _( - "Leading decorators must be attached to a class declaration." + "Leading decorators must be attached to a class declaration.", ), UnexpectedLexicalDeclaration: _( - "Lexical declaration cannot appear in a single-statement context." + "Lexical declaration cannot appear in a single-statement context.", ), UnexpectedNewTarget: _( - "`new.target` can only be used in functions or class properties." + "`new.target` can only be used in functions or class properties.", ), UnexpectedNumericSeparator: _( - "A numeric separator is only allowed between two digits." + "A numeric separator is only allowed between two digits.", ), UnexpectedPrivateField: _("Unexpected private name."), UnexpectedReservedWord: _<{ reservedWord: string }>( - ({ reservedWord }) => `Unexpected reserved word '${reservedWord}'.` + ({ reservedWord }) => `Unexpected reserved word '${reservedWord}'.`, ), UnexpectedSuper: _("'super' is only allowed in object methods and classes."), UnexpectedToken: _<{ loc: Position, expected?: string, }>(({ loc: { line, column }, expected }) => - !!expected - ? `Unexpected token), expected "${expected}"` - : "Unexpected token" + (expected ? `Unexpected token), expected "${expected}"` : "Unexpected token"), ), UnexpectedTokenUnaryExponentiation: _( - "Illegal expression. Wrap left hand side or entire exponentiation in parentheses." + "Illegal expression. Wrap left hand side or entire exponentiation in parentheses.", ), UnsupportedBind: _("Binding should be performed on object property."), UnsupportedDecoratorExport: _( - "A decorated export must export a class declaration." + "A decorated export must export a class declaration.", ), UnsupportedDefaultExport: _( - "Only expressions), functions or classes are allowed as the `default` export." + "Only expressions), functions or classes are allowed as the `default` export.", ), UnsupportedImport: _( - "`import` can only be used in `import()` or `import.meta`." + "`import` can only be used in `import()` or `import.meta`.", ), UnsupportedMetaProperty: _<{ target: string, onlyValidProperty: string }>( ({ target, onlyValidProperty }) => - "The only valid meta property for ${target} is ${target}.${onlyValidProperty}." + "The only valid meta property for ${target} is ${target}.${onlyValidProperty}.", ), UnsupportedParameterDecorator: _( - "Decorators cannot be used to decorate parameters." + "Decorators cannot be used to decorate parameters.", ), UnsupportedPropertyDecorator: _( - "Decorators cannot be used to decorate object literal properties." + "Decorators cannot be used to decorate object literal properties.", ), UnsupportedSuper: _( - "'super' can only be used with function calls (i.e. super()) or in property accesses (i.e. super.prop or super[prop])." + "'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: _<{ name: string }>( - ({ name }) => `Identifier '${name}' has already been declared.` + ({ name }) => `Identifier '${name}' has already been declared.`, ), YieldBindingIdentifier: _( - "Can not use 'yield' as identifier inside a generator." + "Can not use 'yield' as identifier inside a generator.", ), YieldInParameter: _("Yield expression is not allowed in formal parameters."), ZeroDigitNumericSeparator: _( - "Numeric separator can not be used after leading 0." + "Numeric separator can not be used after leading 0.", ), })); diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 013609c64245..2174fc66dd62 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -2460,11 +2460,12 @@ export default class ExpressionParser extends LValParser { // This logic is here to align the error location with the ESTree plugin. this.raise(Errors.IllegalLanguageModeDirective, { // $FlowIgnore - at: (node.kind === "method" || node.kind === "constructor") && - // $FlowIgnore - !!node.key - ? node.key.loc.end - : node + at: + (node.kind === "method" || node.kind === "constructor") && + // $FlowIgnore + !!node.key + ? node.key.loc.end + : node, }); } @@ -2571,7 +2572,7 @@ export default class ExpressionParser extends LValParser { if (!allowEmpty) { this.raise(Errors.UnexpectedToken, { at: this.state.curPosition(), - found: "," + found: ",", }); } elt = null; @@ -2696,7 +2697,7 @@ export default class ExpressionParser extends LValParser { if (checkKeywords && isKeyword(word)) { this.raise(Errors.UnexpectedKeyword, { at: startLoc, - keyword: word + keyword: word, }); return; } @@ -2710,7 +2711,7 @@ export default class ExpressionParser extends LValParser { if (reservedTest(word, this.inModule)) { this.raise(Errors.UnexpectedReservedWord, { at: startLoc, - reservedWord: word + reservedWord: word, }); } } @@ -2779,7 +2780,7 @@ export default class ExpressionParser extends LValParser { this.expressionScope.recordParameterInitializerError( Errors.YieldInParameter, - { at: node } + { at: node }, ); this.next(); diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 290c5e350483..9bd7027c2567 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -220,7 +220,7 @@ export default class StatementParser extends ExpressionParser { for (const [name, loc] of Array.from(this.scope.undefinedExports)) { this.raise(Errors.ModuleExportUndefined, { at: loc, - exportBinding: name + exportBinding: name, }); } } @@ -957,7 +957,7 @@ export default class StatementParser extends ExpressionParser { if (label.name === maybeName) { this.raise(Errors.LabelRedeclaration, { at: expr, - label: maybeName + label: maybeName, }); } } @@ -1175,7 +1175,7 @@ export default class StatementParser extends ExpressionParser { if (init.type === "AssignmentPattern") { this.raise(Errors.InvalidLhs, { at: init, - construct: "for-loop" + construct: "for-loop", }); } @@ -1228,7 +1228,7 @@ export default class StatementParser extends ExpressionParser { if (!isTypescript) { this.raise(Errors.DeclarationMissingInitializer, { at: this.state.lastTokEndLoc, - contextDescription: "Const declarations" + contextDescription: "Const declarations", }); } } else if ( @@ -2329,7 +2329,7 @@ export default class StatementParser extends ExpressionParser { name === "default" ? Errors.DuplicateDefaultExport : Errors.DuplicateExport, - { at: node, name } + { at: node, name }, ); } this.exportedIdentifiers.add(name); @@ -2537,10 +2537,9 @@ export default class StatementParser extends ExpressionParser { node.key = this.parseIdentifier(true); if (node.key.name !== "type") { - this.raise( - Errors.ModuleAttributeDifferentFromType, { - at: node.key, - key: node.key.name, + this.raise(Errors.ModuleAttributeDifferentFromType, { + at: node.key, + key: node.key.name, }); } diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index 2f2e1bdba107..a01478be6ce2 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -99,7 +99,7 @@ export default class UtilParser extends Tokenizer { expectContextual( token: TokenType, - ParseErrorClass?: Class> + ParseErrorClass?: Class>, ): void { if (!this.eatContextual(token)) { if (ParseErrorClass != null) { diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 067420b6a1fa..162d3cd056c5 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -57,141 +57,171 @@ const reservedTypes = new Set([ const FlowErrors = toParseErrorClasses( _ => ({ AmbiguousConditionalArrow: _( - "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate." + "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." + "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: _<{| reservedType: string |}>( - ({ reservedType }) => `Cannot overwrite reserved type ${reservedType}.` + ({ reservedType }) => `Cannot overwrite reserved type ${reservedType}.`, ), DeclareClassElement: _( - "The `declare` modifier can only appear on class fields." + "The `declare` modifier can only appear on class fields.", ), DeclareClassFieldInitializer: _( - "Initializers are not allowed in fields with the `declare` modifier." + "Initializers are not allowed in fields with the `declare` modifier.", ), DuplicateDeclareModuleExports: _( - "Duplicate `declare module.exports` statement." + "Duplicate `declare module.exports` statement.", ), EnumBooleanMemberNotInitialized: _<{| memberName: string, enumName: string, |}>( ({ memberName, enumName }) => - `Boolean enum members need to be initialized. Use either \`${memberName} = true,\` or \`${memberName} = false,\` in enum \`${enumName}\`.` + `Boolean enum members need to be initialized. Use either \`${memberName} = true,\` or \`${memberName} = false,\` in enum \`${enumName}\`.`, ), EnumDuplicateMemberName: _<{| memberName: string, enumName: string |}>( ({ memberName, enumName }) => - `Enum member names need to be unique, but the name \`${memberName}\` has already been used before in enum \`${enumName}\`.` + `Enum member names need to be unique, but the name \`${memberName}\` has already been used before in enum \`${enumName}\`.`, ), EnumInconsistentMemberValues: _<{| enumName: string |}>( ({ enumName }) => - `Enum \`${enumName}\` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers.` + `Enum \`${enumName}\` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers.`, ), EnumInvalidExplicitType: _<{| invalidEnumType: string, enumName: string |}>( ({ invalidEnumType, enumName }) => - `Enum type \`${invalidEnumType}\` is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.` + `Enum type \`${invalidEnumType}\` is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`, ), EnumInvalidExplicitTypeUnknownSupplied: _<{| enumName: string |}>( ({ enumName }) => - `Supplied enum type is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.` + `Supplied enum type is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`, + ), + EnumInvalidMemberInitializerPrimaryType: _<{| + enumName: string, + memberName: string, + explicitType: string, + |}>( + ({ enumName, memberName, explicitType }) => + `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of \`${memberName}\` needs to be a ${explicitType} literal.`, + ), + EnumInvalidMemberInitializerSymbolType: _<{| + enumName: string, + memberName: string, + explicitType: string, + |}>( + ({ enumName, memberName }) => + `Symbol enum members cannot be initialized. Use \`${memberName},\` in enum \`${enumName}\`.`, + ), + EnumInvalidMemberInitializerUnknownType: _<{| + enumName: string, + memberName: string, + explicitType: string, + |}>( + ({ enumName, memberName }) => + `The enum member initializer for \`${memberName}\` needs to be a literal (either a boolean, number, or string) in enum \`${enumName}\`.`, + ), + EnumInvalidMemberName: _<{| + enumName: string, + memberName: string, + suggestion: string, + |}>( + ({ enumName, memberName, suggestion }) => + `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${enumName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`, + ), + EnumNumberMemberNotInitialized: _<{| + enumName: string, + memberName: string, + |}>( + ({ enumName, memberName }) => + `Number enum members need to be initialized, e.g. \`${memberName} = 1\` in enum \`${enumName}\`.`, + ), + EnumStringMemberInconsistentlyInitailized: _<{| enumName: string |}>( + ({ enumName }) => + `String enum members need to consistently either all use initializers, or use no initializers, in enum \`${enumName}\`.`, ), - EnumInvalidMemberInitializerPrimaryType: _<{| enumName: string, memberName: string, - explicitType: string |}>(({ enumName, memberName, explicitType }) => - `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of \`${memberName}\` needs to be a ${explicitType} literal.`), - EnumInvalidMemberInitializerSymbolType: _<{| enumName: string, memberName: string, explicitType: string |}>(({ enumName, memberName }) => - `Symbol enum members cannot be initialized. Use \`${memberName},\` in enum \`${enumName}\`.`), - EnumInvalidMemberInitializerUnknownType: _<{| enumName: string, memberName: string, explicitType: string |}>(({ enumName, memberName }) => - `The enum member initializer for \`${memberName}\` needs to be a literal (either a boolean, number, or string) in enum \`${enumName}\`.`), - EnumInvalidMemberName: _<{| enumName: string, memberName: string, suggestion: string |}>(({ enumName, memberName, suggestion }) => - `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${enumName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`), - EnumNumberMemberNotInitialized: _<{| enumName: string, memberName: string |}>(({ enumName, memberName }) => - `Number enum members need to be initialized, e.g. \`${memberName} = 1\` in enum \`${enumName}\`.`), - EnumStringMemberInconsistentlyInitailized: _<{| enumName: string |}>(({ enumName }) => - `String enum members need to consistently either all use initializers, or use no initializers, in enum \`${enumName}\`.`), GetterMayNotHaveThisParam: _("A getter cannot have a `this` parameter."), 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." + "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." + "Explicit inexact syntax cannot appear inside an explicit exact object type.", ), InexactInsideNonObject: _( - "Explicit inexact syntax cannot appear in class or interface definitions." + "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`." + "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." + "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`." + "`declare module` cannot be used inside another `declare module`.", ), NestedFlowComment: _( - "Cannot have a flow comment inside another flow comment." + "Cannot have a flow comment inside another flow comment.", ), PatternIsOptional: _( - "A binding pattern parameter cannot be optional in an implementation signature." + "A binding pattern parameter cannot be optional in an implementation signature.", ), SetterMayNotHaveThisParam: _("A setter cannot have a `this` parameter."), SpreadVariance: _("Spread properties cannot have variance."), ThisParamAnnotationRequired: _( - "A type annotation is required for the `this` parameter." + "A type annotation is required for the `this` parameter.", ), ThisParamBannedInConstructor: _( - "Constructors cannot have a `this` parameter; constructors don't bind `this` like other functions." + "Constructors cannot have a `this` parameter; constructors don't bind `this` like other functions.", ), ThisParamMayNotBeOptional: _("The `this` parameter cannot be optional."), ThisParamMustBeFirst: _( - "The `this` parameter must be the first function parameter." + "The `this` parameter must be the first function parameter.", ), ThisParamNoDefault: _("The `this` parameter may not have a default value."), TypeBeforeInitializer: _( - "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`." + "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." + "The type cast expression is expected to be wrapped with parenthesis.", ), UnexpectedExplicitInexactInObject: _( - "Explicit inexact syntax must appear at the end of an inexact object." + "Explicit inexact syntax must appear at the end of an inexact object.", ), UnexpectedReservedType: _<{| reservedType: string |}>( - ({ reservedType }) => `Unexpected reserved type ${reservedType}.` + ({ reservedType }) => `Unexpected reserved type ${reservedType}.`, ), UnexpectedReservedUnderscore: _( - "`_` is only allowed as a type argument to call or new." + "`_` is only allowed as a type argument to call or new.", ), UnexpectedSpaceBetweenModuloChecks: _( - "Spaces between `%` and `checks` are not allowed here." + "Spaces between `%` and `checks` are not allowed here.", ), UnexpectedSpreadType: _( - "Spread operator cannot appear in class or interface definitions." + "Spread operator cannot appear in class or interface definitions.", ), UnexpectedSubtractionOperand: _( - 'Unexpected token, expected "number" or "bigint".' + 'Unexpected token, expected "number" or "bigint".', ), UnexpectedTokenAfterTypeParameter: _( - "Expected an arrow function after this type parameter declaration." + "Expected an arrow function after this type parameter declaration.", ), UnexpectedTypeParameterBeforeAsyncArrowFunction: _( - "Type parameters must come after the async keyword, e.g. instead of ` async () => {}`, use `async () => {}`." + "Type parameters must come after the async keyword, e.g. instead of ` async () => {}`, use `async () => {}`.", ), UnsupportedDeclareExportKind: _<{| unsupported: string, suggestion: string, |}>( ({ unsupported, suggestion }) => - `\`declare export ${unsupported}\` is not supported. Use \`${suggestion}\` instead.` + `\`declare export ${unsupported}\` is not supported. Use \`${suggestion}\` instead.`, ), UnsupportedStatementInDeclareModule: _( - "Only declares and type imports are allowed inside declare module." + "Only declares and type imports are allowed inside declare module.", ), UnterminatedFlowComment: _("Unterminated flow-comment."), }), - { syntaxPlugin: "flow" } + { syntaxPlugin: "flow" }, ); /* eslint-disable sort-keys */ @@ -717,10 +747,12 @@ export default (superClass: Class): Class => this.raise( declaration ? FlowErrors.AssignReservedType - : FlowErrors.UnexpectedReservedType, { - at: startLoc, - reservedType: word, - }); + : FlowErrors.UnexpectedReservedType, + { + at: startLoc, + reservedType: word, + }, + ); } flowParseRestrictedIdentifier( @@ -3316,12 +3348,14 @@ export default (superClass: Class): Class => : explicitType === "symbol" ? FlowErrors.EnumInvalidMemberInitializerSymbolType : FlowErrors.EnumInvalidMemberInitializerUnknownType, - { at: loc, - enumName, - memberName, - // FIXME: ? - explicitType: "hi"/*explicitType || "unknown"*/, - }); + { + at: loc, + enumName, + memberName, + // FIXME: ? + explicitType: "hi" /*explicitType || "unknown"*/, + }, + ); } flowEnumErrorNumberMemberNotInitialized( @@ -3565,7 +3599,7 @@ export default (superClass: Class): Class => this.raise(FlowErrors.EnumInvalidExplicitType, { at: this.state.startLoc, enumName, - invalidEnumType: value + invalidEnumType: value, }); } diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index 1921f55d5671..56444e5d59eb 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -23,35 +23,34 @@ import { Errors, toParseErrorClasses } from "../../parse-error"; const JsxErrors = toParseErrorClasses( _ => ({ AttributeIsEmpty: _( - "JSX attributes must only be assigned a non-empty expression." + "JSX attributes must only be assigned a non-empty expression.", ), MissingClosingTagElement: _<{| openingTagName: string |}>( ({ openingTagName }) => - `Expected corresponding JSX closing tag for <${openingTagName}>.` + `Expected corresponding JSX closing tag for <${openingTagName}>.`, ), MissingClosingTagFragment: _( - "Expected corresponding JSX closing tag for <>." + "Expected corresponding JSX closing tag for <>.", ), UnexpectedSequenceExpression: _( - "Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?" + "Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?", ), UnsupportedJsxValue: _( - "JSX value should be either an expression or a quoted JSX text." + "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 <>...?" + "Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...?", ), // FIXME: Unify with Errors.UnexpectedToken UnexpectedToken: _<{| found: string, HTMLEntity: string |}>( ({ found, HTMLEntity }) => - `Unexpected token \`${found}\`. Did you mean \`${HTMLEntity}\` or \`{'${found}'}\`?` + `Unexpected token \`${found}\`. Did you mean \`${HTMLEntity}\` or \`{'${found}'}\`?`, ), }), - { syntaxPlugin: "jsx" } + { syntaxPlugin: "jsx" }, ); - /* eslint-disable sort-keys */ function isFragment(object: ?N.JSXElement): boolean { @@ -127,7 +126,8 @@ export default (superClass: Class): Class => this.raise(JsxErrors.UnexpectedToken, { at: this.state.curPosition(), found: this.input[this.state.pos], - HTMLEntity: ch === charCodes.rightCurlyBrace ? "}" : ">" + HTMLEntity: + ch === charCodes.rightCurlyBrace ? "}" : ">", }); } /* falls through */ diff --git a/packages/babel-parser/src/plugins/placeholders.js b/packages/babel-parser/src/plugins/placeholders.js index 74c044817d73..14ff8aefd616 100644 --- a/packages/babel-parser/src/plugins/placeholders.js +++ b/packages/babel-parser/src/plugins/placeholders.js @@ -50,9 +50,9 @@ type MaybePlaceholder = NodeOf; // | Placeholder const PlaceholderErrors = toParseErrorClasses( _ => ({ ClassNameIsRequired: _("A class name is required."), - UnexpectedSpace: _("Unexpected space in placeholder.") + UnexpectedSpace: _("Unexpected space in placeholder."), }), - { syntaxPlugin: "placeholders" } + { syntaxPlugin: "placeholders" }, ); /* eslint-disable sort-keys */ @@ -369,7 +369,7 @@ export default (superClass: Class): Class => assertNoSpace(): void { if (this.state.start > this.state.lastTokEndLoc.index) { this.raise(PlaceholderErrors.UnexpectedSpace, { - at: this.state.lastTokEndLoc + at: this.state.lastTokEndLoc, }); } } diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 418e2df5ab33..29bd9d7fdc7d 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -69,153 +69,173 @@ type ParsingContext = /* eslint sort-keys: "error" */ const TSErrors = toParseErrorClasses( _ => ({ - AbstractMethodHasImplementation: _<{| methodName: string |}>(({ methodName }) => - `Method '${methodName}' cannot have an implementation because it is marked abstract.` + AbstractMethodHasImplementation: _<{| methodName: string |}>( + ({ methodName }) => + `Method '${methodName}' cannot have an implementation because it is marked abstract.`, ), - AbstractPropertyHasInitializer: _<{| propertyName: string |}>(({ propertyName }) => - `Property '${propertyName}' cannot have an initializer because it is marked abstract.` + AbstractPropertyHasInitializer: _<{| propertyName: string |}>( + ({ propertyName }) => + `Property '${propertyName}' cannot have an initializer because it is marked abstract.`, ), AccesorCannotDeclareThisParameter: _( - "'get' and 'set' accessors cannot declare 'this' parameters." + "'get' and 'set' accessors cannot declare 'this' parameters.", ), AccesorCannotHaveTypeParameters: _( - "An accessor cannot have type parameters." + "An accessor cannot have type parameters.", ), ClassMethodHasDeclare: _( - "Class methods cannot have the 'declare' modifier." + "Class methods cannot have the 'declare' modifier.", ), ClassMethodHasReadonly: _( - "Class methods cannot have the 'readonly' modifier." + "Class methods cannot have the 'readonly' modifier.", ), ConstructorHasTypeParameters: _( - "Type parameters cannot appear on a constructor declaration." + "Type parameters cannot appear on a constructor declaration.", ), // kind? - DeclareAccessor: _<{| accessorKind: "get" | "set" |}>(({ accessorKind }) => `'declare' is not allowed in ${accessorKind}ters.`), + DeclareAccessor: _<{| accessorKind: "get" | "set" |}>( + ({ accessorKind }) => `'declare' is not allowed in ${accessorKind}ters.`, + ), DeclareClassFieldHasInitializer: _( - "Initializers are not allowed in ambient contexts." + "Initializers are not allowed in ambient contexts.", ), DeclareFunctionHasImplementation: _( - "An implementation cannot be declared in ambient contexts." + "An implementation cannot be declared in ambient contexts.", + ), + DuplicateAccessibilityModifier: _<{| modifier: N.Accessibility |}>( + ({ modifier }) => "Accessibility modifier ${modifier} already seen.", + ), + DuplicateModifier: _<{| modifier: TsModifier |}>( + ({ modifier }) => `Duplicate modifier: '${modifier}'.`, + ), + EmptyHeritageClauseType: _<{| descriptor: string |}>( + ({ descriptor }) => `'${descriptor}' list cannot be empty.`, ), - DuplicateAccessibilityModifier: _<{| modifier: N.Accessibility |}>(({ modifier }) => "Accessibility modifier ${modifier} already seen."), - DuplicateModifier: _<{| modifier: TsModifier |}>(({ modifier }) => `Duplicate modifier: '${modifier}'.`), - EmptyHeritageClauseType: _<{| descriptor: string |}>(({ descriptor }) => `'${descriptor}' list cannot be empty.`), EmptyTypeArguments: _("Type argument list cannot be empty."), EmptyTypeParameters: _("Type parameter list cannot be empty."), ExpectedAmbientAfterExportDeclare: _( - "'export declare' must be followed by an ambient declaration." + "'export declare' must be followed by an ambient declaration.", ), ImportAliasHasImportType: _("An import alias can not use 'import type'."), - IncompatibleModifiers: _<{| modifiers: [TsModifier, TsModifier] |}>(({ modifiers }) => - `'${modifiers[0]}' modifier cannot be used with '${modifiers[1]}' modifier.` + IncompatibleModifiers: _<{| modifiers: [TsModifier, TsModifier] |}>( + ({ modifiers }) => + `'${modifiers[0]}' modifier cannot be used with '${modifiers[1]}' modifier.`, ), IndexSignatureHasAbstract: _( - "Index signatures cannot have the 'abstract' modifier." + "Index signatures cannot have the 'abstract' modifier.", ), - IndexSignatureHasAccessibility: _<{| modifier: N.Accessibility |}>(({ modifier }) => - `Index signatures cannot have an accessibility modifier ('${modifier}').` + IndexSignatureHasAccessibility: _<{| modifier: N.Accessibility |}>( + ({ modifier }) => + `Index signatures cannot have an accessibility modifier ('${modifier}').`, ), IndexSignatureHasDeclare: _( - "Index signatures cannot have the 'declare' modifier." + "Index signatures cannot have the 'declare' modifier.", ), IndexSignatureHasOverride: _( - "'override' modifier cannot appear on an index signature." + "'override' modifier cannot appear on an index signature.", ), IndexSignatureHasStatic: _( - "Index signatures cannot have the 'static' modifier." + "Index signatures cannot have the 'static' modifier.", + ), + InvalidModifierOnTypeMember: _<{| modifier: TsModifier |}>( + ({ modifier }) => + `'${modifier}' modifier cannot appear on a type member.`, ), - InvalidModifierOnTypeMember: _<{| modifier: TsModifier |}>(({ modifier }) => - `'${modifier}' modifier cannot appear on a type member.` + InvalidModifiersOrder: _<{| orderedModifiers: [TsModifier, TsModifier] |}>( + ({ orderedModifiers }) => + `'${orderedModifiers[0]}' modifier must precede '${orderedModifiers[1]}' modifier.`, ), - InvalidModifiersOrder: _<{| orderedModifiers: [TsModifier, TsModifier] |}>(({ orderedModifiers }) => `'${orderedModifiers[0]}' modifier must precede '${orderedModifiers[1]}' modifier.`), InvalidTupleMemberLabel: _( - "Tuple members must be labeled with a simple identifier." + "Tuple members must be labeled with a simple identifier.", ), MissingInterfaceName: _( - "'interface' declarations must be followed by an identifier." + "'interface' declarations must be followed by an identifier.", ), MixedLabeledAndUnlabeledElements: _( - "Tuple members must all have names or all not have names." + "Tuple members must all have names or all not have names.", ), NonAbstractClassHasAbstractMethod: _( - "Abstract methods can only appear within an abstract class." + "Abstract methods can only appear within an abstract class.", ), NonClassMethodPropertyHasAbstractModifer: _( - "'abstract' modifier can only appear on a class, method, or property declaration." + "'abstract' modifier can only appear on a class, method, or property declaration.", ), OptionalTypeBeforeRequired: _( - "A required element cannot follow an optional element." + "A required element cannot follow an optional element.", ), OverrideNotInSubClass: _( - "This member cannot have an 'override' modifier because its containing class does not extend another class." + "This member cannot have an 'override' modifier because its containing class does not extend another class.", ), PatternIsOptional: _( - "A binding pattern parameter cannot be optional in an implementation signature." + "A binding pattern parameter cannot be optional in an implementation signature.", ), PrivateElementHasAbstract: _( - "Private elements cannot have the 'abstract' modifier." + "Private elements cannot have the 'abstract' modifier.", ), - PrivateElementHasAccessibility: _<{| modifier: N.Accessibility |}>(({ modifier }) => - `Private elements cannot have an accessibility modifier ('${modifier}').` + PrivateElementHasAccessibility: _<{| modifier: N.Accessibility |}>( + ({ modifier }) => + `Private elements cannot have an accessibility modifier ('${modifier}').`, ), ReadonlyForMethodSignature: _( - "'readonly' modifier can only appear on a property declaration or index signature." + "'readonly' modifier can only appear on a property declaration or index signature.", ), ReservedArrowTypeParam: _( - "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `() => ...`." + "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `() => ...`.", ), ReservedTypeAssertion: _( - "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead." + "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead.", ), SetAccesorCannotHaveOptionalParameter: _( - "A 'set' accessor cannot have an optional parameter." + "A 'set' accessor cannot have an optional parameter.", ), SetAccesorCannotHaveRestParameter: _( - "A 'set' accessor cannot have rest parameter." + "A 'set' accessor cannot have rest parameter.", ), SetAccesorCannotHaveReturnType: _( - "A 'set' accessor cannot have a return type annotation." + "A 'set' accessor cannot have a return type annotation.", ), SingleTypeParameterWithoutTrailingComma: _<{ name: string }>(({ name }) => `Single type parameter ${name} should have a trailing comma. Example usage: <${name},>.` ), StaticBlockCannotHaveModifier: _( - "Static class blocks cannot have any modifier." + "Static class blocks cannot have any modifier.", ), TypeAnnotationAfterAssign: _( - "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`." + "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`.", ), TypeImportCannotSpecifyDefaultAndNamed: _( - "A type-only import can specify a default import or named bindings, but not both." + "A type-only import can specify a default import or named bindings, but not both.", ), TypeModifierIsUsedInTypeExports: _( - "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement." + "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.", ), TypeModifierIsUsedInTypeImports: _( - "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement." + "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.", ), UnexpectedParameterModifier: _( - "A parameter property is only allowed in a constructor implementation." + "A parameter property is only allowed in a constructor implementation.", ), UnexpectedReadonly: _( - "'readonly' type modifier is only permitted on array and tuple literal types." + "'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." + "Unexpected type cast in parameter position.", ), UnsupportedImportTypeArgument: _( - "Argument in a type import must be a string literal." + "Argument in a type import must be a string literal.", ), UnsupportedParameterPropertyKind: _( - "A parameter property may not be declared using a binding pattern." + "A parameter property may not be declared using a binding pattern.", ), - UnsupportedSignatureParameterKind: _<{| unsupportedParameterType: string |}>(({ unsupportedParameterType }) => - `Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got ${unsupportedParameterType}.` + UnsupportedSignatureParameterKind: _<{| + unsupportedParameterType: string, + |}>( + ({ unsupportedParameterType }) => + `Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got ${unsupportedParameterType}.`, ), }), - { syntaxPlugin: "typescript" } + { syntaxPlugin: "typescript" }, ); /* eslint-disable sort-keys */ @@ -363,7 +383,7 @@ export default (superClass: Class): Class => if (modified.accessibility) { this.raise(TSErrors.DuplicateAccessibilityModifier, { at: startLoc, - modifier + modifier, }); } else { enforceOrder(startLoc, modifier, modifier, "override"); @@ -390,7 +410,7 @@ export default (superClass: Class): Class => if (disallowedModifiers?.includes(modifier)) { this.raise(TSErrors.InvalidModifierOnTypeMember, { at: startLoc, - modifier + modifier, }); } } @@ -2082,7 +2102,7 @@ export default (superClass: Class): Class => "protected", "override", "readonly", - ] + ], }); accessibility = modified.accessibility; override = modified.override; @@ -2804,9 +2824,10 @@ export default (superClass: Class): Class => const { key } = node; this.raise(TSErrors.AbstractPropertyHasInitializer, { at: this.state.startLoc, - propertyName: key.type === "Identifier" && !node.computed - ? key.name - : `[${this.input.slice(key.start, key.end)}]`, + propertyName: + key.type === "Identifier" && !node.computed + ? key.name + : `[${this.input.slice(key.start, key.end)}]`, }); } @@ -2816,7 +2837,7 @@ export default (superClass: Class): Class => parseClassPrivateProperty( node: N.ClassPrivateProperty, ): N.ClassPrivateProperty { - // $FlowIgnore + // $FlowIgnore if (node.abstract) { this.raise(TSErrors.PrivateElementHasAbstract, { at: node }); } @@ -2852,7 +2873,7 @@ export default (superClass: Class): Class => if (method.declare && (method.kind === "get" || method.kind === "set")) { this.raise(TSErrors.DeclareAccessor, { at: method, - accessorKind: method.kind + accessorKind: method.kind, }); } if (typeParameters) method.typeParameters = typeParameters; @@ -3469,9 +3490,10 @@ export default (superClass: Class): Class => const { key } = method; this.raise(TSErrors.AbstractMethodHasImplementation, { at: method, - methodName: key.type === "Identifier" && !method.computed - ? key.name - : `[${this.input.slice(key.start, key.end)}]`, + methodName: + key.type === "Identifier" && !method.computed + ? key.name + : `[${this.input.slice(key.start, key.end)}]`, }); } } diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 5fe780182393..57a91692f32c 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -3,7 +3,11 @@ /*:: declare var invariant; */ import type { Options } from "../options"; -import { Position, SourceLocation, createPositionWithColumnOffset } from "../util/location"; +import { + Position, + SourceLocation, + createPositionWithColumnOffset, +} from "../util/location"; import CommentsParser from "../parser/comments"; import * as N from "../types"; import * as charCodes from "charcodes"; @@ -1072,7 +1076,7 @@ export default class Tokenizer extends CommentsParser { throw this.raise(Errors.InvalidOrUnexpectedToken, { at: this.state.curPosition(), - found: String.fromCodePoint(code) + found: String.fromCodePoint(code), }); } @@ -1539,7 +1543,7 @@ export default class Tokenizer extends CommentsParser { recordStrictModeErrors( ParseErrorClass: DeferredStrictErrorClass, - { at } : { at: Position }, + { at }: { at: Position }, ) { const index = at.index; @@ -1632,7 +1636,7 @@ export default class Tokenizer extends CommentsParser { return null; } else { this.recordStrictModeErrors(Errors.StrictNumericEscape, { - at: codePos + at: codePos, }); } } @@ -1743,13 +1747,10 @@ export default class Tokenizer extends CommentsParser { } } - raise< - ErrorProperties, - T: Class>> - ( + raise>>( ParseErrorClass: T, - raiseProperties: RaiseProperties - ) : ParseError { + raiseProperties: RaiseProperties, + ): ParseError { const { at, ...rest } = raiseProperties; const loc = at instanceof Position ? at : at.loc.start; const error = new ParseErrorClass({ ...rest, loc }); @@ -1760,7 +1761,7 @@ export default class Tokenizer extends CommentsParser { return error; } -/* + /* /** * Raise a parsing error on given position pos. If errorRecovery is true, * it will first search current errors and overwrite the error thrown on the exact @@ -1775,7 +1776,7 @@ export default class Tokenizer extends CommentsParser { */ raiseOverwrite>>( ParseErrorClass: T, - raiseProperties: RaiseProperties + raiseProperties: RaiseProperties, ): ParseError | empty { const { at, ...rest } = raiseProperties; const loc = at instanceof Position ? at : at.loc.start; @@ -1800,7 +1801,7 @@ export default class Tokenizer extends CommentsParser { // Raise an unexpected token error. Can take the expected token type. unexpected(loc?: Position | null, type?: TokenType): void { throw this.raise(Errors.UnexpectedToken, { - expected: !!type ? tokenLabelName(type) : null, + expected: type ? tokenLabelName(type) : null, at: loc != null ? loc : this.state.startLoc, }); } @@ -1812,7 +1813,7 @@ export default class Tokenizer extends CommentsParser { throw this.raise(Errors.MissingPlugin, { at: loc != null ? loc : this.state.startLoc, - missingPlugin: pluginName + missingPlugin: pluginName, }); } @@ -1820,7 +1821,7 @@ export default class Tokenizer extends CommentsParser { if (!pluginNames.some(name => this.hasPlugin(name))) { throw this.raise(Errors.MissingOneOfPlugins, { at: this.state.startLoc, - missingPlugin: pluginNames + missingPlugin: pluginNames, }); } } diff --git a/packages/babel-parser/src/tokenizer/state.js b/packages/babel-parser/src/tokenizer/state.js index 3e3b7010814b..e9988a8d71d9 100644 --- a/packages/babel-parser/src/tokenizer/state.js +++ b/packages/babel-parser/src/tokenizer/state.js @@ -11,8 +11,8 @@ import type { ParseError } from "../parse-error"; import StrictErrors from "../parse-error/strict-mode"; export type DeferredStrictErrorClass = - | typeof StrictErrors.StrictNumericEscape - | typeof StrictErrors.StrictOctalLiteral; + | typeof StrictErrors.StrictNumericEscape + | typeof StrictErrors.StrictOctalLiteral; type TopicContextState = { // When a topic binding has been currently established, diff --git a/packages/babel-parser/src/util/class-scope.js b/packages/babel-parser/src/util/class-scope.js index d89e41598a36..2b8419ba2a35 100644 --- a/packages/babel-parser/src/util/class-scope.js +++ b/packages/babel-parser/src/util/class-scope.js @@ -110,7 +110,7 @@ export default class ClassScopeHandler { // top-level this.parser.raise(Errors.InvalidPrivateFieldResolution, { at: loc, - name + name, }); } } diff --git a/packages/babel-parser/src/util/expression-scope.js b/packages/babel-parser/src/util/expression-scope.js index f4662eebbb97..f78caafb641f 100644 --- a/packages/babel-parser/src/util/expression-scope.js +++ b/packages/babel-parser/src/util/expression-scope.js @@ -78,7 +78,7 @@ class ExpressionScope { type ArrowHeadParsingParameterInitializerErrorClass = | typeof Errors.AwaitExpressionFormalParameter - | typeof Errors.YieldInParameter + | typeof Errors.YieldInParameter; type ArrowHeadParsingDeclarationErrorClass = | ArrowHeadParsingParameterInitializerErrorClass @@ -88,14 +88,14 @@ type ArrowHeadParsingDeclarationErrorClass = class ArrowHeadParsingScope extends ExpressionScope { declarationErrors: Map< number, - [ArrowHeadParsingDeclarationErrorClass, Position] + [ArrowHeadParsingDeclarationErrorClass, Position], > = new Map(); constructor(type: 1 | 2) { super(type); } recordDeclarationError( ParsingError: T, - { at }: { at: Position } + { at }: { at: Position }, ) { const index = at.index; @@ -105,13 +105,12 @@ class ArrowHeadParsingScope extends ExpressionScope { this.declarationErrors.delete(index); } iterateErrors( - iterator: ([T, Position]) => void + iterator: ([T, Position]) => void, ) { this.declarationErrors.forEach(iterator); } } - export default class ExpressionScopeHandler { parser: Tokenizer; stack: Array = [new ExpressionScope()]; @@ -139,7 +138,7 @@ export default class ExpressionScopeHandler { */ recordParameterInitializerError( ParseErrorClass: ArrowHeadParsingParameterInitializerErrorClass, - { at: node } : { at: Node } + { at: node }: { at: Node }, ): void { const origin = { at: node.loc.start }; const { stack } = this; @@ -180,7 +179,7 @@ export default class ExpressionScopeHandler { * @returns {void} * @memberof ExpressionScopeHandler */ - recordParenthesizedIdentifierError({ at: node } : { at: Node }): void { + recordParenthesizedIdentifierError({ at: node }: { at: Node }): void { const { stack } = this; const scope: ExpressionScope = stack[stack.length - 1]; const origin = { at: node.loc.start }; @@ -188,7 +187,10 @@ export default class ExpressionScopeHandler { this.parser.raise(Errors.InvalidParenthesizedAssignment, origin); } else if (scope.canBeArrowParameterDeclaration()) { /*:: invariant(scope instanceof ArrowHeadParsingScope) */ - scope.recordDeclarationError(Errors.InvalidParenthesizedAssignment, origin); + scope.recordDeclarationError( + Errors.InvalidParenthesizedAssignment, + origin, + ); } else { return; } diff --git a/packages/babel-parser/src/util/scope.js b/packages/babel-parser/src/util/scope.js index 5c88a2bfa87e..5fef485e935c 100644 --- a/packages/babel-parser/src/util/scope.js +++ b/packages/babel-parser/src/util/scope.js @@ -39,7 +39,6 @@ export class Scope { // The functions in this module keep track of declared variables in the // current scope in order to detect duplicate variable names. export default class ScopeHandler { - parser: Tokenizer; scopeStack: Array = []; inModule: boolean; From 9e5643d65892c9b3bc87d8618231fd823699d0f4 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Thu, 10 Feb 2022 13:47:47 -0800 Subject: [PATCH 008/104] Fix remaining style issues and remove unecessary dry-error-messages. Reviewed by @tolmasky. --- .eslintrc.cjs | 17 - .../README.md | 45 -- .../test/rules/dry-error-messages.js | 678 ------------------ .../babel-parser/src/parse-error/module.js | 16 +- .../babel-parser/src/parse-error/standard.js | 10 +- .../src/parse-error/strict-mode.js | 26 +- packages/babel-parser/src/parser/util.js | 1 - .../babel-parser/src/plugins/flow/index.js | 2 +- .../babel-parser/src/plugins/jsx/index.js | 10 +- .../babel-parser/src/plugins/placeholders.js | 2 +- .../src/plugins/typescript/index.js | 2 +- .../babel-parser/src/util/expression-scope.js | 4 +- 12 files changed, 46 insertions(+), 767 deletions(-) delete mode 100644 eslint/babel-eslint-plugin-development-internal/test/rules/dry-error-messages.js diff --git a/.eslintrc.cjs b/.eslintrc.cjs index c7f92f01e003..31f33180bf2f 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,7 +1,5 @@ "use strict"; -const path = require("path"); - const cjsGlobals = ["__dirname", "__filename", "require", "module", "exports"]; const testFiles = [ @@ -105,21 +103,6 @@ module.exports = { eqeqeq: ["error", "always", { null: "ignore" }], }, }, - { - files: ["packages/babel-parser/src/**/*.{js,ts}"], - rules: { - "@babel/development-internal/dry-error-messages": [ - "error", - { - errorModule: path.resolve( - __dirname, - "packages/babel-parser/src/parser/error.js" - ), - }, - ], - "@babel/development-internal/report-error-message-format": "error", - }, - }, { files: ["packages/babel-helpers/src/helpers/**.js"], rules: { diff --git a/eslint/babel-eslint-plugin-development-internal/README.md b/eslint/babel-eslint-plugin-development-internal/README.md index 2d8fa5e626ac..d4e9088977e5 100644 --- a/eslint/babel-eslint-plugin-development-internal/README.md +++ b/eslint/babel-eslint-plugin-development-internal/README.md @@ -24,51 +24,6 @@ The plugin can be loaded in your `.eslintrc.*` configuration file as follows: (n ## Rules -### `@babel/development-internal/dry-error-messages` - -Intended for use in `packages/babel-parser/src/**/*`. When enabled, this rule warns when `this.raise()` invocations raise errors that are not imported from a designated error module. - -Accepts an object configuration option: - -```ts -{ - errorModule: string -} -``` - -`errorModule` (required): The rule expects either an absolute path or a module name (for a module in `node_modules`). Please note that the rule will not check anything if` errorModule` is not given. - -Example configuration: - -```js -{ - rules: { - "@babel/development-internal/dry-error-messages": [ - "error", - { - errorModule: "@babel/shared-error-messages" - } - ] - } -} -``` -and -```js -{ - rules: { - "@babel/development-internal/dry-error-messages": [ - "error", - { - errorModule: path.resolve( - __dirname, - "packages/shared-error-messages/lib/index.js" - ) - } - ] - } -} -``` - ### `@babel/development-internal/report-error-message-format` This rule is inspired by https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/report-message-format.md. diff --git a/eslint/babel-eslint-plugin-development-internal/test/rules/dry-error-messages.js b/eslint/babel-eslint-plugin-development-internal/test/rules/dry-error-messages.js deleted file mode 100644 index 609af895772b..000000000000 --- a/eslint/babel-eslint-plugin-development-internal/test/rules/dry-error-messages.js +++ /dev/null @@ -1,678 +0,0 @@ -import path from "path"; -import rule from "../../lib/rules/dry-error-messages.cjs"; -import RuleTester from "../../../babel-eslint-shared-fixtures/utils/RuleTester.js"; -import { fileURLToPath } from "url"; - -const dirname = path.dirname(fileURLToPath(import.meta.url)); - -const FILENAME = path.resolve(dirname, "test/lib/index.js"); -const ERRORS_MODULE = "errorsModule"; -const MODULE_SAME_DIR = path.resolve(dirname, "test/lib/errorsModule.js"); -const MODULE_PARENT_DIR = path.resolve(dirname, "test/errorsModule.js"); - -const ruleTester = new RuleTester(); - -ruleTester.run("dry-error-messages", rule, { - valid: [ - // Ignores malformed `this.raise` invocations. - { - filename: FILENAME, - code: "this.raise(loc);", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "this.notRaise(loc, 'Uh oh');", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "throw new Error(this.raise('Uh oh'));", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "this.raise(() => { throw new Error('Uh oh') });", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "throw new Error('Uh oh')", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "throw this.createError('Uh oh')", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "throw this.error", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "this.raise", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "throw obj.error", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "throw obj.raise", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "import { Errors } from 'errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "import { Errors } from './errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_SAME_DIR }], - }, - { - filename: FILENAME, - code: "import { Errors } from '../errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_PARENT_DIR }], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from 'errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from './errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_SAME_DIR }], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from '../errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_PARENT_DIR }], - }, - { - filename: FILENAME, - code: "import { Errors } from 'errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "import { Errors } from './errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_SAME_DIR }], - }, - { - filename: FILENAME, - code: "import { Errors } from '../errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_PARENT_DIR }], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from 'errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from './errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_SAME_DIR }], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from '../errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_PARENT_DIR }], - }, - { - filename: FILENAME, - code: "import Errors from 'errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "import Errors from './errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_SAME_DIR }], - }, - { - filename: FILENAME, - code: "import Errors from '../errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_PARENT_DIR }], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from './errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_SAME_DIR }], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from '../errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_PARENT_DIR }], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from 'errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from './errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_SAME_DIR }], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from '../errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_PARENT_DIR }], - }, - { - filename: FILENAME, - code: "import Errors from 'errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "import Errors from './errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_SAME_DIR }], - }, - { - filename: FILENAME, - code: "import Errors from '../errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_PARENT_DIR }], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from './errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_SAME_DIR }], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from '../errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_PARENT_DIR }], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from 'errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: ERRORS_MODULE }], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from './errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_SAME_DIR }], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from '../errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_PARENT_DIR }], - }, - - // Do not warn when file linted is error module. - { - filename: FILENAME, - code: "this.raise( 'Oh no!', loc);", - options: [{ errorModule: FILENAME }], - }, - { - filename: MODULE_SAME_DIR, - code: "this.raise('Oh no!', loc);", - options: [{ errorModule: MODULE_SAME_DIR }], - }, - - // Do not warn if second argument is missing - { - filename: FILENAME, - code: "this.raise(loc);", - options: [{ errorModule: ERRORS_MODULE }], - }, - - // Support ternary as second argument - { - filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(a ? Errors.someErrorMessage : Errors.someOtherErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - }, - ], - invalid: [ - { - filename: FILENAME, - code: "this.raise(new Error('Uh oh'), loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "throw this.raise(new Error('Uh oh'), loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "const Errors = { someErrorMessage: 'Uh oh!' }; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import { Errors } from 'errorsModule'; this.raise('Uh oh!', loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import { Errors } from 'errorsModule'; const msg = 'Uh oh!'; this.raise(msg, loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import { Errors } from 'not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import { Errors } from './not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_SAME_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_SAME_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import { Errors } from '../not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_PARENT_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_PARENT_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from 'not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from './not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_SAME_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_SAME_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from '../not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_PARENT_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_PARENT_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import { Errors } from 'not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import { Errors } from './not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_SAME_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_SAME_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import { Errors } from '../not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_PARENT_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_PARENT_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from 'not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from './not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_SAME_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_SAME_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import { NotErrors, Errors } from '../not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_PARENT_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_PARENT_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors from 'not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors from './not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_SAME_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_SAME_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors from '../not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_PARENT_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_PARENT_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from 'not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from './not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_SAME_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_SAME_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from '../not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_PARENT_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_PARENT_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from 'not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from './not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_SAME_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_SAME_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from '../not-errorsModule'; this.raise(Errors.someErrorMessage, loc);", - options: [{ errorModule: MODULE_PARENT_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_PARENT_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors from 'not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors from './not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_SAME_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_SAME_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors from '../not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_PARENT_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_PARENT_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from 'not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from './not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_SAME_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_SAME_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from '../not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_PARENT_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_PARENT_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from 'not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from './not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_SAME_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_SAME_DIR }, - }, - ], - }, - { - filename: FILENAME, - code: "import NotErrors, { Errors } from '../not-errorsModule'; function fn() { this.raise(Errors.someErrorMessage, loc); }", - options: [{ errorModule: MODULE_PARENT_DIR }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: MODULE_PARENT_DIR }, - }, - ], - }, - - // Should error if either part of a ternary isn't from error module - { - filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(a ? Errors.someErrorMessage : 'hello', loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; this.raise( a ? 'hello' : Errors.someErrorMessage, loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - { - filename: FILENAME, - code: "import Errors, { NotErrors } from 'errorsModule'; this.raise(a ? 'hello' : 'world', loc);", - options: [{ errorModule: ERRORS_MODULE }], - errors: [ - { - messageId: "mustBeImported", - data: { errorModule: ERRORS_MODULE }, - }, - ], - }, - ], -}); diff --git a/packages/babel-parser/src/parse-error/module.js b/packages/babel-parser/src/parse-error/module.js index 823f409ac073..98817ebac765 100644 --- a/packages/babel-parser/src/parse-error/module.js +++ b/packages/babel-parser/src/parse-error/module.js @@ -1,7 +1,13 @@ import { ErrorCodes, toParseErrorClasses } from "../parse-error"; -export default toParseErrorClasses(_ => { - ImportMetaOutsideModule: _(`import.meta may appear only with 'sourceType: "module"'`), - ImportOutsideModule: _(`'import' and 'export' may appear only with 'sourceType: "module"'`), - { code: ErrorCodes.SourceTypeModuleError } -}); +export default toParseErrorClasses( + _ => ({ + ImportMetaOutsideModule: _( + `import.meta may appear only with 'sourceType: "module"'`, + ), + ImportOutsideModule: _( + `'import' and 'export' may appear only with 'sourceType: "module"'`, + ), + }), + { code: ErrorCodes.SourceTypeModuleError }, +); diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index 2332456eb97b..b567efbb2a62 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -2,7 +2,6 @@ import { Position } from "../util/location"; import { toParseErrorClasses } from "../parse-error"; -import { type TokenType, tokenLabelName } from "../tokenizer/types"; export default toParseErrorClasses(_ => ({ AccessorIsGenerator: _<{ accessor: string }>( @@ -70,7 +69,7 @@ export default toParseErrorClasses(_ => ({ EscapedCharNotAnIdentifier: _("Invalid Unicode escape."), ExportBindingIsString: _<{ localBinding: string, exportBinding: string }>( ({ localBinding, exportBinding }) => - "A string literal cannot be used as an exported binding without `from`.\n- Did you mean `export { '${localBinding}' as '{exportBinding}' } from 'some-module'`?", + `A string literal cannot be used as an exported binding without \`from\`.\n- Did you mean \`export { '${localBinding}' as '${exportBinding}' } from 'some-module'?\``, ), ExportDefaultFromAsIdentifier: _( "'from' is not allowed as an identifier after 'export default'.", @@ -336,8 +335,11 @@ export default toParseErrorClasses(_ => ({ UnexpectedToken: _<{ loc: Position, expected?: string, + /* eslint-disable no-confusing-arrow */ }>(({ loc: { line, column }, expected }) => - (expected ? `Unexpected token), expected "${expected}"` : "Unexpected token"), + expected + ? `Unexpected token, expected "${expected} (${line}:${column})"` + : "Unexpected token", ), UnexpectedTokenUnaryExponentiation: _( "Illegal expression. Wrap left hand side or entire exponentiation in parentheses.", @@ -354,7 +356,7 @@ export default toParseErrorClasses(_ => ({ ), UnsupportedMetaProperty: _<{ target: string, onlyValidProperty: string }>( ({ target, onlyValidProperty }) => - "The only valid meta property for ${target} is ${target}.${onlyValidProperty}.", + `The only valid meta property for ${target} is ${target}.${onlyValidProperty}.`, ), UnsupportedParameterDecorator: _( "Decorators cannot be used to decorate parameters.", diff --git a/packages/babel-parser/src/parse-error/strict-mode.js b/packages/babel-parser/src/parse-error/strict-mode.js index 50263d47dde6..103357997c48 100644 --- a/packages/babel-parser/src/parse-error/strict-mode.js +++ b/packages/babel-parser/src/parse-error/strict-mode.js @@ -1,11 +1,23 @@ +// @flow + import { toParseErrorClasses } from "../parse-error"; -export default toParseErrorClasses(_ => { +export default toParseErrorClasses(_ => ({ StrictDelete: _("Deleting local variable in strict mode."), - StrictEvalArguments: _<{ binding: string }>(({ binding }) => `Assigning to '${binding}' in strict mode.`), - StrictEvalArgumentsBinding: _<{ binding: string }>(({ binding }) => `Binding '${binding}' in strict mode.`), - StrictFunction: _("In strict mode code, functions can only be declared at top level or inside a block."), - StrictNumericEscape: _("The only valid numeric escape in strict mode is '\\0'."), - StrictOctalLiteral: _("Legacy octal literals are not allowed in strict mode."), + StrictEvalArguments: _<{ binding: string }>( + ({ binding }) => `Assigning to '${binding}' in strict mode.`, + ), + StrictEvalArgumentsBinding: _<{ binding: string }>( + ({ binding }) => `Binding '${binding}' in strict mode.`, + ), + StrictFunction: _( + "In strict mode code, functions can only be declared at top level or inside a block.", + ), + StrictNumericEscape: _( + "The only valid numeric escape in strict mode is '\\0'.", + ), + StrictOctalLiteral: _( + "Legacy octal literals are not allowed in strict mode.", + ), StrictWith: _("'with' in strict mode."), -}); +})); diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index a01478be6ce2..3d49bb44e62c 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -3,7 +3,6 @@ import { type Position } from "../util/location"; import { tokenIsLiteralPropertyName, - tokenLabelName, tt, type TokenType, } from "../tokenizer/types"; diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 162d3cd056c5..ce0be520271a 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -127,7 +127,7 @@ const FlowErrors = toParseErrorClasses( suggestion: string, |}>( ({ enumName, memberName, suggestion }) => - `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${enumName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`, + `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${memberName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`, ), EnumNumberMemberNotInitialized: _<{| enumName: string, diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index 56444e5d59eb..892701db10be 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -35,6 +35,11 @@ const JsxErrors = toParseErrorClasses( UnexpectedSequenceExpression: _( "Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?", ), + // FIXME: Unify with Errors.UnexpectedToken + UnexpectedToken: _<{| found: string, HTMLEntity: string |}>( + ({ found, HTMLEntity }) => + `Unexpected token \`${found}\`. Did you mean \`${HTMLEntity}\` or \`{'${found}'}\`?`, + ), UnsupportedJsxValue: _( "JSX value should be either an expression or a quoted JSX text.", ), @@ -42,11 +47,6 @@ const JsxErrors = toParseErrorClasses( UnwrappedAdjacentJSXElements: _( "Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...?", ), - // FIXME: Unify with Errors.UnexpectedToken - UnexpectedToken: _<{| found: string, HTMLEntity: string |}>( - ({ found, HTMLEntity }) => - `Unexpected token \`${found}\`. Did you mean \`${HTMLEntity}\` or \`{'${found}'}\`?`, - ), }), { syntaxPlugin: "jsx" }, ); diff --git a/packages/babel-parser/src/plugins/placeholders.js b/packages/babel-parser/src/plugins/placeholders.js index 14ff8aefd616..cf3723f21cba 100644 --- a/packages/babel-parser/src/plugins/placeholders.js +++ b/packages/babel-parser/src/plugins/placeholders.js @@ -5,7 +5,7 @@ import * as charCodes from "charcodes"; import { tokenLabelName, tt } from "../tokenizer/types"; import type Parser from "../parser"; import * as N from "../types"; -import { Errors, toParseErrorClasses } from "../parse-error"; +import { toParseErrorClasses } from "../parse-error"; export type PlaceholderTypes = | "Identifier" diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 29bd9d7fdc7d..bdfab34d60b4 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -103,7 +103,7 @@ const TSErrors = toParseErrorClasses( "An implementation cannot be declared in ambient contexts.", ), DuplicateAccessibilityModifier: _<{| modifier: N.Accessibility |}>( - ({ modifier }) => "Accessibility modifier ${modifier} already seen.", + ({ modifier }) => `Accessibility modifier ${modifier} already seen.`, ), DuplicateModifier: _<{| modifier: TsModifier |}>( ({ modifier }) => `Duplicate modifier: '${modifier}'.`, diff --git a/packages/babel-parser/src/util/expression-scope.js b/packages/babel-parser/src/util/expression-scope.js index f78caafb641f..36d63f7d618a 100644 --- a/packages/babel-parser/src/util/expression-scope.js +++ b/packages/babel-parser/src/util/expression-scope.js @@ -1,6 +1,6 @@ // @flow -import { Errors, ParseError } from "../parse-error"; +import { Errors } from "../parse-error"; import { Position } from "./location"; import type { Node } from "../types"; import Tokenizer from "../tokenizer"; @@ -19,7 +19,7 @@ ExpressionScope is used to track declaration errors in these ambiguous patterns: e.g. we don't know if `async({ x })` is a call expression or an async arrow function parameters until we see an `=>` after `)` -The following declaration errors (@see parser/error-message) will be recorded in +The following declaration errors (@see parser-errors/standard) will be recorded in some expression scopes and thrown later when we know what the ambigous pattern is - AwaitBindingIdentifier From 7b0b9901df95846ce7c2dfd81600ddaf6f6b6f86 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Thu, 10 Feb 2022 14:24:04 -0800 Subject: [PATCH 009/104] Don't use enums and address more issues. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 13 ++++++----- .../babel-parser/src/parse-error/module.js | 4 ++-- .../babel-parser/src/parse-error/standard.js | 22 +++++++++---------- .../babel-parser/src/parser/expression.js | 2 +- packages/babel-parser/src/parser/statement.js | 6 ++--- packages/babel-parser/src/plugins/estree.js | 4 ++-- .../babel-parser/src/plugins/flow/index.js | 2 +- 7 files changed, 27 insertions(+), 26 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 53ebdf0e7ede..c923a225d6a9 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -5,10 +5,12 @@ import type { NodeBase } from "./types"; const { assign: ObjectAssign } = Object; -export enum ParseErrorCode { - SyntaxError = "BABEL_PARSER_SYNTAX_ERROR", - SourceTypeModuleError = "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", -} +export const ParseErrorCodes = Object.freeze({ + SyntaxError: "BABEL_PARSER_SYNTAX_ERROR", + SourceTypeModuleError: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", +}); + +export type ParseErrorCode = $Values; type ToMessage = (self: ErrorProperties) => string; @@ -18,7 +20,7 @@ export class ParseError extends SyntaxError { name: string = "SyntaxError"; - code: ParseErrorCode = ParseErrorCode.SyntaxError; + code: ParseErrorCode = ParseErrorCodes.SyntaxError; reasonCode: string = this.constructor.reasonCode; loc: Position; @@ -75,7 +77,6 @@ export function toParseErrorClasses( ); } - export type RaiseProperties = {| ...ErrorProperties, at: Position | NodeBase, diff --git a/packages/babel-parser/src/parse-error/module.js b/packages/babel-parser/src/parse-error/module.js index 98817ebac765..d1174b97ccd4 100644 --- a/packages/babel-parser/src/parse-error/module.js +++ b/packages/babel-parser/src/parse-error/module.js @@ -1,4 +1,4 @@ -import { ErrorCodes, toParseErrorClasses } from "../parse-error"; +import { ParseErrorCodes, toParseErrorClasses } from "../parse-error"; export default toParseErrorClasses( _ => ({ @@ -9,5 +9,5 @@ export default toParseErrorClasses( `'import' and 'export' may appear only with 'sourceType: "module"'`, ), }), - { code: ErrorCodes.SourceTypeModuleError }, + { code: ParseErrorCodes.SourceTypeModuleError }, ); diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index b567efbb2a62..43645cc916b4 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -59,17 +59,17 @@ export default toParseErrorClasses(_ => ({ ), DuplicateConstructor: _("Duplicate constructor in the same class."), DuplicateDefaultExport: _("Only one default export allowed per module."), - DuplicateExport: _<{| export: string |}>( - ({ export: name }) => - `\`${name}\` has already been exported. Exported identifiers must be unique.`, + DuplicateExport: _<{| exportedBinding: string |}>( + ({ exportedBinding: name }) => + `\`${exportedBinding}\` 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."), - ExportBindingIsString: _<{ localBinding: string, exportBinding: string }>( - ({ localBinding, exportBinding }) => - `A string literal cannot be used as an exported binding without \`from\`.\n- Did you mean \`export { '${localBinding}' as '${exportBinding}' } from 'some-module'?\``, + ExportBindingIsString: _<{ localBinding: string, exportedBinding: string }>( + ({ localBinding, exportedBinding }) => + `A string literal cannot be used as an exported binding without \`from\`.\n- Did you mean \`export { '${localBinding}' as '${exportedBinding}' } from 'some-module'?\``, ), ExportDefaultFromAsIdentifier: _( "'from' is not allowed as an identifier after 'export default'.", @@ -94,9 +94,9 @@ export default toParseErrorClasses(_ => ({ "Illegal 'use strict' directive in function with non-simple parameter list.", ), IllegalReturn: _("'return' outside of function."), - ImportBindingIsString: _<{ importBinding: string }>( - ({ importBinding }) => - `A string literal cannot be used as an imported binding.\n- Did you mean \`import { "${importBinding}" as foo }\`?`, + ImportBindingIsString: _<{ importedBinding: string }>( + ({ importedBinding }) => + `A string literal cannot be used as an imported binding.\n- Did you mean \`import { "${importedBinding}" as foo }\`?`, ), ImportCallArgumentTrailingComma: _( "Trailing comma is disallowed inside import(...) arguments.", @@ -121,8 +121,8 @@ export default toParseErrorClasses(_ => ({ ), InvalidEscapeSequence: _("Bad character escape sequence."), InvalidEscapeSequenceTemplate: _("Invalid escape sequence in template."), - InvalidEscapedReservedWord: _<{ keyword: string }>( - ({ keyword }) => `Escape sequence in keyword ${keyword}.`, + InvalidEscapedReservedWord: _<{ reservedWord: string }>( + ({ reservedWord }) => `Escape sequence in keyword ${reservedWord}.`, ), InvalidIdentifier: _<{ identifier: string }>( ({ identifier }) => `Invalid identifier ${identifier}.`, diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 2174fc66dd62..4d494e12d272 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -2459,8 +2459,8 @@ export default class ExpressionParser extends LValParser { if (hasStrictModeDirective && nonSimple) { // This logic is here to align the error location with the ESTree plugin. this.raise(Errors.IllegalLanguageModeDirective, { - // $FlowIgnore at: + // $FlowIgnore (node.kind === "method" || node.kind === "constructor") && // $FlowIgnore !!node.key diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 9bd7027c2567..c3a650450861 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -1228,7 +1228,7 @@ export default class StatementParser extends ExpressionParser { if (!isTypescript) { this.raise(Errors.DeclarationMissingInitializer, { at: this.state.lastTokEndLoc, - contextDescription: "Const declarations", + declaration: "Const declarations", }); } } else if ( @@ -1237,7 +1237,7 @@ export default class StatementParser extends ExpressionParser { ) { this.raise(Errors.DeclarationMissingInitializer, { at: this.state.lastTokEndLoc, - contextDescription: "Complex binding patterns", + declaration: "Complex binding patterns", }); } decl.init = null; @@ -2539,7 +2539,7 @@ export default class StatementParser extends ExpressionParser { if (node.key.name !== "type") { this.raise(Errors.ModuleAttributeDifferentFromType, { at: node.key, - key: node.key.name, +// key: node.key.name, }); } diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index 725babe64a76..5195b35aabde 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -368,9 +368,9 @@ export default (superClass: Class): Class => toAssignableObjectExpressionProp(prop: N.Node, ...args) { if (prop.kind === "get" || prop.kind === "set") { - this.raise(Errors.PatternHasAccessor, { node: prop.key }); + this.raise(Errors.PatternHasAccessor, { at: prop.key }); } else if (prop.method) { - this.raise(Errors.PatternHasMethod, { node: prop.key }); + this.raise(Errors.PatternHasMethod, { at: prop.key }); } else { super.toAssignableObjectExpressionProp(prop, ...args); } diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index ce0be520271a..f1bde9c8f736 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -2762,7 +2762,7 @@ export default (superClass: Class): Class => /*:: invariant(firstIdent instanceof N.StringLiteral) */ throw this.raise(Errors.ImportBindingIsString, { at: specifier, - importBinding: firstIdent.value, + importedBinding: firstIdent.value, }); } /*:: invariant(firstIdent instanceof N.Node) */ From fbd4765a2d6290a7391c3c63d9f29781b79a5d3e Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Thu, 10 Feb 2022 15:54:08 -0800 Subject: [PATCH 010/104] More fixes. Reviewed by @tolmasky. --- .../babel-parser/src/parse-error/standard.js | 74 +++++++++---------- .../src/parse-error/strict-mode.js | 4 +- .../babel-parser/src/parser/expression.js | 4 +- packages/babel-parser/src/parser/lval.js | 2 +- packages/babel-parser/src/parser/statement.js | 13 ++-- .../src/plugins/typescript/index.js | 2 +- 6 files changed, 49 insertions(+), 50 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index 43645cc916b4..a00e9a0ad3ce 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -4,7 +4,7 @@ import { Position } from "../util/location"; import { toParseErrorClasses } from "../parse-error"; export default toParseErrorClasses(_ => ({ - AccessorIsGenerator: _<{ accessor: string }>( + AccessorIsGenerator: _<{| accessor: string |}>( ({ accessor }) => `A ${accessor} cannot be a generator`, ), @@ -39,7 +39,7 @@ export default toParseErrorClasses(_ => ({ ConstructorIsAccessor: _("Class constructor may not be an accessor."), ConstructorIsAsync: _("Constructor can't be an async function."), ConstructorIsGenerator: _("Constructor can't be a generator."), - DeclarationMissingInitializer: _<{ declaration: string }>( + DeclarationMissingInitializer: _<{| declaration: string |}>( ({ declaration }) => `${declaration}' require an initialization value.`, ), DecoratorBeforeExport: _( @@ -67,7 +67,7 @@ export default toParseErrorClasses(_ => ({ DuplicateRegExpFlags: _("Duplicate regular expression flag."), ElementAfterRest: _("Rest element must be last element."), EscapedCharNotAnIdentifier: _("Invalid Unicode escape."), - ExportBindingIsString: _<{ localBinding: string, exportedBinding: string }>( + ExportBindingIsString: _<{| localBinding: string, exportedBinding: string |}>( ({ localBinding, exportedBinding }) => `A string literal cannot be used as an exported binding without \`from\`.\n- Did you mean \`export { '${localBinding}' as '${exportedBinding}' } from 'some-module'?\``, ), @@ -75,7 +75,7 @@ export default toParseErrorClasses(_ => ({ "'from' is not allowed as an identifier after 'export default'.", ), - ForInOfLoopInitializer: _<{ construct: "for-in" | "for-of" }>( + ForInOfLoopInitializer: _<{| construct: "for-in" | "for-of" |}>( ({ construct }) => `'${construct}' loop variable declaration may not have an initializer.`, ), @@ -86,7 +86,7 @@ export default toParseErrorClasses(_ => ({ "Generators can only be declared at the top level or inside a block.", ), - IllegalBreakContinue: _<{ construct: "break" | "continue" }>( + IllegalBreakContinue: _<{| construct: "break" | "continue" |}>( ({ construct }) => `Unsyntactic ${construct}.`, ), @@ -94,14 +94,14 @@ export default toParseErrorClasses(_ => ({ "Illegal 'use strict' directive in function with non-simple parameter list.", ), IllegalReturn: _("'return' outside of function."), - ImportBindingIsString: _<{ importedBinding: string }>( + ImportBindingIsString: _<{| importedBinding: string |}>( ({ importedBinding }) => `A string literal cannot be used as an imported binding.\n- Did you mean \`import { "${importedBinding}" as foo }\`?`, ), ImportCallArgumentTrailingComma: _( "Trailing comma is disallowed inside import(...) arguments.", ), - ImportCallArity: _<{ required: 1 | 2 }>( + ImportCallArity: _<{| required: 1 | 2 |}>( ({ required }) => `\`import()\` requires exactly ${ required === 1 ? "one argument" : "one or two arguments" @@ -116,36 +116,36 @@ export default toParseErrorClasses(_ => ({ InvalidCodePoint: _("Code point out of bounds."), InvalidCoverInitializedName: _("Invalid shorthand property initializer."), InvalidDecimal: _("Invalid decimal."), - InvalidDigit: _<{ radix: number }>( + InvalidDigit: _<{| radix: number |}>( ({ radix }) => `Expected number in radix ${radix}.`, ), InvalidEscapeSequence: _("Bad character escape sequence."), InvalidEscapeSequenceTemplate: _("Invalid escape sequence in template."), - InvalidEscapedReservedWord: _<{ reservedWord: string }>( + InvalidEscapedReservedWord: _<{| reservedWord: string |}>( ({ reservedWord }) => `Escape sequence in keyword ${reservedWord}.`, ), - InvalidIdentifier: _<{ identifier: string }>( + InvalidIdentifier: _<{| identifier: string |}>( ({ identifier }) => `Invalid identifier ${identifier}.`, ), - InvalidLhs: _<{ contextDescription: string }>( - ({ contextDescription }) => - `Invalid left-hand side in ${contextDescription}.`, + InvalidLhs: _<{| construct: string /*"for-loop"*/ |}>( + ({ construct }) => + `Invalid left-hand side in ${construct}.`, ), - InvalidLhsBinding: _<{ contextDescription: string }>( - ({ contextDescription }) => - `Binding invalid left-hand side in ${contextDescription}.`, + InvalidLhsBinding: _<{| construct: string |}>( + ({ construct }) => + `Binding invalid left-hand side in ${construct}.`, ), InvalidNumber: _("Invalid number."), InvalidOrMissingExponent: _( "Floating-point numbers require a valid exponent after the 'e'.", ), - InvalidOrUnexpectedToken: _<{ found: string }>( + InvalidOrUnexpectedToken: _<{| found: string |}>( ({ found }) => `Unexpected character '${found}'.`, ), InvalidParenthesizedAssignment: _( "Invalid parenthesized assignment pattern.", ), - InvalidPrivateFieldResolution: _<{ name: string }>( + InvalidPrivateFieldResolution: _<{| name: string |}>( ({ name }) => `Private name #${name} is not defined.`, ), InvalidPropertyBindingPattern: _("Binding member expression."), @@ -153,7 +153,7 @@ export default toParseErrorClasses(_ => ({ "Only properties and spread elements are allowed in record definitions.", ), InvalidRestAssignmentPattern: _("Invalid rest operator's argument."), - LabelRedeclaration: _<{ label: string }>( + LabelRedeclaration: _<{| label: string |}>( ({ label }) => `Label '${label}' is already declared.`, ), LetInLexicalBinding: _( @@ -166,13 +166,13 @@ export default toParseErrorClasses(_ => ({ "Only '=' operator can be used for specifying default value.", ), MissingSemicolon: _("Missing semicolon."), - MissingPlugin: _<{ missingPlugin: string }>( + MissingPlugin: _<{| missingPlugin: string |}>( ({ missingPlugin }) => `This experimental syntax requires enabling the parser plugin: "${missingPlugin}".`, ), // FIXME: Would be nice to make this "missingPlugins" instead. // Also), seems like we can drop the "(s)" from the message and just make it "s". - MissingOneOfPlugins: _<{ missingPlugin: string[] }>( + MissingOneOfPlugins: _<{| missingPlugin: string[] |}>( ({ missingPlugin }) => `This experimental syntax requires enabling one of the following parser plugin(s): ${missingPlugin .map(name => JSON.stringify(name)) @@ -188,17 +188,17 @@ export default toParseErrorClasses(_ => ({ ModuleAttributeInvalidValue: _( "Only string literals are allowed as module attribute values.", ), - ModuleAttributesWithDuplicateKeys: _<{ key: string }>( + ModuleAttributesWithDuplicateKeys: _<{| key: string |}>( ({ key }) => `Duplicate key "${key}" is not allowed in module attributes.`, ), - ModuleExportNameHasLoneSurrogate: _<{ surrogateCharCode: number }>( + ModuleExportNameHasLoneSurrogate: _<{| surrogateCharCode: number |}>( ({ surrogateCharCode }) => `An export name cannot include a lone surrogate), found '\\u${surrogateCharCode.toString( 16, )}'.`, ), - ModuleExportUndefined: _<{ moduleExportName: string }>( - ({ moduleExportName }) => `Export '${moduleExportName}' is not defined.`, + ModuleExportUndefined: _<{| exportedBinding: string |}>( + ({ exportedBinding }) => `Export '${exportedBinding}' is not defined.`, ), MultipleDefaultsInSwitch: _("Multiple default clauses."), NewlineAfterThrow: _("Illegal newline after throw."), @@ -223,7 +223,7 @@ export default toParseErrorClasses(_ => ({ PatternHasAccessor: _("Object pattern can't contain getter or setter."), PatternHasMethod: _("Object pattern can't contain methods."), // This error is only used by the smart-mix proposal - PipeBodyIsTighter: _<{ expressionDescription: string }>( + PipeBodyIsTighter: _<{| expressionDescription: string |}>( ({ expressionDescription }) => `Unexpected ${expressionDescription} after pipeline body; any ${expressionDescription} expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence.`, ), @@ -233,14 +233,14 @@ export default toParseErrorClasses(_ => ({ PipeTopicUnbound: _( "Topic reference is unbound; it must be inside a pipe body.", ), - PipeTopicUnconfiguredToken: _<{ token: string }>( + PipeTopicUnconfiguredToken: _<{| token: string |}>( ({ token }) => `Invalid topic token ${token}. In order to use ${token} as a topic reference), the pipelineOperator plugin must be configured with { "proposal": _("hack"), "topicToken": _("${token}" }.`, ), PipeTopicUnused: _( "Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once.", ), - PipeUnparenthesizedBody: _<{ expressionDescription: string }>( + PipeUnparenthesizedBody: _<{| expressionDescription: string |}>( ({ expressionDescription }) => `Hack-style pipe body cannot be an unparenthesized ${expressionDescription} expression; please wrap it in parentheses.`, ), @@ -268,11 +268,11 @@ export default toParseErrorClasses(_ => ({ 'Topic reference is used), but the pipelineOperator plugin was not passed a "proposal": _("hack" or "smart" option.', ), - PrivateInExpectedIn: _<{ name: string }>( + PrivateInExpectedIn: _<{| name: string |}>( ({ name }) => `Private names are only allowed in property accesses (\`obj.#${name}\`) or in \`in\` expressions (\`#${name} in obj\`).`, ), - PrivateNameRedeclaration: _<{ name: string }>( + PrivateNameRedeclaration: _<{| name: string |}>( ({ name }) => `Duplicate private name #${name}.`, ), RecordExpressionBarIncorrectEndSyntaxType: _( @@ -312,7 +312,7 @@ export default toParseErrorClasses(_ => ({ UnexpectedImportExport: _( "'import' and 'export' may only appear at the top level.", ), - UnexpectedKeyword: _<{ keyword: string }>( + UnexpectedKeyword: _<{| keyword: string |}>( ({ keyword }) => `Unexpected keyword '${keyword}'.`, ), UnexpectedLeadingDecorator: _( @@ -328,15 +328,15 @@ export default toParseErrorClasses(_ => ({ "A numeric separator is only allowed between two digits.", ), UnexpectedPrivateField: _("Unexpected private name."), - UnexpectedReservedWord: _<{ reservedWord: string }>( + UnexpectedReservedWord: _<{| reservedWord: string |}>( ({ reservedWord }) => `Unexpected reserved word '${reservedWord}'.`, ), UnexpectedSuper: _("'super' is only allowed in object methods and classes."), - UnexpectedToken: _<{ + UnexpectedToken: _<{| loc: Position, - expected?: string, + expected?: ?string, /* eslint-disable no-confusing-arrow */ - }>(({ loc: { line, column }, expected }) => + |}>(({ loc: { line, column }, expected }) => expected ? `Unexpected token, expected "${expected} (${line}:${column})"` : "Unexpected token", @@ -354,7 +354,7 @@ export default toParseErrorClasses(_ => ({ UnsupportedImport: _( "`import` can only be used in `import()` or `import.meta`.", ), - UnsupportedMetaProperty: _<{ target: string, onlyValidProperty: string }>( + UnsupportedMetaProperty: _<{| target: string, onlyValidProperty: string |}>( ({ target, onlyValidProperty }) => `The only valid meta property for ${target} is ${target}.${onlyValidProperty}.`, ), @@ -371,7 +371,7 @@ export default toParseErrorClasses(_ => ({ UnterminatedRegExp: _("Unterminated regular expression."), UnterminatedString: _("Unterminated string constant."), UnterminatedTemplate: _("Unterminated template."), - VarRedeclaration: _<{ name: string }>( + VarRedeclaration: _<{| name: string |}>( ({ name }) => `Identifier '${name}' has already been declared.`, ), YieldBindingIdentifier: _( diff --git a/packages/babel-parser/src/parse-error/strict-mode.js b/packages/babel-parser/src/parse-error/strict-mode.js index 103357997c48..82c1469c512d 100644 --- a/packages/babel-parser/src/parse-error/strict-mode.js +++ b/packages/babel-parser/src/parse-error/strict-mode.js @@ -4,10 +4,10 @@ import { toParseErrorClasses } from "../parse-error"; export default toParseErrorClasses(_ => ({ StrictDelete: _("Deleting local variable in strict mode."), - StrictEvalArguments: _<{ binding: string }>( + StrictEvalArguments: _<{| binding: string |}>( ({ binding }) => `Assigning to '${binding}' in strict mode.`, ), - StrictEvalArgumentsBinding: _<{ binding: string }>( + StrictEvalArgumentsBinding: _<{| binding: string |}>( ({ binding }) => `Binding '${binding}' in strict mode.`, ), StrictFunction: _( diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 4d494e12d272..f3e53e1c83e9 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -1587,7 +1587,7 @@ export default class ExpressionParser extends LValParser { this.raise(Errors.UnsupportedMetaProperty, { at: node.property, target: meta.name, - property: propertyName, + onlyValidProperty: propertyName, }); } @@ -2572,7 +2572,7 @@ export default class ExpressionParser extends LValParser { if (!allowEmpty) { this.raise(Errors.UnexpectedToken, { at: this.state.curPosition(), - found: ",", +// found: ",", }); } elt = null; diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 6c0ad7850173..6747800ef440 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -622,7 +622,7 @@ export default class LValParser extends NodeUtils { bindingType === BIND_NONE ? Errors.InvalidLhs : Errors.InvalidLhsBinding, - { at: expr, contextDescription }, + { at: expr, construct: contextDescription }, ); } } diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index c3a650450861..189cbde5eadd 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -220,7 +220,7 @@ export default class StatementParser extends ExpressionParser { for (const [name, loc] of Array.from(this.scope.undefinedExports)) { this.raise(Errors.ModuleExportUndefined, { at: loc, - exportBinding: name, + exportedBinding: name, }); } } @@ -2325,12 +2325,11 @@ export default class StatementParser extends ExpressionParser { name: string, ): void { if (this.exportedIdentifiers.has(name)) { - this.raise( - name === "default" - ? Errors.DuplicateDefaultExport - : Errors.DuplicateExport, - { at: node, name }, - ); + if (name === "default") { + this.raise(Errors.DuplicateDefaultExport, { at: node }); + } else { + this.raise(Errors.DuplicateExport, { at: node, exportedBinding: name }); + } } this.exportedIdentifiers.add(name); } diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index bdfab34d60b4..6ee20ebdb9d8 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3226,7 +3226,7 @@ export default (superClass: Class): Class => contextDescription !== "parenthesized expression" && !expr.extra?.parenthesized ) { - this.raise(Errors.InvalidLhs, { at: expr, contextDescription }); + this.raise(Errors.InvalidLhs, { at: expr, construct: contextDescription }); break; } this.checkLVal(expr.expression, "parenthesized expression", ...args); From 5725a1712f1396911b851e241fd637f1ce497613 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 11 Feb 2022 07:06:04 -0800 Subject: [PATCH 011/104] Workaround for import situation. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 27 ++++++++++++++++--- .../babel-parser/src/parse-error/module.js | 2 +- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index c923a225d6a9..eaf6b64a840c 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -12,6 +12,8 @@ export const ParseErrorCodes = Object.freeze({ export type ParseErrorCode = $Values; +var instantiated = false; + type ToMessage = (self: ErrorProperties) => string; export class ParseError extends SyntaxError { @@ -56,7 +58,9 @@ function toReasonCodelessParseErrorClass(toMessageOrMessage) { function fromToMessage(toMessage: ToMessage): Class> { return class extends ParseError { - static toMessage = toMessage; + get message() { + return toMessage(this); + } }; } @@ -65,15 +69,28 @@ export function toParseErrorClasses( { syntaxPlugin, code } : Object = {} ): T { // $FlowIgnore + if (!instantiated) { + const classes = { + _instantiate() { + delete classes._instantiate, + Object.assign(classes, toParseErrorClasses(toClasses, { syntaxPlugin, code })); + return classes; + } + }; + + return classes; + } + return Object.fromEntries( Object.entries(toClasses(toReasonCodelessParseErrorClass)).map( - ([reasonCode, ParseErrorClass]) => + ([reasonCode, ParseErrorClass]) => [ + reasonCode, // $FlowIgnore Object.assign(ParseErrorClass, { reasonCode }, !!syntaxPlugin && { syntaxPlugin }, !!code && { code }) - ) + ]) ); } @@ -87,4 +104,6 @@ export { default as ModuleErrors } from "./parse-error/module"; import StandardErrors from "./parse-error/standard"; import StrictErrors from "./parse-error/strict-mode"; -export const Errors = { ...StandardErrors, ...StrictErrors }; +instantiated = true; + +export const Errors = { ...StandardErrors._instantiate(), ...StrictErrors._instantiate() }; diff --git a/packages/babel-parser/src/parse-error/module.js b/packages/babel-parser/src/parse-error/module.js index d1174b97ccd4..83a169713355 100644 --- a/packages/babel-parser/src/parse-error/module.js +++ b/packages/babel-parser/src/parse-error/module.js @@ -9,5 +9,5 @@ export default toParseErrorClasses( `'import' and 'export' may appear only with 'sourceType: "module"'`, ), }), - { code: ParseErrorCodes.SourceTypeModuleError }, + //{ code: ParseErrorCodes.SourceTypeModuleError }, ); From 9c1fe56714d9fbf58256bab0f01c8452e128f7b0 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 11 Feb 2022 18:39:29 -0800 Subject: [PATCH 012/104] Fix a bunch of errors messages. Reviewed by @tolmasky. --- .../src/index.cjs | 2 - .../src/rules/dry-error-messages.cjs | 169 ------------------ packages/babel-parser/src/parse-error.js | 17 +- .../babel-parser/src/parse-error/module.js | 2 + .../babel-parser/src/parse-error/standard.js | 37 ++-- .../babel-parser/src/parser/expression.js | 2 +- .../babel-parser/src/plugins/flow/index.js | 8 +- .../src/plugins/typescript/index.js | 4 +- packages/babel-parser/src/tokenizer/index.js | 2 +- 9 files changed, 42 insertions(+), 201 deletions(-) delete mode 100644 eslint/babel-eslint-plugin-development-internal/src/rules/dry-error-messages.cjs diff --git a/eslint/babel-eslint-plugin-development-internal/src/index.cjs b/eslint/babel-eslint-plugin-development-internal/src/index.cjs index 107f4696ae5f..b7b23f2a24b7 100644 --- a/eslint/babel-eslint-plugin-development-internal/src/index.cjs +++ b/eslint/babel-eslint-plugin-development-internal/src/index.cjs @@ -1,8 +1,6 @@ -const dryErrorMessages = require("./rules/dry-error-messages.cjs"); const reportErrorMessageFormat = require("./rules/report-error-message-format.cjs"); const rules = { - "dry-error-messages": dryErrorMessages, "report-error-message-format": reportErrorMessageFormat, }; diff --git a/eslint/babel-eslint-plugin-development-internal/src/rules/dry-error-messages.cjs b/eslint/babel-eslint-plugin-development-internal/src/rules/dry-error-messages.cjs deleted file mode 100644 index a3e28036208f..000000000000 --- a/eslint/babel-eslint-plugin-development-internal/src/rules/dry-error-messages.cjs +++ /dev/null @@ -1,169 +0,0 @@ -const path = require("path"); - -const REL_PATH_REGEX = /^\.{1,2}/; - -function isRelativePath(filePath) { - return REL_PATH_REGEX.test(filePath); -} - -function resolveAbsolutePath(currentFilePath, moduleToResolve) { - return isRelativePath(moduleToResolve) - ? path.resolve(path.dirname(currentFilePath), moduleToResolve) - : moduleToResolve; -} - -function isSourceErrorModule(currentFilePath, targetModulePath, src) { - for (const srcPath of [src, `${src}.js`, `${src}/index`, `${src}/index.js`]) { - if ( - path.normalize(resolveAbsolutePath(currentFilePath, targetModulePath)) === - path.normalize(resolveAbsolutePath(currentFilePath, srcPath)) - ) { - return true; - } - } - - return false; -} - -function isCurrentFileErrorModule(currentFilePath, errorModule) { - return currentFilePath === errorModule; -} - -function findIdNode(node) { - if (node.type === "Identifier") { - return node; - } - - if (node.type === "MemberExpression" && node.object.type === "Identifier") { - return node.object; - } - - return null; -} - -function findIdNodes(node) { - if (node.type === "ConditionalExpression") { - const consequent = findIdNode(node.consequent); - const alternate = findIdNode(node.alternate); - - if (consequent && alternate) { - return [consequent, alternate]; - } - } - - const idNode = findIdNode(node); - - if (idNode) { - return [idNode]; - } - - return null; -} - -function findReference(node, scope) { - let currentScope = scope; - - while (currentScope) { - const ref = currentScope.set.get(node.name); - - if (ref) { - return ref; - } - - currentScope = currentScope.upper; - } - - return null; -} - -function referencesImportedBinding(node, scope, bindings) { - const ref = findReference(node, scope); - - if (ref) { - const topLevelDef = ref.defs[0]; - - if (topLevelDef.type === "ImportBinding") { - const defNode = topLevelDef.node; - - for (const spec of bindings) { - if ( - spec.loc.start === defNode.loc.start && - spec.loc.end === defNode.loc.end - ) { - return true; - } - } - } - } - - return false; -} - -module.exports = { - meta: { - type: "suggestion", - docs: { - description: - "enforce @babel/parser's error messages to be consolidated in one module", - }, - schema: [ - { - type: "object", - properties: { - errorModule: { type: "string" }, - }, - additionalProperties: false, - required: ["errorModule"], - }, - ], - messages: { - mustBeImported: 'Error messages must be imported from "{{errorModule}}".', - }, - }, - create({ options, report, getFilename, getScope }) { - const [{ errorModule = "" } = {}] = options; - const filename = getFilename(); - const importedBindings = new Set(); - - if ( - // Do not run check if errorModule config option is not given. - !errorModule.length || - // Do not check the target error module file. - isCurrentFileErrorModule(filename, errorModule) - ) { - return {}; - } - - return { - // Check imports up front so that we don't have to check them for every ThrowStatement. - ImportDeclaration(node) { - if (isSourceErrorModule(filename, errorModule, node.source.value)) { - for (const spec of node.specifiers) { - importedBindings.add(spec); - } - } - }, - "CallExpression[callee.type='MemberExpression'][callee.object.type='ThisExpression'][callee.property.name='raise'][arguments.length>=2]"( - node, - ) { - const [errorMsgNode] = node.arguments; - const nodesToCheck = findIdNodes(errorMsgNode); - - if ( - Array.isArray(nodesToCheck) && - nodesToCheck.every(node => - referencesImportedBinding(node, getScope(), importedBindings), - ) - ) { - return; - } - - report({ - node: errorMsgNode, - messageId: "mustBeImported", - data: { errorModule }, - }); - }, - }; - }, -}; diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index eaf6b64a840c..ba8f8a9b8e4a 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -5,6 +5,8 @@ import type { NodeBase } from "./types"; const { assign: ObjectAssign } = Object; +const ManuallyAssignedErrorMessages = new WeakMap(); + export const ParseErrorCodes = Object.freeze({ SyntaxError: "BABEL_PARSER_SYNTAX_ERROR", SourceTypeModuleError: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", @@ -20,7 +22,7 @@ export class ParseError extends SyntaxError { static reasonCode: string; static toMessage: ToMessage; - name: string = "SyntaxError"; +// static name: string = "SyntaxError"; code: ParseErrorCode = ParseErrorCodes.SyntaxError; reasonCode: string = this.constructor.reasonCode; @@ -41,6 +43,8 @@ export class ParseError extends SyntaxError { } } +Object.defineProperty(ParseError, "name", { value: "SyntaxError" }); + declare function toReasonCodelessParseErrorClass( T ): Class>; @@ -59,7 +63,12 @@ function toReasonCodelessParseErrorClass(toMessageOrMessage) { function fromToMessage(toMessage: ToMessage): Class> { return class extends ParseError { get message() { - return toMessage(this); + return ManuallyAssignedErrorMessages.has(this) + ? ManuallyAssignedErrorMessages.get(this) + : `${toMessage(this)} (${this.loc.line}:${this.loc.column})`; + } + set message(message) { + ManuallyAssignedErrorMessages.set(this, message); } }; } @@ -99,11 +108,13 @@ export type RaiseProperties = {| at: Position | NodeBase, |}; -export { default as ModuleErrors } from "./parse-error/module"; +import { default as ModuleErrorsUninstantiated } from "./parse-error/module"; import StandardErrors from "./parse-error/standard"; import StrictErrors from "./parse-error/strict-mode"; instantiated = true; +export const ModuleErrors = ModuleErrorsUninstantiated._instantiate(); + export const Errors = { ...StandardErrors._instantiate(), ...StrictErrors._instantiate() }; diff --git a/packages/babel-parser/src/parse-error/module.js b/packages/babel-parser/src/parse-error/module.js index 83a169713355..a36166e8bd65 100644 --- a/packages/babel-parser/src/parse-error/module.js +++ b/packages/babel-parser/src/parse-error/module.js @@ -1,3 +1,5 @@ +// @flow + import { ParseErrorCodes, toParseErrorClasses } from "../parse-error"; export default toParseErrorClasses( diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index a00e9a0ad3ce..7056c85e05fb 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -5,7 +5,7 @@ import { toParseErrorClasses } from "../parse-error"; export default toParseErrorClasses(_ => ({ AccessorIsGenerator: _<{| accessor: string |}>( - ({ accessor }) => `A ${accessor} cannot be a generator`, + ({ accessor }) => `A ${accessor}ter cannot be a generator.`, ), ArgumentsInClass: _( @@ -40,7 +40,7 @@ export default toParseErrorClasses(_ => ({ ConstructorIsAsync: _("Constructor can't be an async function."), ConstructorIsGenerator: _("Constructor can't be a generator."), DeclarationMissingInitializer: _<{| declaration: string |}>( - ({ declaration }) => `${declaration}' require an initialization value.`, + ({ declaration }) => `'${declaration}' 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.", @@ -60,7 +60,7 @@ export default toParseErrorClasses(_ => ({ DuplicateConstructor: _("Duplicate constructor in the same class."), DuplicateDefaultExport: _("Only one default export allowed per module."), DuplicateExport: _<{| exportedBinding: string |}>( - ({ exportedBinding: name }) => + ({ exportedBinding }) => `\`${exportedBinding}\` has already been exported. Exported identifiers must be unique.`, ), DuplicateProto: _("Redefinition of __proto__ property."), @@ -69,7 +69,7 @@ export default toParseErrorClasses(_ => ({ EscapedCharNotAnIdentifier: _("Invalid Unicode escape."), ExportBindingIsString: _<{| localBinding: string, exportedBinding: string |}>( ({ localBinding, exportedBinding }) => - `A string literal cannot be used as an exported binding without \`from\`.\n- Did you mean \`export { '${localBinding}' as '${exportedBinding}' } from 'some-module'?\``, + `A string literal cannot be used as an exported binding without \`from\`.\n- Did you mean \`export { '${localBinding}' as '${exportedBinding}' } from 'some-module'\`?`, ), ExportDefaultFromAsIdentifier: _( "'from' is not allowed as an identifier after 'export default'.", @@ -166,9 +166,11 @@ export default toParseErrorClasses(_ => ({ "Only '=' operator can be used for specifying default value.", ), MissingSemicolon: _("Missing semicolon."), - MissingPlugin: _<{| missingPlugin: string |}>( + MissingPlugin: _<{| missingPlugin: [string] |}>( ({ missingPlugin }) => - `This experimental syntax requires enabling the parser plugin: "${missingPlugin}".`, + `This experimental syntax requires enabling the parser plugin: ${missingPlugin + .map(name => JSON.stringify(name)) + .join(", ")}.`, ), // FIXME: Would be nice to make this "missingPlugins" instead. // Also), seems like we can drop the "(s)" from the message and just make it "s". @@ -176,7 +178,7 @@ export default toParseErrorClasses(_ => ({ ({ missingPlugin }) => `This experimental syntax requires enabling one of the following parser plugin(s): ${missingPlugin .map(name => JSON.stringify(name)) - .join("), ")}.`, + .join(", ")}.`, ), MissingUnicodeEscape: _("Expecting Unicode escape sequence \\uXXXX."), MixingCoalesceWithLogical: _( @@ -193,7 +195,7 @@ export default toParseErrorClasses(_ => ({ ), ModuleExportNameHasLoneSurrogate: _<{| surrogateCharCode: number |}>( ({ surrogateCharCode }) => - `An export name cannot include a lone surrogate), found '\\u${surrogateCharCode.toString( + `An export name cannot include a lone surrogate, found '\\u${surrogateCharCode.toString( 16, )}'.`, ), @@ -228,14 +230,14 @@ export default toParseErrorClasses(_ => ({ `Unexpected ${expressionDescription} after pipeline body; any ${expressionDescription} expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence.`, ), PipeTopicRequiresHackPipes: _( - 'Topic reference is used), but the pipelineOperator plugin was not passed a "proposal": _("hack" or "smart" option.', + 'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" or "smart" option.', ), PipeTopicUnbound: _( "Topic reference is unbound; it must be inside a pipe body.", ), PipeTopicUnconfiguredToken: _<{| token: string |}>( ({ token }) => - `Invalid topic token ${token}. In order to use ${token} as a topic reference), the pipelineOperator plugin must be configured with { "proposal": _("hack"), "topicToken": _("${token}" }.`, + `Invalid topic token ${token}. In order to use ${token} as a topic reference, the pipelineOperator plugin must be configured with { "proposal": "hack", "topicToken": "${token}" }.`, ), PipeTopicUnused: _( "Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once.", @@ -265,7 +267,7 @@ export default toParseErrorClasses(_ => ({ "Topic reference was used in a lexical context without topic binding.", ), PrimaryTopicRequiresSmartPipeline: _( - 'Topic reference is used), but the pipelineOperator plugin was not passed a "proposal": _("hack" or "smart" option.', + 'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" or "smart" option.', ), PrivateInExpectedIn: _<{| name: string |}>( @@ -287,7 +289,7 @@ export default toParseErrorClasses(_ => ({ RecordNoProto: _("'__proto__' is not allowed in Record expressions."), 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.", + "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."), SuperNotAllowed: _( @@ -333,13 +335,10 @@ export default toParseErrorClasses(_ => ({ ), UnexpectedSuper: _("'super' is only allowed in object methods and classes."), UnexpectedToken: _<{| - loc: Position, expected?: ?string, - /* eslint-disable no-confusing-arrow */ - |}>(({ loc: { line, column }, expected }) => - expected - ? `Unexpected token, expected "${expected} (${line}:${column})"` - : "Unexpected token", + unexpected?: ?string, + |}>(({ expected, unexpected }) => + `Unexpected token${ unexpected ? ` '${unexpected}'.` : ""}${ expected ? `, expected "${expected}"` : ""}` ), UnexpectedTokenUnaryExponentiation: _( "Illegal expression. Wrap left hand side or entire exponentiation in parentheses.", @@ -349,7 +348,7 @@ export default toParseErrorClasses(_ => ({ "A decorated export must export a class declaration.", ), UnsupportedDefaultExport: _( - "Only expressions), functions or classes are allowed as the `default` export.", + "Only expressions, functions or classes are allowed as the `default` export.", ), UnsupportedImport: _( "`import` can only be used in `import()` or `import.meta`.", diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index f3e53e1c83e9..c20e288d836a 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -2572,7 +2572,7 @@ export default class ExpressionParser extends LValParser { if (!allowEmpty) { this.raise(Errors.UnexpectedToken, { at: this.state.curPosition(), -// found: ",", + unexpected: ",", }); } elt = null; diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index f1bde9c8f736..cdb8edd3b6df 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -116,6 +116,7 @@ const FlowErrors = toParseErrorClasses( EnumInvalidMemberInitializerUnknownType: _<{| enumName: string, memberName: string, + // eslint-disable-next-line no-unused-vars explicitType: string, |}>( ({ enumName, memberName }) => @@ -3341,9 +3342,7 @@ export default (superClass: Class): Class => { enumName, explicitType, memberName }: EnumContext, ) { return this.raise( - explicitType === "boolean" || - explicitType === "number" || - explicitType === "string" + /^(boolean|number|string)$/.test(explicitType) ? FlowErrors.EnumInvalidMemberInitializerPrimaryType : explicitType === "symbol" ? FlowErrors.EnumInvalidMemberInitializerSymbolType @@ -3352,8 +3351,7 @@ export default (superClass: Class): Class => at: loc, enumName, memberName, - // FIXME: ? - explicitType: "hi" /*explicitType || "unknown"*/, + explicitType }, ); } diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 6ee20ebdb9d8..84ff9be33963 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -103,7 +103,9 @@ const TSErrors = toParseErrorClasses( "An implementation cannot be declared in ambient contexts.", ), DuplicateAccessibilityModifier: _<{| modifier: N.Accessibility |}>( - ({ modifier }) => `Accessibility modifier ${modifier} already seen.`, + // `Accessibility modifier ${modifier} already seen.` would be more helpful. + // eslint-disable-line no-unused-vars + ({ modifier }) => `Accessibility modifier already seen.`, ), DuplicateModifier: _<{| modifier: TsModifier |}>( ({ modifier }) => `Duplicate modifier: '${modifier}'.`, diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 57a91692f32c..cca525b38d52 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -1813,7 +1813,7 @@ export default class Tokenizer extends CommentsParser { throw this.raise(Errors.MissingPlugin, { at: loc != null ? loc : this.state.startLoc, - missingPlugin: pluginName, + missingPlugin: [pluginName], }); } From 193f473b3e3ba5cd94948290a979067ace735e06 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 11 Feb 2022 18:59:00 -0800 Subject: [PATCH 013/104] Fix some flow stuff. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index ba8f8a9b8e4a..0357508e1d85 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -5,7 +5,7 @@ import type { NodeBase } from "./types"; const { assign: ObjectAssign } = Object; -const ManuallyAssignedErrorMessages = new WeakMap(); +const ManuallyAssignedErrorMessages = new WeakMap, string>(); export const ParseErrorCodes = Object.freeze({ SyntaxError: "BABEL_PARSER_SYNTAX_ERROR", @@ -64,7 +64,9 @@ function fromToMessage(toMessage: ToMessage): Class> { return class extends ParseError { get message() { return ManuallyAssignedErrorMessages.has(this) + // $FlowIgnore ? ManuallyAssignedErrorMessages.get(this) + // $FlowIgnore : `${toMessage(this)} (${this.loc.line}:${this.loc.column})`; } set message(message) { @@ -77,7 +79,6 @@ export function toParseErrorClasses( toClasses: (typeof toReasonCodelessParseErrorClass) => T, { syntaxPlugin, code } : Object = {} ): T { - // $FlowIgnore if (!instantiated) { const classes = { _instantiate() { @@ -90,6 +91,7 @@ export function toParseErrorClasses( return classes; } + // $FlowIgnore return Object.fromEntries( Object.entries(toClasses(toReasonCodelessParseErrorClass)).map( ([reasonCode, ParseErrorClass]) => [ From c788091e41815cd8f4188d0fb1d02439d24fff72 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 12 Feb 2022 07:12:57 -0800 Subject: [PATCH 014/104] Add a way to specify code. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 68 +++++++++---------- .../babel-parser/src/parse-error/module.js | 23 +++---- .../babel-parser/src/parse-error/standard.js | 7 +- .../src/parse-error/strict-mode.js | 6 +- .../babel-parser/src/parser/expression.js | 4 +- packages/babel-parser/src/parser/statement.js | 4 +- packages/babel-parser/src/tokenizer/state.js | 7 +- 7 files changed, 55 insertions(+), 64 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 0357508e1d85..1eb1289a5ddd 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -14,17 +14,16 @@ export const ParseErrorCodes = Object.freeze({ export type ParseErrorCode = $Values; -var instantiated = false; - type ToMessage = (self: ErrorProperties) => string; export class ParseError extends SyntaxError { + static code: ParseErrorCode; static reasonCode: string; static toMessage: ToMessage; // static name: string = "SyntaxError"; - code: ParseErrorCode = ParseErrorCodes.SyntaxError; + code: ParseErrorCode = this.constructor.code; reasonCode: string = this.constructor.reasonCode; loc: Position; @@ -45,29 +44,37 @@ export class ParseError extends SyntaxError { Object.defineProperty(ParseError, "name", { value: "SyntaxError" }); -declare function toReasonCodelessParseErrorClass( - T +declare function toParseErrorClass( + T, + ?{ code?: ParseErrorCode } ): Class>; -declare function toReasonCodelessParseErrorClass( - (T) => string +declare function toParseErrorClass( + (T) => string, + ?{ code?: ParseErrorCode } ): Class>; -function toReasonCodelessParseErrorClass(toMessageOrMessage) { +export function toParseErrorClass(toMessageOrMessage, properties) { return fromToMessage( typeof toMessageOrMessage === "string" ? () => toMessageOrMessage - : toMessageOrMessage + : toMessageOrMessage, + properties ); } -function fromToMessage(toMessage: ToMessage): Class> { +function fromToMessage( + toMessage: ToMessage, + { code = ParseErrorCodes.SyntaxError } = {} +): Class> { return class extends ParseError { + code: ParseErrorCode = code; + get message() { return ManuallyAssignedErrorMessages.has(this) - // $FlowIgnore - ? ManuallyAssignedErrorMessages.get(this) - // $FlowIgnore - : `${toMessage(this)} (${this.loc.line}:${this.loc.column})`; + ? // $FlowIgnore + ManuallyAssignedErrorMessages.get(this) + : // $FlowIgnore + `${toMessage(this)} (${this.loc.line}:${this.loc.column})`; } set message(message) { ManuallyAssignedErrorMessages.set(this, message); @@ -75,32 +82,20 @@ function fromToMessage(toMessage: ToMessage): Class> { }; } + export function toParseErrorClasses( - toClasses: (typeof toReasonCodelessParseErrorClass) => T, + toClasses: (typeof toParseErrorClass) => T, { syntaxPlugin, code } : Object = {} ): T { - if (!instantiated) { - const classes = { - _instantiate() { - delete classes._instantiate, - Object.assign(classes, toParseErrorClasses(toClasses, { syntaxPlugin, code })); - return classes; - } - }; - - return classes; - } - // $FlowIgnore return Object.fromEntries( - Object.entries(toClasses(toReasonCodelessParseErrorClass)).map( + Object.entries(toClasses(toParseErrorClass)).map( ([reasonCode, ParseErrorClass]) => [ reasonCode, // $FlowIgnore Object.assign(ParseErrorClass, { reasonCode }, - !!syntaxPlugin && { syntaxPlugin }, - !!code && { code }) + !!syntaxPlugin && { syntaxPlugin }) ]) ); } @@ -110,13 +105,12 @@ export type RaiseProperties = {| at: Position | NodeBase, |}; -import { default as ModuleErrorsUninstantiated } from "./parse-error/module"; - +import ModuleErrors from "./parse-error/module"; import StandardErrors from "./parse-error/standard"; import StrictErrors from "./parse-error/strict-mode"; -instantiated = true; - -export const ModuleErrors = ModuleErrorsUninstantiated._instantiate(); - -export const Errors = { ...StandardErrors._instantiate(), ...StrictErrors._instantiate() }; +export const Errors = { + ...toParseErrorClasses(ModuleErrors), + ...toParseErrorClasses(StandardErrors), + ...toParseErrorClasses(StrictErrors) +}; diff --git a/packages/babel-parser/src/parse-error/module.js b/packages/babel-parser/src/parse-error/module.js index a36166e8bd65..628ca02c10a4 100644 --- a/packages/babel-parser/src/parse-error/module.js +++ b/packages/babel-parser/src/parse-error/module.js @@ -1,15 +1,14 @@ // @flow -import { ParseErrorCodes, toParseErrorClasses } from "../parse-error"; +import { ParseErrorCodes, toParseErrorClass } from "../parse-error"; -export default toParseErrorClasses( - _ => ({ - ImportMetaOutsideModule: _( - `import.meta may appear only with 'sourceType: "module"'`, - ), - ImportOutsideModule: _( - `'import' and 'export' may appear only with 'sourceType: "module"'`, - ), - }), - //{ code: ParseErrorCodes.SourceTypeModuleError }, -); +export default (_: typeof toParseErrorClass) => ({ + ImportMetaOutsideModule: _( + `import.meta may appear only with 'sourceType: "module"'`, + { code: ParseErrorCodes.SourceTypeModuleError } + ), + ImportOutsideModule: _( + `'import' and 'export' may appear only with 'sourceType: "module"'`, + { code: ParseErrorCodes.SourceTypeModuleError } + ), +}); diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index 7056c85e05fb..6840d99fb8f2 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -1,9 +1,8 @@ // @flow -import { Position } from "../util/location"; -import { toParseErrorClasses } from "../parse-error"; +import { toParseErrorClass } from "../parse-error"; -export default toParseErrorClasses(_ => ({ +export default (_: typeof toParseErrorClass) => ({ AccessorIsGenerator: _<{| accessor: string |}>( ({ accessor }) => `A ${accessor}ter cannot be a generator.`, ), @@ -380,4 +379,4 @@ export default toParseErrorClasses(_ => ({ ZeroDigitNumericSeparator: _( "Numeric separator can not be used after leading 0.", ), -})); +}); diff --git a/packages/babel-parser/src/parse-error/strict-mode.js b/packages/babel-parser/src/parse-error/strict-mode.js index 82c1469c512d..a760dbb553db 100644 --- a/packages/babel-parser/src/parse-error/strict-mode.js +++ b/packages/babel-parser/src/parse-error/strict-mode.js @@ -1,8 +1,8 @@ // @flow -import { toParseErrorClasses } from "../parse-error"; +import { toParseErrorClass } from "../parse-error"; -export default toParseErrorClasses(_ => ({ +export default (_: typeof toParseErrorClass) => ({ StrictDelete: _("Deleting local variable in strict mode."), StrictEvalArguments: _<{| binding: string |}>( ({ binding }) => `Assigning to '${binding}' in strict mode.`, @@ -20,4 +20,4 @@ export default toParseErrorClasses(_ => ({ "Legacy octal literals are not allowed in strict mode.", ), StrictWith: _("'with' in strict mode."), -})); +}); diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index c20e288d836a..14076e07570c 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -67,7 +67,7 @@ import { newAsyncArrowScope, newExpressionScope, } from "../util/expression-scope"; -import { Errors, ModuleErrors, ParseError } from "../parse-error"; +import { Errors, ParseError } from "../parse-error"; import { setInnerComments } from "./comments"; import { cloneIdentifier } from "./node"; @@ -1601,7 +1601,7 @@ export default class ExpressionParser extends LValParser { if (this.isContextual(tt._meta)) { if (!this.inModule) { - this.raise(ModuleErrors.ImportMetaOutsideModule, { at: id }); + this.raise(Errors.ImportMetaOutsideModule, { at: id }); } this.sawUnambiguousESM = true; } diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 189cbde5eadd..6e09d1a13ae5 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -10,7 +10,7 @@ import { getExportedToken, } from "../tokenizer/types"; import ExpressionParser from "./expression"; -import { Errors, ModuleErrors } from "../parse-error"; +import { Errors } from "../parse-error"; import { isIdentifierChar, isIdentifierStart } from "../util/identifier"; import { lineBreak } from "../util/whitespace"; import * as charCodes from "charcodes"; @@ -483,7 +483,7 @@ export default class StatementParser extends ExpressionParser { assertModuleNodeAllowed(node: N.Node): void { if (!this.options.allowImportExportEverywhere && !this.inModule) { - this.raise(ModuleErrors.ImportOutsideModule, { at: node }); + this.raise(Errors.ImportOutsideModule, { at: node }); } } diff --git a/packages/babel-parser/src/tokenizer/state.js b/packages/babel-parser/src/tokenizer/state.js index e9988a8d71d9..1e3fe4d300cd 100644 --- a/packages/babel-parser/src/tokenizer/state.js +++ b/packages/babel-parser/src/tokenizer/state.js @@ -7,12 +7,11 @@ import { Position } from "../util/location"; import { types as ct, type TokContext } from "./context"; import { tt, type TokenType } from "./types"; -import type { ParseError } from "../parse-error"; -import StrictErrors from "../parse-error/strict-mode"; +import { Errors, ParseError } from "../parse-error"; export type DeferredStrictErrorClass = - | typeof StrictErrors.StrictNumericEscape - | typeof StrictErrors.StrictOctalLiteral; + | typeof Errors.StrictNumericEscape + | typeof Errors.StrictOctalLiteral; type TopicContextState = { // When a topic binding has been currently established, From 78e9eb31d4c640e7827485e5e4ae70fecb6c80e0 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 12 Feb 2022 09:33:17 -0800 Subject: [PATCH 015/104] Fix lint errors. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 44 ++++++++++++------- .../babel-parser/src/parse-error/module.js | 4 +- .../babel-parser/src/parse-error/standard.js | 13 +++--- packages/babel-parser/src/parser/statement.js | 2 +- .../babel-parser/src/plugins/flow/index.js | 4 +- .../src/plugins/typescript/index.js | 7 ++- 6 files changed, 46 insertions(+), 28 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 1eb1289a5ddd..e01faf76e248 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -21,7 +21,11 @@ export class ParseError extends SyntaxError { static reasonCode: string; static toMessage: ToMessage; -// static name: string = "SyntaxError"; + // Unfortunately babel-standalone turns this into a straight assigmnent + // instead of a defineProperty, so it throws an error. We do it manually + // below instead. + // + // static name: string = "SyntaxError"; code: ParseErrorCode = this.constructor.code; reasonCode: string = this.constructor.reasonCode; @@ -42,39 +46,46 @@ export class ParseError extends SyntaxError { } } +// We do this for backwards compatibility so that all errors just have the +// "SyntaxError" name in their messages instead of leaking the private subclass +// name. Object.defineProperty(ParseError, "name", { value: "SyntaxError" }); declare function toParseErrorClass( T, - ?{ code?: ParseErrorCode } + ?{ code?: ParseErrorCode }, ): Class>; + +// eslint-disable-next-line no-redeclare declare function toParseErrorClass( (T) => string, - ?{ code?: ParseErrorCode } + ?{ code?: ParseErrorCode }, ): Class>; +// eslint-disable-next-line no-redeclare export function toParseErrorClass(toMessageOrMessage, properties) { return fromToMessage( typeof toMessageOrMessage === "string" ? () => toMessageOrMessage : toMessageOrMessage, - properties + properties, ); } function fromToMessage( toMessage: ToMessage, - { code = ParseErrorCodes.SyntaxError } = {} + { code = ParseErrorCodes.SyntaxError, reasonCode = "" } = {}, ): Class> { return class extends ParseError { - code: ParseErrorCode = code; + static code: ParseErrorCode = code; + static reasonCode: string = reasonCode; get message() { return ManuallyAssignedErrorMessages.has(this) ? // $FlowIgnore - ManuallyAssignedErrorMessages.get(this) + ManuallyAssignedErrorMessages.get(this) : // $FlowIgnore - `${toMessage(this)} (${this.loc.line}:${this.loc.column})`; + `${toMessage(this)} (${this.loc.line}:${this.loc.column})`; } set message(message) { ManuallyAssignedErrorMessages.set(this, message); @@ -82,21 +93,22 @@ function fromToMessage( }; } - export function toParseErrorClasses( toClasses: (typeof toParseErrorClass) => T, - { syntaxPlugin, code } : Object = {} + { syntaxPlugin }: Object = {}, ): T { // $FlowIgnore return Object.fromEntries( Object.entries(toClasses(toParseErrorClass)).map( ([reasonCode, ParseErrorClass]) => [ reasonCode, - // $FlowIgnore - Object.assign(ParseErrorClass, - { reasonCode }, - !!syntaxPlugin && { syntaxPlugin }) - ]) + ObjectAssign( + ParseErrorClass, + !ParseErrorClass.reasonCode && { reasonCode }, + !!syntaxPlugin && { syntaxPlugin }, + ), + ], + ), ); } @@ -112,5 +124,5 @@ import StrictErrors from "./parse-error/strict-mode"; export const Errors = { ...toParseErrorClasses(ModuleErrors), ...toParseErrorClasses(StandardErrors), - ...toParseErrorClasses(StrictErrors) + ...toParseErrorClasses(StrictErrors), }; diff --git a/packages/babel-parser/src/parse-error/module.js b/packages/babel-parser/src/parse-error/module.js index 628ca02c10a4..c68d25d472bc 100644 --- a/packages/babel-parser/src/parse-error/module.js +++ b/packages/babel-parser/src/parse-error/module.js @@ -5,10 +5,10 @@ import { ParseErrorCodes, toParseErrorClass } from "../parse-error"; export default (_: typeof toParseErrorClass) => ({ ImportMetaOutsideModule: _( `import.meta may appear only with 'sourceType: "module"'`, - { code: ParseErrorCodes.SourceTypeModuleError } + { code: ParseErrorCodes.SourceTypeModuleError }, ), ImportOutsideModule: _( `'import' and 'export' may appear only with 'sourceType: "module"'`, - { code: ParseErrorCodes.SourceTypeModuleError } + { code: ParseErrorCodes.SourceTypeModuleError }, ), }); diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index 6840d99fb8f2..60cfce7bc13a 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -127,12 +127,10 @@ export default (_: typeof toParseErrorClass) => ({ ({ identifier }) => `Invalid identifier ${identifier}.`, ), InvalidLhs: _<{| construct: string /*"for-loop"*/ |}>( - ({ construct }) => - `Invalid left-hand side in ${construct}.`, + ({ construct }) => `Invalid left-hand side in ${construct}.`, ), InvalidLhsBinding: _<{| construct: string |}>( - ({ construct }) => - `Binding invalid left-hand side in ${construct}.`, + ({ construct }) => `Binding invalid left-hand side in ${construct}.`, ), InvalidNumber: _("Invalid number."), InvalidOrMissingExponent: _( @@ -336,8 +334,11 @@ export default (_: typeof toParseErrorClass) => ({ UnexpectedToken: _<{| expected?: ?string, unexpected?: ?string, - |}>(({ expected, unexpected }) => - `Unexpected token${ unexpected ? ` '${unexpected}'.` : ""}${ expected ? `, expected "${expected}"` : ""}` + |}>( + ({ expected, unexpected }) => + `Unexpected token${unexpected ? ` '${unexpected}'.` : ""}${ + expected ? `, expected "${expected}"` : "" + }`, ), UnexpectedTokenUnaryExponentiation: _( "Illegal expression. Wrap left hand side or entire exponentiation in parentheses.", diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 6e09d1a13ae5..39d3335464b4 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -2538,7 +2538,7 @@ export default class StatementParser extends ExpressionParser { if (node.key.name !== "type") { this.raise(Errors.ModuleAttributeDifferentFromType, { at: node.key, -// key: node.key.name, + // key: node.key.name, }); } diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index cdb8edd3b6df..a560ffd5847c 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -166,6 +166,8 @@ const FlowErrors = toParseErrorClasses( ), PatternIsOptional: _( "A binding pattern parameter cannot be optional in an implementation signature.", + // For consistency in TypeScript and Flow error codes + !process.env.BABEL_8_BREAKING && { reasonCode: "OptionalBindingPattern" }, ), SetterMayNotHaveThisParam: _("A setter cannot have a `this` parameter."), SpreadVariance: _("Spread properties cannot have variance."), @@ -3351,7 +3353,7 @@ export default (superClass: Class): Class => at: loc, enumName, memberName, - explicitType + explicitType, }, ); } diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 84ff9be33963..443084be8708 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -104,7 +104,7 @@ const TSErrors = toParseErrorClasses( ), DuplicateAccessibilityModifier: _<{| modifier: N.Accessibility |}>( // `Accessibility modifier ${modifier} already seen.` would be more helpful. - // eslint-disable-line no-unused-vars + // eslint-disable-next-line no-unused-vars ({ modifier }) => `Accessibility modifier already seen.`, ), DuplicateModifier: _<{| modifier: TsModifier |}>( @@ -3228,7 +3228,10 @@ export default (superClass: Class): Class => contextDescription !== "parenthesized expression" && !expr.extra?.parenthesized ) { - this.raise(Errors.InvalidLhs, { at: expr, construct: contextDescription }); + this.raise(Errors.InvalidLhs, { + at: expr, + construct: contextDescription, + }); break; } this.checkLVal(expr.expression, "parenthesized expression", ...args); From 8c3c405367a5d359198dee79f90f7d756e054300 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 09:10:16 -0800 Subject: [PATCH 016/104] Fix Flow errors. Reviewed by @tolmasky. Another Flow things. Reviewed by @tolmasky. Fix Flow error. Reviewed by @tolmasky. Fix flow errors. Reviewed by @tolmasky. Fix flow error. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 10 +++++++--- packages/babel-parser/src/plugins/flow/index.js | 2 +- packages/babel-parser/src/util/expression-scope.js | 8 ++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index e01faf76e248..6e9cf4b896e5 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -19,6 +19,7 @@ type ToMessage = (self: ErrorProperties) => string; export class ParseError extends SyntaxError { static code: ParseErrorCode; static reasonCode: string; + static syntaxPlugin: ?string; static toMessage: ToMessage; // Unfortunately babel-standalone turns this into a straight assigmnent @@ -29,6 +30,7 @@ export class ParseError extends SyntaxError { code: ParseErrorCode = this.constructor.code; reasonCode: string = this.constructor.reasonCode; + syntaxPlugin: ?string = this.constructor.syntaxPlugin; loc: Position; pos: number; @@ -53,13 +55,13 @@ Object.defineProperty(ParseError, "name", { value: "SyntaxError" }); declare function toParseErrorClass( T, - ?{ code?: ParseErrorCode }, + ?{ code?: ParseErrorCode } | boolean, ): Class>; // eslint-disable-next-line no-redeclare declare function toParseErrorClass( (T) => string, - ?{ code?: ParseErrorCode }, + ?{ code?: ParseErrorCode } | boolean, ): Class>; // eslint-disable-next-line no-redeclare @@ -100,10 +102,12 @@ export function toParseErrorClasses( // $FlowIgnore return Object.fromEntries( Object.entries(toClasses(toParseErrorClass)).map( - ([reasonCode, ParseErrorClass]) => [ + ([reasonCode, ParseErrorClass: Class>]) => [ reasonCode, + // $FlowIgnore ObjectAssign( ParseErrorClass, + // $FlowIgnore !ParseErrorClass.reasonCode && { reasonCode }, !!syntaxPlugin && { syntaxPlugin }, ), diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index a560ffd5847c..df47b6529e04 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -3344,7 +3344,7 @@ export default (superClass: Class): Class => { enumName, explicitType, memberName }: EnumContext, ) { return this.raise( - /^(boolean|number|string)$/.test(explicitType) + /^(boolean|number|string)$/.test(explicitType || "") ? FlowErrors.EnumInvalidMemberInitializerPrimaryType : explicitType === "symbol" ? FlowErrors.EnumInvalidMemberInitializerSymbolType diff --git a/packages/babel-parser/src/util/expression-scope.js b/packages/babel-parser/src/util/expression-scope.js index 36d63f7d618a..3886c613731a 100644 --- a/packages/babel-parser/src/util/expression-scope.js +++ b/packages/babel-parser/src/util/expression-scope.js @@ -94,18 +94,18 @@ class ArrowHeadParsingScope extends ExpressionScope { super(type); } recordDeclarationError( - ParsingError: T, + ParsingErrorClass: T, { at }: { at: Position }, ) { const index = at.index; - this.declarationErrors.set(index, [ParsingError, at]); + this.declarationErrors.set(index, [ParsingErrorClass, at]); } clearDeclarationError(index: number) { this.declarationErrors.delete(index); } - iterateErrors( - iterator: ([T, Position]) => void, + iterateErrors( + iterator: ([ArrowHeadParsingDeclarationErrorClass, Position]) => void, ) { this.declarationErrors.forEach(iterator); } From 2d6868d78e3f91c7cb0da4c67149b0032d75b4a7 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 13:52:20 -0800 Subject: [PATCH 017/104] See if this gets the name right. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 6e9cf4b896e5..0d06245c8e87 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -51,7 +51,9 @@ export class ParseError extends SyntaxError { // We do this for backwards compatibility so that all errors just have the // "SyntaxError" name in their messages instead of leaking the private subclass // name. -Object.defineProperty(ParseError, "name", { value: "SyntaxError" }); +Object.defineProperty(ParseError.prototype.constructor, "name", { + value: "SyntaxError", +}); declare function toParseErrorClass( T, From fc112eeeb87cc4d815d9e8e21df6d350d2894007 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 19 Feb 2022 16:17:15 -0800 Subject: [PATCH 018/104] Add comments to raise and raiseOverwrite. Reviewed by @tolmasky. --- packages/babel-parser/src/tokenizer/index.js | 32 ++++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index cca525b38d52..40a1e9ff1119 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -1747,6 +1747,20 @@ export default class Tokenizer extends CommentsParser { } } + /** + * Raise a `ParseError` given the appropriate properties. If passed a + *`Position` for the `at` property, raises the `ParseError` at that location. + * Otherwise, if passed a `Node`, raises the `ParseError` at the start + * location of that `Node`. + * + * If `errorRecovery` is `true`, the error is pushed to the errors array and + * returned. If `errorRecovery` is `false`, the error is instead thrown. + * + * @param {Class>>} ParseErrorClass + * @param {RaiseProperties} raiseProperties + * @returns {(ParseError | empty)} + * @memberof Tokenizer + */ raise>>( ParseErrorClass: T, raiseProperties: RaiseProperties, @@ -1761,18 +1775,16 @@ export default class Tokenizer extends CommentsParser { return error; } - /* /** - * Raise a parsing error on given position pos. If errorRecovery is true, - * it will first search current errors and overwrite the error thrown on the exact - * position before with the new error message. If errorRecovery is false, it - * fallbacks to `raise`. + * If `errorRecovery` is `false`, this method behaves identically to `raise`. + * If `errorRecovery` is `true`, this method will first see if there is an + * already an error stored at the same `Position`, and replaces it with the + * one generated here. * - * @param {number} pos - * @param {string} errorTemplate - * @param {...any} params - * @returns {(Error | empty)} - * @memberof ParserError + * @param {Class>>} ParseErrorClass + * @param {RaiseProperties} raiseProperties + * @returns {(ParseError | empty)} + * @memberof Tokenizer */ raiseOverwrite>>( ParseErrorClass: T, From 6b219091b46eada4f2a650c522202a129efd540a Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 19 Feb 2022 16:37:29 -0800 Subject: [PATCH 019/104] Remove forward declaration. Reviewed by @tolmasky. --- packages/babel-parser/src/tokenizer/index.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 40a1e9ff1119..bfd810a841b0 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -131,14 +131,6 @@ export class Token { // ## Tokenizer export default class Tokenizer extends CommentsParser { - // Forward-declarations - // parser/util.js - /*:: - +hasPrecedingLineBreak: () => boolean; - +unexpected: (loc?: ?Position, type?: TokenType) => empty; - +expectPlugin: (name: string, loc?: Position) => true; - */ - isLookahead: boolean; // Token store. From 774c78b99f10944bdcac58ac0079380e033d49a3 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 14 Feb 2022 15:14:41 -0800 Subject: [PATCH 020/104] Change option to be kind. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error/standard.js | 4 ++-- packages/babel-parser/src/parser/expression.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index 60cfce7bc13a..4d16196911e9 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -3,8 +3,8 @@ import { toParseErrorClass } from "../parse-error"; export default (_: typeof toParseErrorClass) => ({ - AccessorIsGenerator: _<{| accessor: string |}>( - ({ accessor }) => `A ${accessor}ter cannot be a generator.`, + AccessorIsGenerator: _<{| kind: "get" | " set" |}>( + ({ kind }) => `A ${kind}ter cannot be a generator.`, ), ArgumentsInClass: _( diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 14076e07570c..f1e841705aae 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -2064,7 +2064,7 @@ export default class ExpressionParser extends LValParser { isGenerator = true; this.raise(Errors.AccessorIsGenerator, { at: this.state.curPosition(), - accessor: keyName, + kind: keyName, }); this.next(); } From c8ac0f5ca94afa394c57dfc5fa4f7c5ffe3f9f56 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 08:34:52 -0800 Subject: [PATCH 021/104] Fix for ambient context not being respected in variable declarations. Reviewed by @tolmasky. --- .../input.js | 2 +- .../output.js | 2 +- .../typescript/const-no-initializer/input.js | 2 +- .../typescript/const-no-initializer/output.js | 2 +- .../constructor-signature-types/input.js | 2 +- .../constructor-signature-types/output.js | 2 +- .../babel-parser/src/parse-error/standard.js | 6 +- packages/babel-parser/src/parser/statement.js | 40 ++++++------- .../src/plugins/typescript/index.js | 59 ++++++++++++++----- .../core/uncategorised/536/output.json | 2 +- .../es2015/uncategorised/324/output.json | 2 +- .../es2015/uncategorised/325/output.json | 2 +- .../unexpected-number/output.json | 2 +- .../output.json | 2 +- .../invalid-syntax/migrated_0138/output.json | 2 +- .../invalid-syntax/migrated_0139/output.json | 2 +- .../invalid-syntax/migrated_0140/output.json | 2 +- .../complex-pattern-requires-init/output.json | 2 +- .../const/no-initializer/output.json | 3 + .../declare/const-new-line/output.json | 4 ++ .../declare/destructure-new-line/output.json | 3 + 21 files changed, 91 insertions(+), 54 deletions(-) diff --git a/packages/babel-generator/test/fixtures/typescript/abstract-constructor-signature-types/input.js b/packages/babel-generator/test/fixtures/typescript/abstract-constructor-signature-types/input.js index fbaa3a91c4a4..6bb94752bf01 100644 --- a/packages/babel-generator/test/fixtures/typescript/abstract-constructor-signature-types/input.js +++ b/packages/babel-generator/test/fixtures/typescript/abstract-constructor-signature-types/input.js @@ -1 +1 @@ -const x: abstract new () => void; +declare const x: abstract new () => void; diff --git a/packages/babel-generator/test/fixtures/typescript/abstract-constructor-signature-types/output.js b/packages/babel-generator/test/fixtures/typescript/abstract-constructor-signature-types/output.js index 5c520bc29f3c..71f417390e21 100644 --- a/packages/babel-generator/test/fixtures/typescript/abstract-constructor-signature-types/output.js +++ b/packages/babel-generator/test/fixtures/typescript/abstract-constructor-signature-types/output.js @@ -1 +1 @@ -const x: abstract new () => void; \ No newline at end of file +declare const x: abstract new () => void; \ No newline at end of file diff --git a/packages/babel-generator/test/fixtures/typescript/const-no-initializer/input.js b/packages/babel-generator/test/fixtures/typescript/const-no-initializer/input.js index 8caae258b97c..7a86e6a93360 100644 --- a/packages/babel-generator/test/fixtures/typescript/const-no-initializer/input.js +++ b/packages/babel-generator/test/fixtures/typescript/const-no-initializer/input.js @@ -1 +1 @@ -const x: number; +declare const x: number; diff --git a/packages/babel-generator/test/fixtures/typescript/const-no-initializer/output.js b/packages/babel-generator/test/fixtures/typescript/const-no-initializer/output.js index bee7698244b8..aa2fb21074c8 100644 --- a/packages/babel-generator/test/fixtures/typescript/const-no-initializer/output.js +++ b/packages/babel-generator/test/fixtures/typescript/const-no-initializer/output.js @@ -1 +1 @@ -const x: number; \ No newline at end of file +declare const x: number; \ No newline at end of file diff --git a/packages/babel-generator/test/fixtures/typescript/constructor-signature-types/input.js b/packages/babel-generator/test/fixtures/typescript/constructor-signature-types/input.js index c1fc7b6057c6..138d69779a4b 100644 --- a/packages/babel-generator/test/fixtures/typescript/constructor-signature-types/input.js +++ b/packages/babel-generator/test/fixtures/typescript/constructor-signature-types/input.js @@ -1 +1 @@ -const x: new () => void; +declare const x: new () => void; diff --git a/packages/babel-generator/test/fixtures/typescript/constructor-signature-types/output.js b/packages/babel-generator/test/fixtures/typescript/constructor-signature-types/output.js index 218e27130c01..cc6549eedebf 100644 --- a/packages/babel-generator/test/fixtures/typescript/constructor-signature-types/output.js +++ b/packages/babel-generator/test/fixtures/typescript/constructor-signature-types/output.js @@ -1 +1 @@ -const x: new () => void; \ No newline at end of file +declare const x: new () => void; \ No newline at end of file diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index 4d16196911e9..91f4f7369cc9 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -3,7 +3,7 @@ import { toParseErrorClass } from "../parse-error"; export default (_: typeof toParseErrorClass) => ({ - AccessorIsGenerator: _<{| kind: "get" | " set" |}>( + AccessorIsGenerator: _<{| kind: "get" | "set" |}>( ({ kind }) => `A ${kind}ter cannot be a generator.`, ), @@ -38,8 +38,8 @@ export default (_: typeof toParseErrorClass) => ({ ConstructorIsAccessor: _("Class constructor may not be an accessor."), ConstructorIsAsync: _("Constructor can't be an async function."), ConstructorIsGenerator: _("Constructor can't be a generator."), - DeclarationMissingInitializer: _<{| declaration: string |}>( - ({ declaration }) => `'${declaration}' require an initialization value.`, + DeclarationMissingInitializer: _<{| kind: "const" | "destructuring" |}>( + ({ kind }) => `Missing initializer in ${kind} declaration.`, ), DecoratorBeforeExport: _( "Decorators must be placed *before* the 'export' keyword. You can set the 'decoratorsBeforeExport' option to false to use the 'export @decorator class {}' syntax.", diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 39d3335464b4..b72fc0b080ca 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -894,9 +894,10 @@ export default class StatementParser extends ExpressionParser { parseVarStatement( node: N.VariableDeclaration, kind: "var" | "let" | "const", + allowMissingInitializer: boolean = false, ): N.VariableDeclaration { this.next(); - this.parseVar(node, false, kind); + this.parseVar(node, false, kind, allowMissingInitializer); this.semicolon(); return this.finishNode(node, "VariableDeclaration"); } @@ -1207,40 +1208,37 @@ export default class StatementParser extends ExpressionParser { node: N.VariableDeclaration, isFor: boolean, kind: "var" | "let" | "const", + allowMissingInitializer: boolean = false, ): N.VariableDeclaration { const declarations = (node.declarations = []); - const isTypescript = this.hasPlugin("typescript"); node.kind = kind; for (;;) { const decl = this.startNode(); this.parseVarId(decl, kind); - if (this.eat(tt.eq)) { - decl.init = isFor - ? this.parseMaybeAssignDisallowIn() - : this.parseMaybeAssignAllowIn(); - } else { + decl.init = !this.eat(tt.eq) + ? null + : isFor + ? this.parseMaybeAssignDisallowIn() + : this.parseMaybeAssignAllowIn(); + + if (decl.init === null && !allowMissingInitializer) { if ( - kind === "const" && - !(this.match(tt._in) || this.isContextual(tt._of)) - ) { - // `const` with no initializer is allowed in TypeScript. - // It could be a declaration like `const x: number;`. - if (!isTypescript) { - this.raise(Errors.DeclarationMissingInitializer, { - at: this.state.lastTokEndLoc, - declaration: "Const declarations", - }); - } - } else if ( decl.id.type !== "Identifier" && !(isFor && (this.match(tt._in) || this.isContextual(tt._of))) ) { this.raise(Errors.DeclarationMissingInitializer, { at: this.state.lastTokEndLoc, - declaration: "Complex binding patterns", + kind: "destructuring", + }); + } else if ( + kind === "const" && + !(this.match(tt._in) || this.isContextual(tt._of)) + ) { + this.raise(Errors.DeclarationMissingInitializer, { + at: this.state.lastTokEndLoc, + kind: "const", }); } - decl.init = null; } declarations.push(this.finishNode(decl, "VariableDeclarator")); if (!this.eat(tt.comma)) break; diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 443084be8708..7480587445f4 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -139,6 +139,9 @@ const TSErrors = toParseErrorClasses( IndexSignatureHasStatic: _( "Index signatures cannot have the 'static' modifier.", ), + InitializerNotAllowInAmbientContext: _( + "Initializers are not allowed in ambient contexts.", + ), InvalidModifierOnTypeMember: _<{| modifier: TsModifier |}>( ({ modifier }) => `'${modifier}' modifier cannot appear on a type member.`, @@ -1860,7 +1863,7 @@ export default (superClass: Class): Class => ); case tt._class: // While this is also set by tsParseExpressionStatement, we need to set it - // before parsing the class declaration to now how to register it in the scope. + // before parsing the class declaration to know how to register it in the scope. nany.declare = true; return this.parseClass( nany, @@ -1877,7 +1880,7 @@ export default (superClass: Class): Class => // falls through case tt._var: kind = kind || this.state.value; - return this.parseVarStatement(nany, kind); + return this.parseVarStatement(nany, kind, true); case tt._global: return this.tsParseAmbientExternalModuleDeclaration(nany); default: { @@ -2513,6 +2516,31 @@ export default (superClass: Class): Class => return super.parseExportDefaultExpression(); } + parseVarStatement( + node: N.VariableDeclaration, + kind: "var" | "let" | "const", + allowMissingInitializer: boolean = false, + ) { + const { isAmbientContext } = this.state; + const declaration = super.parseVarStatement( + node, + kind, + allowMissingInitializer || isAmbientContext, + ); + + if (isAmbientContext) { + for (const declarator of declaration.declarations) { + if (declarator.init) { + this.raise(TSErrors.InitializerNotAllowInAmbientContext, { + at: declarator.init, + }); + } + } + } + + return declaration; + } + parseStatementContent(context: ?string, topLevel: ?boolean): N.Statement { if (this.state.type === tt._const) { const ahead = this.lookahead(); @@ -2741,11 +2769,14 @@ export default (superClass: Class): Class => } parseExportDeclaration(node: N.ExportNamedDeclaration): ?N.Declaration { + if (!this.state.isAmbientContext && this.isContextual(tt._declare)) { + return this.tsInAmbientContext(() => this.parseExportDeclaration(node)); + } + // Store original location/position const startPos = this.state.start; const startLoc = this.state.startLoc; - // "export declare" is equivalent to just "export". const isDeclare = this.eatContextual(tt._declare); if ( @@ -2757,24 +2788,22 @@ export default (superClass: Class): Class => }); } - let declaration: ?N.Declaration; + const isIdentifier = tokenIsIdentifier(this.state.type); + const declaration: ?N.Declaration = + (isIdentifier && this.tsTryParseExportDeclaration()) || + super.parseExportDeclaration(node); + + if (!declaration) return null; - if (tokenIsIdentifier(this.state.type)) { - declaration = this.tsTryParseExportDeclaration(); - } - if (!declaration) { - declaration = super.parseExportDeclaration(node); - } if ( - declaration && - (declaration.type === "TSInterfaceDeclaration" || - declaration.type === "TSTypeAliasDeclaration" || - isDeclare) + declaration.type === "TSInterfaceDeclaration" || + declaration.type === "TSTypeAliasDeclaration" || + isDeclare ) { node.exportKind = "type"; } - if (declaration && isDeclare) { + if (isDeclare) { // Reset location to include `declare` in range this.resetStartLocation(declaration, startPos, startLoc); diff --git a/packages/babel-parser/test/fixtures/core/uncategorised/536/output.json b/packages/babel-parser/test/fixtures/core/uncategorised/536/output.json index cb7ad447ddcd..cc71c56d59f9 100644 --- a/packages/babel-parser/test/fixtures/core/uncategorised/536/output.json +++ b/packages/babel-parser/test/fixtures/core/uncategorised/536/output.json @@ -2,7 +2,7 @@ "type": "File", "start":0,"end":8,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":8,"index":8}}, "errors": [ - "SyntaxError: 'Const declarations' require an initialization value. (1:7)" + "SyntaxError: Missing initializer in const declaration. (1:7)" ], "program": { "type": "Program", diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/324/output.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/324/output.json index a6fddf3bd39e..f3a75a06bb17 100644 --- a/packages/babel-parser/test/fixtures/es2015/uncategorised/324/output.json +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/324/output.json @@ -2,7 +2,7 @@ "type": "File", "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}, "errors": [ - "SyntaxError: 'Complex binding patterns' require an initialization value. (1:7)" + "SyntaxError: Missing initializer in destructuring declaration. (1:7)" ], "program": { "type": "Program", diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/325/output.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/325/output.json index 3fc899594e35..87074675d059 100644 --- a/packages/babel-parser/test/fixtures/es2015/uncategorised/325/output.json +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/325/output.json @@ -2,7 +2,7 @@ "type": "File", "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}, "errors": [ - "SyntaxError: 'Complex binding patterns' require an initialization value. (1:7)" + "SyntaxError: Missing initializer in destructuring declaration. (1:7)" ], "program": { "type": "Program", diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-for-of/unexpected-number/output.json b/packages/babel-parser/test/fixtures/esprima/es2015-for-of/unexpected-number/output.json index 856e91085a2a..aa863576aee7 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-for-of/unexpected-number/output.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-for-of/unexpected-number/output.json @@ -2,7 +2,7 @@ "type": "File", "start":0,"end":18,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":18,"index":18}}, "errors": [ - "SyntaxError: 'Const declarations' require an initialization value. (1:13)", + "SyntaxError: Missing initializer in const declaration. (1:13)", "SyntaxError: Missing semicolon. (1:13)", "SyntaxError: Missing semicolon. (1:16)" ], diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-lexical-declaration/invalid_complex_binding_without_init/output.json b/packages/babel-parser/test/fixtures/esprima/es2015-lexical-declaration/invalid_complex_binding_without_init/output.json index 716c25395d69..48db1d3a5d9a 100644 --- a/packages/babel-parser/test/fixtures/esprima/es2015-lexical-declaration/invalid_complex_binding_without_init/output.json +++ b/packages/babel-parser/test/fixtures/esprima/es2015-lexical-declaration/invalid_complex_binding_without_init/output.json @@ -2,7 +2,7 @@ "type": "File", "start":0,"end":6,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":6,"index":6}}, "errors": [ - "SyntaxError: 'Complex binding patterns' require an initialization value. (1:6)" + "SyntaxError: Missing initializer in destructuring declaration. (1:6)" ], "program": { "type": "Program", diff --git a/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0138/output.json b/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0138/output.json index 21116a466270..e9f35fd6a4b7 100644 --- a/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0138/output.json +++ b/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0138/output.json @@ -2,7 +2,7 @@ "type": "File", "start":0,"end":16,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":16,"index":16}}, "errors": [ - "SyntaxError: 'Const declarations' require an initialization value. (1:15)" + "SyntaxError: Missing initializer in const declaration. (1:15)" ], "program": { "type": "Program", diff --git a/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0139/output.json b/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0139/output.json index 060d631ab175..8365a2df197b 100644 --- a/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0139/output.json +++ b/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0139/output.json @@ -2,7 +2,7 @@ "type": "File", "start":0,"end":16,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":16,"index":16}}, "errors": [ - "SyntaxError: 'Const declarations' require an initialization value. (1:7)" + "SyntaxError: Missing initializer in const declaration. (1:7)" ], "program": { "type": "Program", diff --git a/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0140/output.json b/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0140/output.json index a257682ebe11..1af9735f2f2c 100644 --- a/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0140/output.json +++ b/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0140/output.json @@ -2,7 +2,7 @@ "type": "File", "start":0,"end":8,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":8,"index":8}}, "errors": [ - "SyntaxError: 'Const declarations' require an initialization value. (1:7)" + "SyntaxError: Missing initializer in const declaration. (1:7)" ], "program": { "type": "Program", diff --git a/packages/babel-parser/test/fixtures/esprima/statement-variable/complex-pattern-requires-init/output.json b/packages/babel-parser/test/fixtures/esprima/statement-variable/complex-pattern-requires-init/output.json index 869bacc7dfd9..84c6b1979b7b 100644 --- a/packages/babel-parser/test/fixtures/esprima/statement-variable/complex-pattern-requires-init/output.json +++ b/packages/babel-parser/test/fixtures/esprima/statement-variable/complex-pattern-requires-init/output.json @@ -2,7 +2,7 @@ "type": "File", "start":0,"end":6,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":6,"index":6}}, "errors": [ - "SyntaxError: 'Complex binding patterns' require an initialization value. (1:6)" + "SyntaxError: Missing initializer in destructuring declaration. (1:6)" ], "program": { "type": "Program", diff --git a/packages/babel-parser/test/fixtures/typescript/const/no-initializer/output.json b/packages/babel-parser/test/fixtures/typescript/const/no-initializer/output.json index c3e5fb25610b..93a337822a56 100644 --- a/packages/babel-parser/test/fixtures/typescript/const/no-initializer/output.json +++ b/packages/babel-parser/test/fixtures/typescript/const/no-initializer/output.json @@ -1,6 +1,9 @@ { "type": "File", "start":0,"end":16,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":16,"index":16}}, + "errors": [ + "SyntaxError: Missing initializer in const declaration. (1:15)" + ], "program": { "type": "Program", "start":0,"end":16,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":16,"index":16}}, diff --git a/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/output.json b/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/output.json index 0b639369894c..2dba7ec555da 100644 --- a/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/output.json +++ b/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/output.json @@ -1,6 +1,10 @@ { "type": "File", "start":0,"end":35,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":27,"index":35}}, + "errors": [ + "SyntaxError: Missing initializer in const declaration. (2:15)", + "SyntaxError: Missing initializer in const declaration. (2:26)" + ], "program": { "type": "Program", "start":0,"end":35,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":27,"index":35}}, diff --git a/packages/babel-parser/test/fixtures/typescript/declare/destructure-new-line/output.json b/packages/babel-parser/test/fixtures/typescript/declare/destructure-new-line/output.json index 82112c2678e4..12f46ecb3e07 100644 --- a/packages/babel-parser/test/fixtures/typescript/declare/destructure-new-line/output.json +++ b/packages/babel-parser/test/fixtures/typescript/declare/destructure-new-line/output.json @@ -1,6 +1,9 @@ { "type": "File", "start":0,"end":49,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":41,"index":49}}, + "errors": [ + "SyntaxError: Missing initializer in destructuring declaration. (2:40)" + ], "program": { "type": "Program", "start":0,"end":49,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":41,"index":49}}, From 3d08b3009a4adde6e2d8999768460a71327738b9 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 11:38:11 -0800 Subject: [PATCH 022/104] Make IllegalBreakContinue based on type. Reviewed by @tolmasky. --- .flowconfig | 1 - packages/babel-parser/src/parse-error/standard.js | 5 +++-- packages/babel-parser/src/parser/statement.js | 6 ++---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.flowconfig b/.flowconfig index 82b0e8729823..52db881e0d2e 100644 --- a/.flowconfig +++ b/.flowconfig @@ -23,7 +23,6 @@ lib/babel-packages.js.flow lib/package-json.js.flow [options] -experimental.enums=true include_warnings=true suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe suppress_comment= \\(.\\|\n\\)*\\$FlowIssue diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index 91f4f7369cc9..4a415c104c07 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -85,8 +85,9 @@ export default (_: typeof toParseErrorClass) => ({ "Generators can only be declared at the top level or inside a block.", ), - IllegalBreakContinue: _<{| construct: "break" | "continue" |}>( - ({ construct }) => `Unsyntactic ${construct}.`, + IllegalBreakContinue: _<{| type: "BreakStatement" | "ContinueStatement" |}>( + ({ type }) => + `Unsyntactic ${type === "BreakStatement" ? "break" : "continue"}.`, ), IllegalLanguageModeDirective: _( diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index b72fc0b080ca..e7430cb6505a 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -611,10 +611,8 @@ export default class StatementParser extends ExpressionParser { } } if (i === this.state.labels.length) { - this.raise(Errors.IllegalBreakContinue, { - at: node, - construct: isBreak ? "break" : "continue", - }); + const type = isBreak ? "BreakStatement" : "ContinueStatement"; + this.raise(Errors.IllegalBreakContinue, { at: node, type }); } } From fd50741ea8e4673ee6ba52386d16e850c282a275 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 12 Feb 2022 14:33:27 -0800 Subject: [PATCH 023/104] Begin moving checkLVal to type-based system instead of contextDescription-based system, and fix let error in the process as well. Reviewed by @tolmasky. --- .../babel-parser/src/parse-error/standard.js | 20 +- packages/babel-parser/src/parser/lval.js | 171 ++++++++---------- packages/babel-parser/src/parser/statement.js | 2 +- .../src/plugins/typescript/index.js | 15 +- 4 files changed, 95 insertions(+), 113 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index 4a415c104c07..6f29fb494cd1 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -127,11 +127,13 @@ export default (_: typeof toParseErrorClass) => ({ InvalidIdentifier: _<{| identifier: string |}>( ({ identifier }) => `Invalid identifier ${identifier}.`, ), - InvalidLhs: _<{| construct: string /*"for-loop"*/ |}>( - ({ construct }) => `Invalid left-hand side in ${construct}.`, + InvalidLhs: _<{| inNodeType: string |}>( + ({ inNodeType }) => + `Invalid left-hand side in ${nodeTypeToDescription(inNodeType)}.`, ), - InvalidLhsBinding: _<{| construct: string |}>( - ({ construct }) => `Binding invalid left-hand side in ${construct}.`, + InvalidLhsBinding: _<{| inNodeType: string |}>( + ({ inNodeType }) => + `Binding invalid left-hand side in ${nodeTypeToDescription(inNodeType)}.`, ), InvalidNumber: _("Invalid number."), InvalidOrMissingExponent: _( @@ -382,3 +384,13 @@ export default (_: typeof toParseErrorClass) => ({ "Numeric separator can not be used after leading 0.", ), }); + +// eslint-disable-next-line no-confusing-arrow +const nodeTypeToDescription = type => + type === "ArrayPattern" + ? "array destructuring pattern" + : type === "ObjectPattern" + ? "object destructuring pattern" + : type + .replace(/[a-z][A-Z]/, ([left, right]) => `${left} ${right}`) + .toLowerCase(); diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 6747800ef440..a2c92926f0a3 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -506,123 +506,86 @@ export default class LValParser extends NodeUtils { strict-mode. e.g. `(arguments) => { "use strict "}` * @memberof LValParser */ + checkLVal( - expr: Expression, - contextDescription: string, + expr: Expression | ObjectMethod | ObjectProperty | RestElement, + inNodeType: string = expr.type, bindingType: BindingTypes = BIND_NONE, checkClashes: ?Set, disallowLetBinding?: boolean, strictModeChanged?: boolean = false, ): void { - switch (expr.type) { - case "Identifier": { - const { name } = expr; - if ( - this.state.strict && - // "Global" reserved words have already been checked by parseIdentifier, - // unless they have been found in the id or parameters of a strict-mode - // function in a sloppy context. - (strictModeChanged - ? isStrictBindReservedWord(name, this.inModule) - : isStrictBindOnlyReservedWord(name)) - ) { - this.raise( - bindingType === BIND_NONE - ? Errors.StrictEvalArguments - : Errors.StrictEvalArgumentsBinding, - { at: expr, binding: name }, - ); - } + const type = expr.type; - if (checkClashes) { - if (checkClashes.has(name)) { - this.raise(Errors.ParamDupe, { at: expr }); - } else { - checkClashes.add(name); - } - } - if (disallowLetBinding && name === "let") { - this.raise(Errors.LetInLexicalBinding, { at: expr }); - } - if (!(bindingType & BIND_NONE)) { - this.scope.declareName(name, bindingType, expr.loc.start); - } - break; + // If we find here an ObjectMethod, it's because this was originally + // an ObjectExpression which has then been converted. + // toAssignable already reported this error with a nicer message. + if (this.isObjectMethod(expr)) return; + + if (type === "MemberExpression") { + if (bindingType !== BIND_NONE) { + this.raise(Errors.InvalidPropertyBindingPattern, { at: expr }); } + return; + } - case "MemberExpression": - if (bindingType !== BIND_NONE) { - this.raise(Errors.InvalidPropertyBindingPattern, { - at: expr, - }); - } - break; + if (type === "Identifier") { + const { name } = expr; + if ( + this.state.strict && + // "Global" reserved words have already been checked by parseIdentifier, + // unless they have been found in the id or parameters of a strict-mode + // function in a sloppy context. + (strictModeChanged + ? isStrictBindReservedWord(name, this.inModule) + : isStrictBindOnlyReservedWord(name)) + ) { + this.raise( + bindingType === BIND_NONE + ? Errors.StrictEvalArguments + : Errors.StrictEvalArgumentsBinding, + { at: expr, binding: name }, + ); + } - case "ObjectPattern": - for (let prop of expr.properties) { - if (this.isObjectProperty(prop)) prop = prop.value; - // If we find here an ObjectMethod, it's because this was originally - // an ObjectExpression which has then been converted. - // toAssignable already reported this error with a nicer message. - else if (this.isObjectMethod(prop)) continue; - - this.checkLVal( - prop, - "object destructuring pattern", - bindingType, - checkClashes, - disallowLetBinding, - ); + if (checkClashes) { + if (checkClashes.has(name)) { + this.raise(Errors.ParamDupe, { at: expr }); + } else { + checkClashes.add(name); } - break; + } + if (disallowLetBinding && name === "let") { + this.raise(Errors.LetInLexicalBinding, { at: expr }); + } + if (!(bindingType & BIND_NONE)) { + this.scope.declareName(name, bindingType, expr.loc.start); + } + return; + } - case "ArrayPattern": - for (const elem of expr.elements) { - if (elem) { - this.checkLVal( - elem, - "array destructuring pattern", - bindingType, - checkClashes, - disallowLetBinding, - ); - } - } - break; + const [isRelevantType, key] = LValTraversalKeys[type] || [false, false]; - case "AssignmentPattern": - this.checkLVal( - expr.left, - "assignment pattern", - bindingType, - checkClashes, - ); - break; + if (!key) { + this.raise( + bindingType === BIND_NONE + ? Errors.InvalidLhs + : Errors.InvalidLhsBinding, + { at: expr, inNodeType }, + ); + return; + } - case "RestElement": - this.checkLVal( - expr.argument, - "rest element", - bindingType, - checkClashes, - ); - break; + const relevantType = isRelevantType ? type : inNodeType; - case "ParenthesizedExpression": + for (const child of [].concat(expr[key])) { + if (child) { this.checkLVal( - expr.expression, - "parenthesized expression", + child, + relevantType, bindingType, checkClashes, - ); - break; - - default: { - this.raise( - bindingType === BIND_NONE - ? Errors.InvalidLhs - : Errors.InvalidLhsBinding, - { at: expr, construct: contextDescription }, + disallowLetBinding, ); } } @@ -654,3 +617,13 @@ export default class LValParser extends NodeUtils { return true; } } + +const LValTraversalKeys = Object.assign(Object.create(null), { + AssignmentPattern: [true, "left"], + RestElement: [true, "argument"], + ParenthesizedExpression: [true, "expression"], + ObjectProperty: [false, "value"], + Property: [false, "value"], + ArrayPattern: [true, "elements"], + ObjectPattern: [true, "properties"], +}); diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index e7430cb6505a..d8655a84e7bb 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -1174,7 +1174,7 @@ export default class StatementParser extends ExpressionParser { if (init.type === "AssignmentPattern") { this.raise(Errors.InvalidLhs, { at: init, - construct: "for-loop", + inNodeType: "for-loop", }); } diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 7480587445f4..6f6e46ea5c66 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3236,7 +3236,7 @@ export default (superClass: Class): Class => checkLVal( expr: N.Expression, - contextDescription: string, + inNodeType: string, ...args: | [BindingTypes | void] | [BindingTypes | void, ?Set, boolean | void, boolean | void] @@ -3254,22 +3254,19 @@ export default (superClass: Class): Class => case "TSTypeAssertion": if ( /*bindingType*/ !args[0] && - contextDescription !== "parenthesized expression" && + inNodeType !== "ParenthesizedExpression" && !expr.extra?.parenthesized ) { - this.raise(Errors.InvalidLhs, { - at: expr, - construct: contextDescription, - }); + this.raise(Errors.InvalidLhs, { at: expr, inNodeType }); break; } - this.checkLVal(expr.expression, "parenthesized expression", ...args); + this.checkLVal(expr.expression, "ParenthesizedExpression", ...args); return; case "TSNonNullExpression": - this.checkLVal(expr.expression, contextDescription, ...args); + this.checkLVal(expr.expression, inNodeType, ...args); return; default: - super.checkLVal(expr, contextDescription, ...args); + super.checkLVal(expr, inNodeType, ...args); return; } } From b8eb3cef2c692a575864eef8825f19f7504fbbc6 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 14 Feb 2022 11:24:58 -0800 Subject: [PATCH 024/104] Switch to isValidLVal. Reviewed by @tolmasky. --- .../babel-parser/src/parse-error/standard.js | 3 ++ .../babel-parser/src/parser/expression.js | 2 +- packages/babel-parser/src/parser/lval.js | 42 +++++++++++++-- packages/babel-parser/src/parser/statement.js | 14 ++--- packages/babel-parser/src/plugins/estree.js | 4 ++ .../babel-parser/src/plugins/flow/index.js | 26 +++------- .../babel-parser/src/plugins/placeholders.js | 4 +- .../src/plugins/typescript/index.js | 51 +++++++------------ 8 files changed, 80 insertions(+), 66 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard.js index 6f29fb494cd1..19f25db03d5b 100644 --- a/packages/babel-parser/src/parse-error/standard.js +++ b/packages/babel-parser/src/parse-error/standard.js @@ -391,6 +391,9 @@ const nodeTypeToDescription = type => ? "array destructuring pattern" : type === "ObjectPattern" ? "object destructuring pattern" + : type === "ImportDefaultSpecifier" + ? "default import specifier" : type + .replace(/^[A-Z]+(?=[A-Z][a-z])/, "") .replace(/[a-z][A-Z]/, ([left, right]) => `${left} ${right}`) .toLowerCase(); diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index f1e841705aae..d150804f2610 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -334,7 +334,7 @@ export default class ExpressionParser extends LValParser { node.left = left; } - this.checkLVal(left, "assignment expression"); + this.checkLVal(left, "AssignmentExpression"); this.next(); node.right = this.parseMaybeAssign(); return this.finishNode(node, "AssignmentExpression"); diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index a2c92926f0a3..3043a56499f2 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -490,6 +490,20 @@ export default class LValParser extends NodeUtils { return this.finishNode(node, "AssignmentPattern"); } + // eslint-disable-next-line no-unused-vars + isValidLVal(type: string, isParenthesized: boolean, binding: BindingTypes) { + return ( + { + AssignmentPattern: "left", + RestElement: "argument", + ObjectProperty: "value", + ParenthesizedExpression: "expression", + ArrayPattern: "elements", + ObjectPattern: "properties", + }[type] || false + ); + } + /** * Verify that if a node is an lval - something that can be assigned to. * @@ -514,6 +528,7 @@ export default class LValParser extends NodeUtils { checkClashes: ?Set, disallowLetBinding?: boolean, strictModeChanged?: boolean = false, + parentIsParenthesizedExpression?: boolean = false, ): void { const type = expr.type; @@ -564,9 +579,15 @@ export default class LValParser extends NodeUtils { return; } - const [isRelevantType, key] = LValTraversalKeys[type] || [false, false]; + const validity = this.isValidLVal( + expr.type, + parentIsParenthesizedExpression || expr.extra?.parenthesized, + bindingType, + ); - if (!key) { + if (validity === true) return; + + if (validity === false) { this.raise( bindingType === BIND_NONE ? Errors.InvalidLhs @@ -576,7 +597,15 @@ export default class LValParser extends NodeUtils { return; } - const relevantType = isRelevantType ? type : inNodeType; + const isParenthesizedExpression = + type === "ParenthesizedExpression" || Array.isArray(validity); + const key = Array.isArray(validity) ? validity[1] : validity; + const relevantType = + type === "ArrayPattern" || + type === "ObjectPattern" || + type === "ParenthesizedExpression" + ? type + : inNodeType; for (const child of [].concat(expr[key])) { if (child) { @@ -586,11 +615,17 @@ export default class LValParser extends NodeUtils { bindingType, checkClashes, disallowLetBinding, + strictModeChanged, + isParenthesizedExpression, ); } } } + LValTraversalForType(type: string): [boolean, string | false] | false { + return LValTraversalKeys[type] || false; + } + checkToRestConversion(node: SpreadElement): void { if ( node.argument.type !== "Identifier" && @@ -619,6 +654,7 @@ export default class LValParser extends NodeUtils { } const LValTraversalKeys = Object.assign(Object.create(null), { + AssignmentExpression: [true, "left"], AssignmentPattern: [true, "left"], RestElement: [true, "argument"], ParenthesizedExpression: [true, "expression"], diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index d8655a84e7bb..3196264ac28d 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -2454,11 +2454,14 @@ export default class StatementParser extends ExpressionParser { node: N.ImportDeclaration, specifier: N.Node, type: string, - contextDescription: string, ): void { specifier.local = this.parseIdentifier(); - this.checkLVal(specifier.local, contextDescription, BIND_LEXICAL); - node.specifiers.push(this.finishNode(specifier, type)); + node.specifiers.push(this.finishImportSpecifier(specifier, type)); + } + + finishImportSpecifier(specifier, type) { + this.checkLVal(specifier.local, type, BIND_LEXICAL); + return this.finishNode(specifier, type); } /** @@ -2583,7 +2586,6 @@ export default class StatementParser extends ExpressionParser { node, this.startNode(), "ImportDefaultSpecifier", - "default import specifier", ); return true; } @@ -2600,7 +2602,6 @@ export default class StatementParser extends ExpressionParser { node, specifier, "ImportNamespaceSpecifier", - "import namespace specifier", ); return true; } @@ -2663,8 +2664,7 @@ export default class StatementParser extends ExpressionParser { specifier.local = cloneIdentifier(imported); } } - this.checkLVal(specifier.local, "import specifier", BIND_LEXICAL); - return this.finishNode(specifier, "ImportSpecifier"); + return this.finishImportSpecifier(specifier, "ImportSpecifier"); } // This is used in flow and typescript plugin diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index 5195b35aabde..71f9b9d4bc49 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -343,6 +343,10 @@ export default (superClass: Class): Class => return (node: any); } + isValidLVal(type: string, ...rest) { + return { Property: "value" }[type] || super.isValidLVal(type, ...rest); + } + isAssignable(node: N.Node, isBinding?: boolean): boolean { if (node != null && this.isObjectProperty(node)) { return this.isAssignable(node.value, isBinding); diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index df47b6529e04..4136067c7195 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -2421,21 +2421,10 @@ export default (superClass: Class): Class => return node; } - checkLVal( - expr: N.Expression, - ...args: - | [string, BindingTypes | void] - | [ - string, - BindingTypes | void, - ?Set, - boolean | void, - boolean | void, - ] - ): void { - if (expr.type !== "TypeCastExpression") { - return super.checkLVal(expr, ...args); - } + isValidLVal(type: string, ...rest) { + return ( + { TypeCastExpression: true }[type] || super.isValidLVal(type, ...rest) + ); } // parse class property type annotations @@ -2670,7 +2659,6 @@ export default (superClass: Class): Class => node: N.ImportDeclaration, specifier: N.Node, type: string, - contextDescription: string, ): void { specifier.local = hasTypeImportKind(node) ? this.flowParseRestrictedIdentifier( @@ -2679,8 +2667,7 @@ export default (superClass: Class): Class => ) : this.parseIdentifier(); - this.checkLVal(specifier.local, contextDescription, BIND_LEXICAL); - node.specifiers.push(this.finishNode(specifier, type)); + node.specifiers.push(this.finishImportSpecifier(specifier, type)); } // parse typeof and type imports @@ -2806,8 +2793,7 @@ export default (superClass: Class): Class => ); } - this.checkLVal(specifier.local, "import specifier", BIND_LEXICAL); - return this.finishNode(specifier, "ImportSpecifier"); + return this.finishImportSpecifier(specifier, "ImportSpecifier"); } parseBindingAtom(): N.Pattern { diff --git a/packages/babel-parser/src/plugins/placeholders.js b/packages/babel-parser/src/plugins/placeholders.js index cf3723f21cba..5740957ef105 100644 --- a/packages/babel-parser/src/plugins/placeholders.js +++ b/packages/babel-parser/src/plugins/placeholders.js @@ -139,8 +139,8 @@ export default (superClass: Class): Class => ); } - checkLVal(expr: N.Expression): void { - if (expr.type !== "Placeholder") super.checkLVal(...arguments); + isValidLVal(type: string, ...rest) { + return type === "Placeholder" || super.isValidLVal(type, ...rest); } toAssignable(node: N.Node): N.Node { diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 6f6e46ea5c66..35ce10d8fe2c 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -29,6 +29,7 @@ import { BIND_TS_NAMESPACE, BIND_CLASS, BIND_LEXICAL, + BIND_NONE, } from "../../util/scopeflags"; import TypeScriptScopeHandler from "./scope"; import * as charCodes from "charcodes"; @@ -3234,41 +3235,25 @@ export default (superClass: Class): Class => } } - checkLVal( - expr: N.Expression, - inNodeType: string, - ...args: - | [BindingTypes | void] - | [BindingTypes | void, ?Set, boolean | void, boolean | void] - ): void { - switch (expr.type) { - case "TSTypeCastExpression": + isValidLVal(type: string, isParenthesized: boolean, binding: BindingTypes) { + return ( + { // Allow "typecasts" to appear on the left of assignment expressions, // because it may be in an arrow function. // e.g. `const f = (foo: number = 0) => foo;` - return; - case "TSParameterProperty": - this.checkLVal(expr.parameter, "parameter property", ...args); - return; - case "TSAsExpression": - case "TSTypeAssertion": - if ( - /*bindingType*/ !args[0] && - inNodeType !== "ParenthesizedExpression" && - !expr.extra?.parenthesized - ) { - this.raise(Errors.InvalidLhs, { at: expr, inNodeType }); - break; - } - this.checkLVal(expr.expression, "ParenthesizedExpression", ...args); - return; - case "TSNonNullExpression": - this.checkLVal(expr.expression, inNodeType, ...args); - return; - default: - super.checkLVal(expr, inNodeType, ...args); - return; - } + TSTypeCastExpression: true, + TSParameterProperty: "parameter", + TSNonNullExpression: "expression", + TSAsExpression: (binding !== BIND_NONE || isParenthesized) && [ + true, + "expression", + ], + TSTypeAssertion: (binding !== BIND_NONE || isParenthesized) && [ + true, + "expression", + ], + }[type] || super.isValidLVal(type, isParenthesized, binding) + ); } parseBindingAtom(): N.Pattern { @@ -3676,7 +3661,7 @@ export default (superClass: Class): Class => node[rightOfAsKey] = cloneIdentifier(node[leftOfAsKey]); } if (isImport) { - this.checkLVal(node[rightOfAsKey], "import specifier", BIND_LEXICAL); + this.checkLVal(node[rightOfAsKey], "ImportSpecifier", BIND_LEXICAL); } } }; From 455512ee0e97776b83e052da522693f85ac26b8f Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 14 Feb 2022 11:54:21 -0800 Subject: [PATCH 025/104] Don't use checkLVal for class names. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/lval.js | 6 +++++- packages/babel-parser/src/parser/statement.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 3043a56499f2..940d9e5a24a2 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -574,7 +574,7 @@ export default class LValParser extends NodeUtils { this.raise(Errors.LetInLexicalBinding, { at: expr }); } if (!(bindingType & BIND_NONE)) { - this.scope.declareName(name, bindingType, expr.loc.start); + this.declareNameFromIdentifier(expr, bindingType); } return; } @@ -622,6 +622,10 @@ export default class LValParser extends NodeUtils { } } + declareNameFromIdentifier(identifier: N.Identifier, binding: BindingType) { + this.scope.declareName(identifier.name, binding, identifier.loc.start); + } + LValTraversalForType(type: string): [boolean, string | false] | false { return LValTraversalKeys[type] || false; } diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 3196264ac28d..0f84822739d2 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -1931,7 +1931,7 @@ export default class StatementParser extends ExpressionParser { if (tokenIsIdentifier(this.state.type)) { node.id = this.parseIdentifier(); if (isStatement) { - this.checkLVal(node.id, "class name", bindingType); + this.declareNameFromIdentifier(node.id, bindingType); } } else { if (optionalId || !isStatement) { From b040a52ab88cc3d7e2aafcdd2237ab5f9fd75486 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 09:27:05 -0800 Subject: [PATCH 026/104] Fix some Flow errors. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/expression.js | 5 +++-- packages/babel-parser/src/parser/lval.js | 12 ++++++------ packages/babel-parser/src/parser/statement.js | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index d150804f2610..71fd7cc63282 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -555,12 +555,13 @@ export default class ExpressionParser extends LValParser { parseHackPipeBody(): N.Expression { const { startLoc } = this.state; const body = this.parseMaybeAssign(); + const description = invalidHackPipeBodies.get(body.type); // TODO: Check how to handle type casts in Flow and TS once they are supported - if (invalidHackPipeBodies.has(body.type) && !body.extra?.parenthesized) { + if (!!description && !body.extra?.parenthesized) { this.raise(Errors.PipeUnparenthesizedBody, { at: startLoc, - expressionDescription: invalidHackPipeBodies.get(body.type), + expressionDescription: description, }); } if (!this.topicReferenceWasUsedInCurrentContext()) { diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 940d9e5a24a2..1a699ac1fd75 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -13,9 +13,9 @@ import type { SpreadElement, /*:: ObjectOrClassMember, */ /*:: ClassMember, */ - /*:: ObjectMember, */ + ObjectMember, /*:: TsNamedTypeElementBase, */ - /*:: Identifier, */ + Identifier, /*:: PrivateName, */ /*:: ObjectExpression, */ /*:: ObjectPattern, */ @@ -522,7 +522,7 @@ export default class LValParser extends NodeUtils { */ checkLVal( - expr: Expression | ObjectMethod | ObjectProperty | RestElement, + expr: Expression | ObjectMember | RestElement, inNodeType: string = expr.type, bindingType: BindingTypes = BIND_NONE, checkClashes: ?Set, @@ -544,7 +544,7 @@ export default class LValParser extends NodeUtils { return; } - if (type === "Identifier") { + if (expr.type === "Identifier") { const { name } = expr; if ( this.state.strict && @@ -574,7 +574,7 @@ export default class LValParser extends NodeUtils { this.raise(Errors.LetInLexicalBinding, { at: expr }); } if (!(bindingType & BIND_NONE)) { - this.declareNameFromIdentifier(expr, bindingType); + this.declareNameFromIdentifier((expr: Identifier), bindingType); } return; } @@ -622,7 +622,7 @@ export default class LValParser extends NodeUtils { } } - declareNameFromIdentifier(identifier: N.Identifier, binding: BindingType) { + declareNameFromIdentifier(identifier: Identifier, binding: BindingTypes) { this.scope.declareName(identifier.name, binding, identifier.loc.start); } diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 0f84822739d2..b024554d0647 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -2459,7 +2459,7 @@ export default class StatementParser extends ExpressionParser { node.specifiers.push(this.finishImportSpecifier(specifier, type)); } - finishImportSpecifier(specifier, type) { + finishImportSpecifier(specifier: N.Node, type: string) { this.checkLVal(specifier.local, type, BIND_LEXICAL); return this.finishNode(specifier, type); } From 833d6314c98cdb51c288f74e237ee6791b1f1a0c Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 11:18:05 -0800 Subject: [PATCH 027/104] Temporary fix for Flow error. Reviewed by @tolmasky. --- packages/babel-parser/src/plugins/flow/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 4136067c7195..2b3f9eda798c 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -3339,7 +3339,8 @@ export default (superClass: Class): Class => at: loc, enumName, memberName, - explicitType, + // FIXME: Handle this being missing better. + explicitType: explicitType || "unknown", }, ); } From dd74fc7af5815e9b00392d3595dd0fac86ce4268 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 11:37:28 -0800 Subject: [PATCH 028/104] Remove unused stuff. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/lval.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 1a699ac1fd75..d7026f048048 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -626,10 +626,6 @@ export default class LValParser extends NodeUtils { this.scope.declareName(identifier.name, binding, identifier.loc.start); } - LValTraversalForType(type: string): [boolean, string | false] | false { - return LValTraversalKeys[type] || false; - } - checkToRestConversion(node: SpreadElement): void { if ( node.argument.type !== "Identifier" && @@ -656,14 +652,3 @@ export default class LValParser extends NodeUtils { return true; } } - -const LValTraversalKeys = Object.assign(Object.create(null), { - AssignmentExpression: [true, "left"], - AssignmentPattern: [true, "left"], - RestElement: [true, "argument"], - ParenthesizedExpression: [true, "expression"], - ObjectProperty: [false, "value"], - Property: [false, "value"], - ArrayPattern: [true, "elements"], - ObjectPattern: [true, "properties"], -}); From c5c21c56e44693726293407516b74e4368df5c01 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 12:27:00 -0800 Subject: [PATCH 029/104] Try this. Reviewed by @tolmasky. --- packages/babel-parser/src/plugins/flow/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 2b3f9eda798c..dfdc4a8a7fcf 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -167,7 +167,9 @@ const FlowErrors = toParseErrorClasses( PatternIsOptional: _( "A binding pattern parameter cannot be optional in an implementation signature.", // For consistency in TypeScript and Flow error codes - !process.env.BABEL_8_BREAKING && { reasonCode: "OptionalBindingPattern" }, + !process.env.BABEL_8_BREAKING + ? { reasonCode: "OptionalBindingPattern" } + : {}, ), SetterMayNotHaveThisParam: _("A setter cannot have a `this` parameter."), SpreadVariance: _("Spread properties cannot have variance."), From afefe3a34310d9a2b365a18e578d9a8ebf54addf Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 15:41:03 -0800 Subject: [PATCH 030/104] Don't use Object.fromEntries. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 30 +++++++++++------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 0d06245c8e87..053d0b34515d 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -3,7 +3,7 @@ import { Position } from "./util/location"; import type { NodeBase } from "./types"; -const { assign: ObjectAssign } = Object; +const { assign: ObjectAssign, keys: ObjectKeys } = Object; const ManuallyAssignedErrorMessages = new WeakMap, string>(); @@ -101,21 +101,19 @@ export function toParseErrorClasses( toClasses: (typeof toParseErrorClass) => T, { syntaxPlugin }: Object = {}, ): T { - // $FlowIgnore - return Object.fromEntries( - Object.entries(toClasses(toParseErrorClass)).map( - ([reasonCode, ParseErrorClass: Class>]) => [ - reasonCode, - // $FlowIgnore - ObjectAssign( - ParseErrorClass, - // $FlowIgnore - !ParseErrorClass.reasonCode && { reasonCode }, - !!syntaxPlugin && { syntaxPlugin }, - ), - ], - ), - ); + const classes = toClasses(toParseErrorClass); + + for (const reasonCode of ObjectKeys(classes)) { + const ParseErrorClass = classes[reasonCode]; + + classes[reasonCode] = ObjectAssign( + ParseErrorClass, + !ParseErrorClass.reasonCode && { reasonCode }, + !!syntaxPlugin && { syntaxPlugin }, + ); + } + + return classes; } export type RaiseProperties = {| From ee26f35fe9970228b48b8ddcd3d344aabb38f23c Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 16:01:58 -0800 Subject: [PATCH 031/104] Fix linter error. Reviewed by @tolmasky. --- packages/babel-parser/src/plugins/flow/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index dfdc4a8a7fcf..75f22641947d 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -20,7 +20,6 @@ import * as charCodes from "charcodes"; import { isIteratorStart } from "../../util/identifier"; import FlowScopeHandler from "./scope"; import { - type BindingTypes, BIND_LEXICAL, BIND_VAR, BIND_FUNCTION, From b10acd0a41da49e0f38cf258d0d91db608ab4507 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 16:33:22 -0800 Subject: [PATCH 032/104] Fix for SyntaxError. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 053d0b34515d..768d13300da0 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -48,13 +48,6 @@ export class ParseError extends SyntaxError { } } -// We do this for backwards compatibility so that all errors just have the -// "SyntaxError" name in their messages instead of leaking the private subclass -// name. -Object.defineProperty(ParseError.prototype.constructor, "name", { - value: "SyntaxError", -}); - declare function toParseErrorClass( T, ?{ code?: ParseErrorCode } | boolean, @@ -106,6 +99,13 @@ export function toParseErrorClasses( for (const reasonCode of ObjectKeys(classes)) { const ParseErrorClass = classes[reasonCode]; + // We do this for backwards compatibility so that all errors just have the + // "SyntaxError" name in their messages instead of leaking the private + // subclass name. + Object.defineProperty(ParseErrorClass.prototype.constructor, "name", { + value: "SyntaxError", + }); + classes[reasonCode] = ObjectAssign( ParseErrorClass, !ParseErrorClass.reasonCode && { reasonCode }, From 2608cddf744ad7fa5a488f20401fddebdaca3d9a Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 17:47:43 -0800 Subject: [PATCH 033/104] Add TS1039 Initializers are not allowed in ambient contexts. to error-codes.js. Reviewed by @tolmasky. --- scripts/parser-tests/typescript/error-codes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/parser-tests/typescript/error-codes.js b/scripts/parser-tests/typescript/error-codes.js index 8274005ce313..54a7b046a4e6 100644 --- a/scripts/parser-tests/typescript/error-codes.js +++ b/scripts/parser-tests/typescript/error-codes.js @@ -17,6 +17,7 @@ export default [ "TS1029", // '{0}' modifier must precede '{1}' modifier. "TS1030", // '{0}' modifier already seen. "TS1031", // '{0}' modifier cannot appear on a class element. + "TS1039", // Initializers are not allowed in ambient contexts. "TS1042", // '{0}' modifier cannot be used here. "TS1048", // A rest parameter cannot have an initializer. "TS1053", // A 'set' accessor cannot have rest parameter. From 1f908f41f99e44d885a1d8f90e4009e64a5dea26 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 17:57:44 -0800 Subject: [PATCH 034/104] Add TS1155 'const' declarations must be initialized. Reviewed by @tolmasky. --- scripts/parser-tests/typescript/error-codes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/parser-tests/typescript/error-codes.js b/scripts/parser-tests/typescript/error-codes.js index 54a7b046a4e6..1715253f477f 100644 --- a/scripts/parser-tests/typescript/error-codes.js +++ b/scripts/parser-tests/typescript/error-codes.js @@ -37,6 +37,7 @@ export default [ "TS1123", // Variable declaration list cannot be empty. "TS1141", // String literal expected. "TS1142", // Line break not permitted here. + "TS1155", // 'const' declarations must be initialized. "TS1162", // An object member cannot be declared optional. "TS1163", // A 'yield' expression is only allowed in a generator body. "TS1184", // Modifiers cannot appear here. From b169766394c3e8cc0be77a653b428b2a2cde8d52 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 16 Feb 2022 06:53:47 -0800 Subject: [PATCH 035/104] Allow consts with literals. Reviewed by @tolmasky. --- .../src/plugins/typescript/index.js | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 35ce10d8fe2c..a25bac0d4df2 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -93,6 +93,9 @@ const TSErrors = toParseErrorClasses( ConstructorHasTypeParameters: _( "Type parameters cannot appear on a constructor declaration.", ), + ConstInitiailizerMustBeStringOrNumericLiteralOrLiteralEnumReference: _( + "A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.", + ), // kind? DeclareAccessor: _<{| accessorKind: "get" | "set" |}>( ({ accessorKind }) => `'declare' is not allowed in ${accessorKind}ters.`, @@ -140,7 +143,7 @@ const TSErrors = toParseErrorClasses( IndexSignatureHasStatic: _( "Index signatures cannot have the 'static' modifier.", ), - InitializerNotAllowInAmbientContext: _( + InitializerNotAllowedInAmbientContext: _( "Initializers are not allowed in ambient contexts.", ), InvalidModifierOnTypeMember: _<{| modifier: TsModifier |}>( @@ -2529,13 +2532,30 @@ export default (superClass: Class): Class => allowMissingInitializer || isAmbientContext, ); - if (isAmbientContext) { - for (const declarator of declaration.declarations) { - if (declarator.init) { - this.raise(TSErrors.InitializerNotAllowInAmbientContext, { - at: declarator.init, - }); - } + if (!isAmbientContext) return declaration; + + for (const { id, init } of declaration.declarations) { + + // Empty initializer is the easy case that we want. + if (!init) continue; + + // var and let aren't ever allowed initializers. + // + // If a const declaration has no type annotation and is initiailized to a + // string literal, numeric literal, or (FIXME) enum reference, then it is + // allowed. + if (kind !== "const" || !!id.typeAnnotation) { + this.raise(TSErrors.InitializerNotAllowedInAmbientContext, { + at: init, + }); + } else if ( + init.type !== "BooleanLiteral" && + init.type !== "NumericLiteral" && + init.type !== "BigIntLiteral") { + this.raise( + TSErrors.ConstInitiailizerMustBeStringOrNumericLiteralOrLiteralEnumReference, + { at: init } + ); } } From c69ee8510280ff0dd310c61debc9caebd95d873c Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 16 Feb 2022 07:48:16 -0800 Subject: [PATCH 036/104] Add enum support. Reviewed by @tolmasky. --- .../src/plugins/typescript/index.js | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index a25bac0d4df2..b32e3af925e8 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -90,12 +90,12 @@ const TSErrors = toParseErrorClasses( ClassMethodHasReadonly: _( "Class methods cannot have the 'readonly' modifier.", ), - ConstructorHasTypeParameters: _( - "Type parameters cannot appear on a constructor declaration.", - ), ConstInitiailizerMustBeStringOrNumericLiteralOrLiteralEnumReference: _( "A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.", ), + ConstructorHasTypeParameters: _( + "Type parameters cannot appear on a constructor declaration.", + ), // kind? DeclareAccessor: _<{| accessorKind: "get" | "set" |}>( ({ accessorKind }) => `'declare' is not allowed in ${accessorKind}ters.`, @@ -288,6 +288,16 @@ function tsIsAccessModifier(modifier: string): boolean %checks { export default (superClass: Class): Class => class extends superClass { + parseTopLevel(file, program) { + // If we're in a .d.ts file, then the whole thing is an ambient context. + // + // TODO: Maybe provide am option to parse as .d.ts instead of .ts, so you + // don't have to necessarily rely on the filename. + return this.filename && /\.d\.ts$/.test(this.filename) + ? this.tsInAmbientContext(() => super.parseTopLevel(file, program)) + : super.parseTopLevel(file, program); + } + getScopeHandler(): Class { return TypeScriptScopeHandler; } @@ -2535,7 +2545,6 @@ export default (superClass: Class): Class => if (!isAmbientContext) return declaration; for (const { id, init } of declaration.declarations) { - // Empty initializer is the easy case that we want. if (!init) continue; @@ -2549,12 +2558,16 @@ export default (superClass: Class): Class => at: init, }); } else if ( + init.type !== "StringLiteral" && init.type !== "BooleanLiteral" && init.type !== "NumericLiteral" && - init.type !== "BigIntLiteral") { + init.type !== "BigIntLiteral" && + (init.type !== "TemplateLiteral" || init.expressions.length > 0) && + !isPossiblyLiteralEnum(init) + ) { this.raise( TSErrors.ConstInitiailizerMustBeStringOrNumericLiteralOrLiteralEnumReference, - { at: init } + { at: init }, ); } } @@ -3685,3 +3698,27 @@ export default (superClass: Class): Class => } } }; + +function isPossiblyLiteralEnum(expression: N.Expression): boolean { + if (expression.type !== "MemberExpression") return false; + + const { computed, property } = expression; + + if ( + computed && + property.type !== "StringLiteral" && + (property.type !== "TemplateLiteral" || property.expressions.length > 0) + ) { + return false; + } + + return isUncomputedMemberExpressionChain(expression.object); +} + +function isUncomputedMemberExpressionChain(expression: N.Expression): boolean { + if (expression.type === "Identifier") return true; + if (expression.type !== "MemberExpression") return false; + if (expression.computed) return false; + + return isUncomputedMemberExpressionChain(expression.object); +} From 4fcecf3a371327fec4fb56d5036bc50417ae2a76 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 16 Feb 2022 09:31:57 -0800 Subject: [PATCH 037/104] Add filename to test runner to correctly identify .d.ts files. Reviewed by @tolmasky. --- .../src/plugins/typescript/index.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index b32e3af925e8..0054f94c2c60 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -288,16 +288,6 @@ function tsIsAccessModifier(modifier: string): boolean %checks { export default (superClass: Class): Class => class extends superClass { - parseTopLevel(file, program) { - // If we're in a .d.ts file, then the whole thing is an ambient context. - // - // TODO: Maybe provide am option to parse as .d.ts instead of .ts, so you - // don't have to necessarily rely on the filename. - return this.filename && /\.d\.ts$/.test(this.filename) - ? this.tsInAmbientContext(() => super.parseTopLevel(file, program)) - : super.parseTopLevel(file, program); - } - getScopeHandler(): Class { return TypeScriptScopeHandler; } @@ -3555,7 +3545,17 @@ export default (superClass: Class): Class => } shouldParseAsAmbientContext(): boolean { - return !!this.getPluginOption("typescript", "dts"); + // If the user took the time to provide a filename and it has the ".d.ts" + // file extension, then assume they want ambient context parsing unless + // they explicitly specify otherwise. + const DTS = this.getPluginOption("typescript", "dts"); + const DTSTruthy = !!DTS; + const DTSFalse = DTS === false; + + return ( + DTSTruthy || + (!DTSFalse && !!this.filename && /\.d\.ts$/.test(this.filename)) + ); } parse() { From 6fa0c4ae4b45587f149fa3cc3952a9e0990a24b6 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 16 Feb 2022 19:02:59 -0800 Subject: [PATCH 038/104] Only ignore reserved words in ambient contexts. Parse interface in parseStatementContent. Fix enums. Reviewed by @tolmasky. --- .../src/plugins/typescript/index.js | 41 +++++++++---------- .../parser-tests/typescript/error-codes.js | 4 +- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 0054f94c2c60..06c3fe44e2e1 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -1957,22 +1957,6 @@ export default (superClass: Class): Class => } break; - case "enum": - if (next || tokenIsIdentifier(this.state.type)) { - if (next) this.next(); - return this.tsParseEnumDeclaration(node, /* isConst */ false); - } - break; - - case "interface": - if ( - this.tsCheckLineTerminator(next) && - tokenIsIdentifier(this.state.type) - ) { - return this.tsParseInterfaceDeclaration(node); - } - break; - case "module": if (this.tsCheckLineTerminator(next)) { if (this.match(tt.string)) { @@ -2382,15 +2366,16 @@ export default (superClass: Class): Class => } checkReservedWord( - word: string, // eslint-disable-line no-unused-vars - startLoc: Position, // eslint-disable-line no-unused-vars - checkKeywords: boolean, // eslint-disable-line no-unused-vars - // eslint-disable-next-line no-unused-vars + word: string, + startLoc: Position, + checkKeywords: boolean, isBinding: boolean, ): void { - // Don't bother checking for TypeScript code. // Strict mode words may be allowed as in `declare namespace N { const static: number; }`. // And we have a type checker anyway, so don't bother having the parser do it. + if (!this.state.isAmbientContext) { + super.checkReservedWord(word, startLoc, checkKeywords, isBinding); + } } /* @@ -2574,7 +2559,21 @@ export default (superClass: Class): Class => this.expectContextual(tt._enum); return this.tsParseEnumDeclaration(node, /* isConst */ true); } + } else if (this.state.type === tt._enum) { + const node: N.TsEnumDeclaration = this.startNode(); + this.next(); + return this.tsParseEnumDeclaration(node, /* isConst */ false); + } else if (this.state.type === tt._interface) { + const interfaceNode = this.startNode(); + if ( + this.tsCheckLineTerminator(true) && + tokenIsIdentifier(this.state.type) + ) { + return this.tsParseInterfaceDeclaration(interfaceNode); + } + return super.parseStatementContent(context, topLevel); } + return super.parseStatementContent(context, topLevel); } diff --git a/scripts/parser-tests/typescript/error-codes.js b/scripts/parser-tests/typescript/error-codes.js index 1715253f477f..96885be0eaf1 100644 --- a/scripts/parser-tests/typescript/error-codes.js +++ b/scripts/parser-tests/typescript/error-codes.js @@ -53,8 +53,10 @@ export default [ "TS1247", // A type literal property cannot have an initializer. "TS1248", // A class member cannot have the 'const' keyword. "TS1308", // 'await' expression is only allowed within an async function. - "TS2337", // Super calls are not permitted outside constructors or in nested functions inside constructors. // "TS2300", // Duplicate identifier '{0}'. + "TS2337", // Super calls are not permitted outside constructors or in nested functions inside constructors. + "TS2390", // Constructor implementation is missing. + "TS2391", // Function implementation is missing or not immediately following the declaration. "TS2364", // The left-hand side of an assignment expression must be a variable or a property access. // "TS2371", // A parameter initializer is only allowed in a function or constructor implementation. // "TS2393", // Duplicate function implementation. From 00644eee176aa0faa42db58988a5d240715d30bb Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Thu, 17 Feb 2022 18:24:34 -0800 Subject: [PATCH 039/104] Fix simple parameter error. Reviewed by @tolmasky. --- .../babel-parser/src/parser/expression.js | 6 ++- .../src/plugins/typescript/index.js | 47 +++++++++++++++---- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 71fd7cc63282..681c991d78a7 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -2500,11 +2500,15 @@ export default class ExpressionParser extends LValParser { this.expressionScope.exit(); } + isSimpleParameter(node: N.Pattern | N.TSParameterProperty) { + return node.type === "Identifier"; + } + isSimpleParamList( params: $ReadOnlyArray, ): boolean { for (let i = 0, len = params.length; i < len; i++) { - if (params[i].type !== "Identifier") return false; + if (!this.isSimpleParameter(params[i])) return false; } return true; } diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 06c3fe44e2e1..f2fb894fb093 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -84,6 +84,9 @@ const TSErrors = toParseErrorClasses( AccesorCannotHaveTypeParameters: _( "An accessor cannot have type parameters.", ), + CannotFindName: _<{| name: string |}>( + ({ name }) => `Cannot find name '${name}'.`, + ), ClassMethodHasDeclare: _( "Class methods cannot have the 'declare' modifier.", ), @@ -566,8 +569,13 @@ export default (superClass: Class): Class => return this.finishNode(node, "TSImportType"); } - tsParseEntityName(allowReservedWords: boolean): N.TsEntityName { - let entity: N.TsEntityName = this.parseIdentifier(); + tsParseEntityName( + allowReservedWords: boolean, + allowConst: boolean = false, + ): N.TsEntityName { + let entity: N.TsEntityName = this.parseIdentifier( + allowConst && this.match(tt._const), + ); while (this.eat(tt.dot)) { const node: N.TsQualifiedName = this.startNodeAtNode(entity); node.left = entity; @@ -577,9 +585,12 @@ export default (superClass: Class): Class => return entity; } - tsParseTypeReference(): N.TsTypeReference { + tsParseTypeReference(allowConst: boolean = false): N.TsTypeReference { const node: N.TsTypeReference = this.startNode(); - node.typeName = this.tsParseEntityName(/* allowReservedWords */ false); + node.typeName = this.tsParseEntityName( + /* allowReservedWords */ false, + allowConst, + ); if (!this.hasPrecedingLineBreak() && this.match(tt.lt)) { node.typeParameters = this.tsParseTypeArguments(); } @@ -654,11 +665,23 @@ export default (superClass: Class): Class => } tsTryNextParseConstantContext(): ?N.TsTypeReference { - if (this.lookahead().type === tt._const) { - this.next(); - return this.tsParseTypeReference(); + if (this.lookahead().type !== tt._const) return null; + + this.next(); + const typeReference = this.tsParseTypeReference(/* allowConst */ true); + + // If the type reference has type parameters, then you are using it as a + // type and not as a const signifier. We'll *never* be able to find this + // name, since const isn't allowed as a type name. So in this instance we + // get to pretend we're the type checker. + if (typeReference.typeParameters) { + this.raise(TSErrors.CannotFindName, { + at: typeReference.typeName, + name: typeReference.typeName.name, + }); } - return null; + + return typeReference; } // Note: In TypeScript implementation we must provide `yieldContext` and `awaitContext`, @@ -2133,6 +2156,14 @@ export default (superClass: Class): Class => return elt; } + isSimpleParameter(node) { + return ( + (node.type === "TSParameterProperty" && + super.isSimpleParameter(node.parameter)) || + super.isSimpleParameter(node) + ); + } + parseFunctionBodyAndFinish( node: N.BodilessFunctionOrMethodBase, type: string, From b791955fb8a88dc26c453260b3b7fa2d8168d990 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 18 Feb 2022 10:34:05 -0800 Subject: [PATCH 040/104] Type references should allow reserved words (I believe we were just getting away with the fact that we turned reserved words off before). Reviewed by @tolmasky. --- packages/babel-parser/src/plugins/typescript/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index f2fb894fb093..f03e129f53b2 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -588,7 +588,7 @@ export default (superClass: Class): Class => tsParseTypeReference(allowConst: boolean = false): N.TsTypeReference { const node: N.TsTypeReference = this.startNode(); node.typeName = this.tsParseEntityName( - /* allowReservedWords */ false, + /* allowReservedWords */ true, allowConst, ); if (!this.hasPrecedingLineBreak() && this.match(tt.lt)) { From 708248b1a33f4bbc6d92b1c81f75fd07c82cb6f8 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 18 Feb 2022 11:10:56 -0800 Subject: [PATCH 041/104] Allow import and export in TypeScript regardless of module or script. Reviewed by @tolmasky. --- packages/babel-parser/src/plugins/typescript/index.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index f03e129f53b2..3d6c24e91cb9 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -295,6 +295,13 @@ export default (superClass: Class): Class => return TypeScriptScopeHandler; } + // import and export are always allowed in TypeScript, regardless of whether + // you are parsing as "script" or "module". However, this is different than + // `allowImportExportEverywhere`, as it is only allowed in the same places + // that it would be allowed in a module context, not *anywhere*. + // eslint-disable-next-line no-unused-vars + assertModuleNodeAllowed(node: N.Node): void {} + tsIsIdentifier(): boolean { // TODO: actually a bit more complex in TypeScript, but shouldn't matter. // See https://github.com/Microsoft/TypeScript/issues/15008 @@ -677,7 +684,7 @@ export default (superClass: Class): Class => if (typeReference.typeParameters) { this.raise(TSErrors.CannotFindName, { at: typeReference.typeName, - name: typeReference.typeName.name, + name: "const", }); } From 6415b8418a63c6c0f7061bba9123b900030738fe Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 18 Feb 2022 13:37:48 -0800 Subject: [PATCH 042/104] Fix declare enum and interface and reorganize the code a bit. Reviewed by @tolmasky. --- .../src/plugins/typescript/index.js | 143 ++++++++++-------- 1 file changed, 77 insertions(+), 66 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 3d6c24e91cb9..18fbb1e24a13 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -1604,7 +1604,11 @@ export default (superClass: Class): Class => tsParseInterfaceDeclaration( node: N.TsInterfaceDeclaration, - ): N.TsInterfaceDeclaration { + properties: { declare?: true } = {}, + ): ?N.TsInterfaceDeclaration { + if (this.hasFollowingLineBreak()) return null; + this.expectContextual(tt._interface); + if (properties.declare) node.declare = properties.declare; if (tokenIsIdentifier(this.state.type)) { node.id = this.parseIdentifier(); this.checkLVal( @@ -1711,14 +1715,16 @@ export default (superClass: Class): Class => tsParseEnumDeclaration( node: N.TsEnumDeclaration, - isConst: boolean, + properties: { const?: true, declare?: true } = {}, ): N.TsEnumDeclaration { - if (isConst) node.const = true; + if (properties.const) node.const = properties.const; + if (properties.declare) node.declare = properties.declare; + this.expectContextual(tt._enum); node.id = this.parseIdentifier(); this.checkLVal( node.id, "typescript enum declaration", - isConst ? BIND_TS_CONST_ENUM : BIND_TS_ENUM, + node.const ? BIND_TS_CONST_ENUM : BIND_TS_ENUM, ); this.expect(tt.braceL); @@ -1887,45 +1893,61 @@ export default (superClass: Class): Class => } return this.tsInAmbientContext(() => { - switch (starttype) { - case tt._function: - nany.declare = true; - return this.parseFunctionStatement( - nany, - /* async */ false, - /* declarationPosition */ true, - ); - case tt._class: - // While this is also set by tsParseExpressionStatement, we need to set it - // before parsing the class declaration to know how to register it in the scope. + if (starttype === tt._function) { + nany.declare = true; + return this.parseFunctionStatement( + nany, + /* async */ false, + /* declarationPosition */ true, + ); + } + + if (starttype === tt._class) { + // While this is also set by tsParseExpressionStatement, we need to set it + // before parsing the class declaration to know how to register it in the scope. + nany.declare = true; + return this.parseClass( + nany, + /* isStatement */ true, + /* optionalId */ false, + ); + } + + if (starttype === tt._enum) { + return this.tsParseEnumDeclaration(nany, { declare: true }); + } + + if (starttype === tt._global) { + return this.tsParseAmbientExternalModuleDeclaration(nany); + } + + if (starttype === tt._const || starttype === tt._var) { + if (!this.match(tt._const) || !this.isLookaheadContextual("enum")) { nany.declare = true; - return this.parseClass( - nany, - /* isStatement */ true, - /* optionalId */ false, - ); - case tt._const: - if (this.match(tt._const) && this.isLookaheadContextual("enum")) { - // `const enum = 0;` not allowed because "enum" is a strict mode reserved word. - this.expect(tt._const); - this.expectContextual(tt._enum); - return this.tsParseEnumDeclaration(nany, /* isConst */ true); - } - // falls through - case tt._var: - kind = kind || this.state.value; - return this.parseVarStatement(nany, kind, true); - case tt._global: - return this.tsParseAmbientExternalModuleDeclaration(nany); - default: { - if (tokenIsIdentifier(starttype)) { - return this.tsParseDeclaration( - nany, - this.state.value, - /* next */ true, - ); - } + return this.parseVarStatement(nany, kind || this.state.value, true); } + + // `const enum = 0;` not allowed because "enum" is a strict mode reserved word. + this.expect(tt._const); + return this.tsParseEnumDeclaration(nany, { + const: true, + declare: true, + }); + } + + if (starttype === tt._interface) { + const result = this.tsParseInterfaceDeclaration(nany, { + declare: true, + }); + if (result) return result; + } + + if (tokenIsIdentifier(starttype)) { + return this.tsParseDeclaration( + nany, + this.state.value, + /* next */ true, + ); } }); } @@ -2534,9 +2556,7 @@ export default (superClass: Class): Class => // export default interface allowed in: // https://github.com/Microsoft/TypeScript/pull/16040 if (this.match(tt._interface)) { - const interfaceNode = this.startNode(); - this.next(); - const result = this.tsParseInterfaceDeclaration(interfaceNode); + const result = this.tsParseInterfaceDeclaration(this.startNode()); if (result) return result; } @@ -2589,27 +2609,19 @@ export default (superClass: Class): Class => } parseStatementContent(context: ?string, topLevel: ?boolean): N.Statement { - if (this.state.type === tt._const) { - const ahead = this.lookahead(); - if (ahead.type === tt._enum) { - const node: N.TsEnumDeclaration = this.startNode(); - this.next(); // eat 'const' - this.expectContextual(tt._enum); - return this.tsParseEnumDeclaration(node, /* isConst */ true); - } - } else if (this.state.type === tt._enum) { + if (this.match(tt._const) && this.isLookaheadContextual("enum")) { const node: N.TsEnumDeclaration = this.startNode(); - this.next(); - return this.tsParseEnumDeclaration(node, /* isConst */ false); - } else if (this.state.type === tt._interface) { - const interfaceNode = this.startNode(); - if ( - this.tsCheckLineTerminator(true) && - tokenIsIdentifier(this.state.type) - ) { - return this.tsParseInterfaceDeclaration(interfaceNode); - } - return super.parseStatementContent(context, topLevel); + this.expect(tt._const); // eat 'const' + return this.tsParseEnumDeclaration(node, { const: true }); + } + + if (this.isContextual(tt._enum)) { + return this.tsParseEnumDeclaration(this.startNode()); + } + + if (this.isContextual(tt._interface)) { + const result = this.tsParseInterfaceDeclaration(this.startNode()); + if (result) return result; } return super.parseStatementContent(context, topLevel); @@ -3527,7 +3539,7 @@ export default (superClass: Class): Class => tsParseAbstractDeclaration( node: any, - ): N.ClassDeclaration | N.TsInterfaceDeclaration | typeof undefined { + ): N.ClassDeclaration | ?N.TsInterfaceDeclaration { if (this.match(tt._class)) { node.abstract = true; return this.parseClass( @@ -3546,7 +3558,6 @@ export default (superClass: Class): Class => this.raise(TSErrors.NonClassMethodPropertyHasAbstractModifer, { at: node, }); - this.next(); return this.tsParseInterfaceDeclaration( (node: N.TsInterfaceDeclaration), ); From 085361f048842fb2ff25684376a5b10484eea7e7 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 18 Feb 2022 15:26:05 -0800 Subject: [PATCH 043/104] Fix for keywords in export as. Reviewed by @tolmasky. --- packages/babel-parser/src/plugins/typescript/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 18fbb1e24a13..5a9ff9c2d595 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3697,7 +3697,9 @@ export default (superClass: Class): Class => // { type as as something } hasTypeSpecifier = true; leftOfAs = firstAs; - rightOfAs = this.parseIdentifier(); + rightOfAs = isImport + ? this.parseIdentifier() + : this.parseModuleExportName(); canParseAsKeyword = false; } else { // { type as as } @@ -3707,7 +3709,9 @@ export default (superClass: Class): Class => } else if (tokenIsKeywordOrIdentifier(this.state.type)) { // { type as something } canParseAsKeyword = false; - rightOfAs = this.parseIdentifier(); + rightOfAs = isImport + ? this.parseIdentifier() + : this.parseModuleExportName(); } else { // { type as } hasTypeSpecifier = true; From 7327182116ac22b2b083c03e11573b94510f9d67 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 19 Feb 2022 10:57:44 -0800 Subject: [PATCH 044/104] Fix reserved words in import types. Reviewed by @tolmasky. --- .../src/plugins/typescript/index.js | 47 +++++++++++++------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 5a9ff9c2d595..12bf578be472 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -568,7 +568,12 @@ export default (superClass: Class): Class => this.expect(tt.parenR); if (this.eat(tt.dot)) { - node.qualifier = this.tsParseEntityName(/* allowReservedWords */ true); + // In this instance, the entity name will actually itself be a + // qualifier, so allow it to be a reserved word as well. + node.qualifier = this.tsParseEntityName({ + allowReservedEntityName: true, + allowReservedQualifiers: true, + }); } if (this.match(tt.lt)) { node.typeParameters = this.tsParseTypeArguments(); @@ -576,17 +581,22 @@ export default (superClass: Class): Class => return this.finishNode(node, "TSImportType"); } - tsParseEntityName( - allowReservedWords: boolean, - allowConst: boolean = false, - ): N.TsEntityName { + tsParseEntityName({ + allowReservedEntityName, + allowReservedQualifiers, + }: { + allowReservedEntityName: TokenType | boolean, + allowReservedQualifiers: boolean, + }): N.TsEntityName { let entity: N.TsEntityName = this.parseIdentifier( - allowConst && this.match(tt._const), + allowReservedEntityName === true || + (allowReservedEntityName !== false && + this.match(allowReservedEntityName)), ); while (this.eat(tt.dot)) { const node: N.TsQualifiedName = this.startNodeAtNode(entity); node.left = entity; - node.right = this.parseIdentifier(allowReservedWords); + node.right = this.parseIdentifier(allowReservedQualifiers); entity = this.finishNode(node, "TSQualifiedName"); } return entity; @@ -594,10 +604,10 @@ export default (superClass: Class): Class => tsParseTypeReference(allowConst: boolean = false): N.TsTypeReference { const node: N.TsTypeReference = this.startNode(); - node.typeName = this.tsParseEntityName( - /* allowReservedWords */ true, - allowConst, - ); + node.typeName = this.tsParseEntityName({ + allowReservedEntityName: allowConst && tt._const, + allowReservedQualifiers: true, + }); if (!this.hasPrecedingLineBreak() && this.match(tt.lt)) { node.typeParameters = this.tsParseTypeArguments(); } @@ -625,7 +635,10 @@ export default (superClass: Class): Class => if (this.match(tt._import)) { node.exprName = this.tsParseImportType(); } else { - node.exprName = this.tsParseEntityName(/* allowReservedWords */ true); + node.exprName = this.tsParseEntityName({ + allowReservedEntityName: false, + allowReservedQualifiers: true, + }); } return this.finishNode(node, "TSTypeQuery"); } @@ -1594,7 +1607,10 @@ export default (superClass: Class): Class => const node: N.TsExpressionWithTypeArguments = this.startNode(); // Note: TS uses parseLeftHandSideExpressionOrHigher, // then has grammar errors later if it's not an EntityName. - node.expression = this.tsParseEntityName(/* allowReservedWords */ false); + node.expression = this.tsParseEntityName({ + allowReservedEntityName: false, + allowReservedQualifiers: false, + }); if (this.match(tt.lt)) { node.typeParameters = this.tsParseTypeArguments(); } @@ -1836,7 +1852,10 @@ export default (superClass: Class): Class => tsParseModuleReference(): N.TsModuleReference { return this.tsIsExternalModuleReference() ? this.tsParseExternalModuleReference() - : this.tsParseEntityName(/* allowReservedWords */ false); + : this.tsParseEntityName({ + allowReservedEntityName: false, + allowReservedQualifiers: false, + }); } tsParseExternalModuleReference(): N.TsExternalModuleReference { From 624a102134b07a4da4701aed783133e82a0c5652 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 19 Feb 2022 11:12:51 -0800 Subject: [PATCH 045/104] Rename files. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 8 ++++---- .../src/parse-error/{module.js => module-errors.js} | 0 .../src/parse-error/{standard.js => standard-errors.js} | 0 .../parse-error/{strict-mode.js => strict-mode-errors.js} | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename packages/babel-parser/src/parse-error/{module.js => module-errors.js} (100%) rename packages/babel-parser/src/parse-error/{standard.js => standard-errors.js} (100%) rename packages/babel-parser/src/parse-error/{strict-mode.js => strict-mode-errors.js} (100%) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 768d13300da0..5c50d7d440b9 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -121,12 +121,12 @@ export type RaiseProperties = {| at: Position | NodeBase, |}; -import ModuleErrors from "./parse-error/module"; -import StandardErrors from "./parse-error/standard"; -import StrictErrors from "./parse-error/strict-mode"; +import ModuleErrors from "./parse-error/module-errors"; +import StandardErrors from "./parse-error/standard-errors"; +import StrictModeErrors from "./parse-error/strict-mode-errors"; export const Errors = { ...toParseErrorClasses(ModuleErrors), ...toParseErrorClasses(StandardErrors), - ...toParseErrorClasses(StrictErrors), + ...toParseErrorClasses(StrictModeErrors), }; diff --git a/packages/babel-parser/src/parse-error/module.js b/packages/babel-parser/src/parse-error/module-errors.js similarity index 100% rename from packages/babel-parser/src/parse-error/module.js rename to packages/babel-parser/src/parse-error/module-errors.js diff --git a/packages/babel-parser/src/parse-error/standard.js b/packages/babel-parser/src/parse-error/standard-errors.js similarity index 100% rename from packages/babel-parser/src/parse-error/standard.js rename to packages/babel-parser/src/parse-error/standard-errors.js diff --git a/packages/babel-parser/src/parse-error/strict-mode.js b/packages/babel-parser/src/parse-error/strict-mode-errors.js similarity index 100% rename from packages/babel-parser/src/parse-error/strict-mode.js rename to packages/babel-parser/src/parse-error/strict-mode-errors.js From 5e6a90527e8910d4d6035ad45052adf182c4cddf Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 19 Feb 2022 08:08:14 -0800 Subject: [PATCH 046/104] Add 1 --- scripts/parser-tests/utils/parser-test-runner.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/parser-tests/utils/parser-test-runner.js b/scripts/parser-tests/utils/parser-test-runner.js index 79e235b48b5e..2c5616989bd0 100644 --- a/scripts/parser-tests/utils/parser-test-runner.js +++ b/scripts/parser-tests/utils/parser-test-runner.js @@ -61,6 +61,7 @@ class TestRunner { parser(test.contents, { sourceType: test.sourceType, plugins: test.plugins, + sourceFilename: test.fileName, }); } From 3db388e28b1ca85042380c2e1d5940d7ae686394 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 16 Feb 2022 11:14:45 -0800 Subject: [PATCH 047/104] Remove files form allowlist that no longer need to be there. Reviewed by @tolmasky. --- scripts/parser-tests/typescript/allowlist.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/parser-tests/typescript/allowlist.txt b/scripts/parser-tests/typescript/allowlist.txt index 0cfd7cca8167..4f520846d25c 100644 --- a/scripts/parser-tests/typescript/allowlist.txt +++ b/scripts/parser-tests/typescript/allowlist.txt @@ -280,7 +280,6 @@ indexTypeCheck.ts indexWithoutParamType.ts indexerSignatureWithRestParam.ts inferrenceInfiniteLoopWithSubtyping.ts -initializedParameterBeforeNonoptionalNotOptional.ts interfaceMayNotBeExtendedWitACall.ts interfaceWithImplements1.ts invalidReferenceSyntax1.ts @@ -367,7 +366,6 @@ multipleInheritance.ts nameCollisions.ts namespacesWithTypeAliasOnlyExportsMerge.ts narrowedImports.ts -newNamesInGlobalAugmentations1.ts noBundledEmitFromNodeModules.ts noCrashOnImportShadowing.ts noImplicitAnyDestructuringVarDeclaration.ts @@ -394,7 +392,6 @@ preserveUnusedImports.ts privacyCheckExternalModuleExportAssignmentOfGenericClass.ts privacyTopLevelAmbientExternalModuleImportWithExport.ts privacyTopLevelAmbientExternalModuleImportWithoutExport.ts -reExportGlobalDeclaration1.ts reExportUndefined1.ts reExportUndefined2.ts readonlyInNonPropertyParameters.ts From c9a977b1e13f1703dc7c3f21bf8ba604cc5e6356 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 16 Feb 2022 12:20:15 -0800 Subject: [PATCH 048/104] Parse as separate files and switch to only using error codes. Reviewed by @tolmasky. --- scripts/parser-tests/typescript/allowlist.txt | 197 +----------------- .../parser-tests/typescript/error-codes.js | 5 +- scripts/parser-tests/typescript/index.js | 130 +++++++++--- .../parser-tests/utils/parser-test-runner.js | 17 +- 4 files changed, 112 insertions(+), 237 deletions(-) diff --git a/scripts/parser-tests/typescript/allowlist.txt b/scripts/parser-tests/typescript/allowlist.txt index 4f520846d25c..bc8d98e7643c 100644 --- a/scripts/parser-tests/typescript/allowlist.txt +++ b/scripts/parser-tests/typescript/allowlist.txt @@ -19,12 +19,6 @@ aliasUsageInOrExpression.ts aliasUsageInTypeArgumentOfExtendsClause.ts aliasUsageInVarAssignment.ts aliasUsedAsNameValue.ts -allowImportClausesToMergeWithTypes.ts -allowJscheckJsTypeParameterNoCrash.ts -amdDeclarationEmitNoExtraDeclare.ts -amdModuleConstEnumUsage.ts -amdModuleName2.ts -anonClassDeclarationEmitIsAnon.ts anyDeclare.ts argumentsBindsToFunctionScopeArgumentList.ts argumentsReferenceInConstructor4_Js.ts @@ -32,17 +26,6 @@ argumentsReferenceInMethod4_Js.ts argumentsReferenceInObjectLiteral_Js.ts arrayOfExportedClass.ts asiAbstract.ts -asyncFunctionsAcrossFiles.ts -augmentExportEquals1.ts -augmentExportEquals1_1.ts -augmentExportEquals2.ts -augmentExportEquals2_1.ts -augmentExportEquals3.ts -augmentExportEquals3_1.ts -augmentExportEquals4.ts -augmentExportEquals4_1.ts -augmentExportEquals6.ts -augmentExportEquals6_1.ts augmentExportEquals7.ts augmentedTypesClass.ts augmentedTypesClass2.ts @@ -53,102 +36,42 @@ augmentedTypesEnum2.ts augmentedTypesFunction.ts augmentedTypesInterface.ts augmentedTypesVar.ts -bigintIndex.ts binderBinaryExpressionStress.ts binderBinaryExpressionStressJs.ts bundledDtsLateExportRenaming.ts -cacheResolutions.ts -cachedModuleResolution1.ts -cachedModuleResolution2.ts -cachedModuleResolution3.ts -cachedModuleResolution4.ts -cachedModuleResolution5.ts -cachedModuleResolution6.ts -cachedModuleResolution7.ts -cachedModuleResolution8.ts -cachedModuleResolution9.ts callOverloads2.ts checkSuperCallBeforeThisAccessing9.ts classCannotExtendVar.ts -classExpressionWithDecorator1.ts -classExtendsAcrossFiles.ts classExtendsMultipleBaseClasses.ts classOverloadForFunction.ts classWithEmptyTypeParameter.ts collisionExportsRequireAndClass.ts commonSourceDir5.ts commonSourceDir6.ts -commonjsSafeImport.ts conflictingTypeAnnotatedVar.ts constDeclarations-invalidContexts.ts constDeclarations-scopes.ts constDeclarations-validContexts.ts -constEnumNamespaceReferenceCausesNoImport2.ts -constEnumNoEmitReexport.ts -constEnumNoPreserveDeclarationReexport.ts -constEnumPreserveEmitNamedExport1.ts -constEnumPreserveEmitNamedExport2.ts -constEnumPreserveEmitReexport.ts -contextualOverloadListFromArrayUnion.ts convertKeywordsYes.ts -declarationEmitAmdModuleNameDirective.ts -declarationEmitCommonSourceDirectoryDoesNotContainAllFiles.ts -declarationEmitComputedNameCausesImportToBePainted.ts -declarationEmitComputedNameConstEnumAlias.ts -declarationEmitCrossFileImportTypeOfAmbientModule.ts -declarationEmitDefaultExportWithStaticAssignment.ts declarationEmitDestructuring2.ts declarationEmitDestructuringOptionalBindingParametersInOverloads.ts declarationEmitDestructuringParameterProperties.ts declarationEmitDestructuringWithOptionalBindingParameters.ts -declarationEmitExpandoPropertyPrivateName.ts -declarationEmitExportAssignedNamespaceNoTripleSlashTypesReference.ts -declarationEmitExportAssignment.ts -declarationEmitExportDeclaration.ts declarationEmitExpressionInExtends6.ts -declarationEmitExpressionWithNonlocalPrivateUniqueSymbol.ts -declarationEmitForModuleImportingModuleAugmentationRetainsImport.ts -declarationEmitForTypesWhichNeedImportTypes.ts declarationEmitInterfaceWithNonEntityNameExpressionHeritage.ts -declarationEmitMixinPrivateProtected.ts -declarationEmitObjectAssignedDefaultExport.ts -declarationEmitPrefersPathKindBasedOnBundling.ts -declarationEmitPrefersPathKindBasedOnBundling2.ts -declarationEmitPrivatePromiseLikeInterface.ts -declarationEmitPrivateSymbolCausesVarDeclarationEmit2.ts -declarationEmitReadonlyComputedProperty.ts -declarationEmitStringEnumUsedInNonlocalSpread.ts -declarationImportTypeAliasInferredAndEmittable.ts -declarationMapsMultifile.ts -declarationMapsOutFile.ts -declarationsForInferredTypeFromOtherFile.ts declarationsIndirectGeneratedAliasReference.ts declareModifierOnImport1.ts -decoratorMetadataRestParameterWithImportedType.ts -decoratorMetadataWithImportDeclarationNameCollision.ts -decoratorMetadataWithImportDeclarationNameCollision3.ts -decoratorMetadataWithImportDeclarationNameCollision4.ts -decoratorMetadataWithImportDeclarationNameCollision5.ts -decoratorMetadataWithImportDeclarationNameCollision7.ts -decoratorReferenceOnOtherProperty.ts decoratorsOnComputedProperties.ts decrementAndIncrementOperators.ts defaultArgsInOverloads.ts -defaultDeclarationEmitDefaultImport.ts -defaultIsNotVisibleInLocalScope.ts -defaultPropsEmptyCurlyBecomesAnyForJs.ts defaultValueInFunctionTypes.ts deleteOperator1.ts deleteOperatorInStrictMode.ts dependencyViaImportAlias.ts -destructuredDeclarationEmit.ts -doubleUnderscoreExportStarConflict.ts duplicateIdentifierBindingElementInParameterDeclaration1.ts duplicateIdentifierBindingElementInParameterDeclaration2.ts duplicateIdentifierEnum.ts duplicateIdentifierInCatchBlock.ts -duplicateIdentifierRelatedSpans1.ts -duplicateIdentifierRelatedSpans2.ts duplicateIdentifiersAcrossFileBoundaries.ts duplicateLabel1.ts duplicateLabel2.ts @@ -156,9 +79,7 @@ duplicateVarAndImport.ts duplicateVarAndImport2.ts duplicateVarsAcrossFileBoundaries.ts dynamicImportTrailingComma.ts -dynamicNames.ts elidedEmbeddedStatementsReplacedWithSemicolon.ts -emitClassMergedWithConstNamespaceNotElided.ts emitSuperCallBeforeEmitParameterPropertyDeclaration1.ts emitSuperCallBeforeEmitParameterPropertyDeclaration1ES6.ts emitSuperCallBeforeEmitPropertyDeclarationAndParameterPropertyDeclaration1.ts @@ -170,38 +91,13 @@ enumGenericTypeClash.ts es3-oldStyleOctalLiteralTypes.ts es3defaultAliasIsQuoted.ts es5-asyncFunctionWithStatements.ts -es5-importHelpersAsyncFunctions.ts es5-oldStyleOctalLiteralInEnums.ts -es5ModuleInternalNamedImports.ts -es6ExportAssignment2.ts -es6ExportAssignment3.ts -es6ImportDefaultBindingFollowedWithNamedImport.ts -es6ImportDefaultBindingFollowedWithNamedImport1.ts -es6ImportDefaultBindingFollowedWithNamedImport1InEs5.ts -es6ImportDefaultBindingFollowedWithNamedImportDts.ts -es6ImportDefaultBindingFollowedWithNamedImportDts1.ts -es6ImportDefaultBindingFollowedWithNamedImportInEs5.ts +es5ModuleInternalNamedImports.tses6ImportDefaultBindingFollowedWithNamedImport1InEs5.ts es6ImportDefaultBindingMergeErrors.ts -es6ImportEqualsDeclaration.ts -es6ImportEqualsExportModuleCommonJsError.ts -es6ImportEqualsExportModuleEs2015Error.ts es6ImportNameSpaceImportMergeErrors.ts -es6ImportNamedImport.ts -es6ImportNamedImportAmd.ts -es6ImportNamedImportDts.ts -es6ImportNamedImportInEs5.ts -es6ImportNamedImportInExportAssignment.ts es6ImportNamedImportMergeErrors.ts -es6ImportNamedImportNoExportMember.ts -es6ImportNamedImportNoNamedExports.ts -es6ImportNamedImportWithTypesAndValues.ts es6ModuleInternalNamedImports.ts es6ModuleInternalNamedImports2.ts -es6UseOfTopLevelRequire.ts -esModuleInterop.ts -esModuleInteropImportTSLibHasImport.ts -esModuleInteropNamedDefaultImports.ts -esModuleInteropTslibHelpers.ts expandoFunctionContextualTypesNoValue.ts exportAssignClassAndModule.ts exportAssignmentImportMergeNoCrash.ts @@ -214,17 +110,12 @@ exportClassExtendingIntersection.ts exportClassWithoutName.ts exportDeclarationsInAmbientNamespaces.ts exportDefaultAbstractClass.ts -exportDefaultAsyncFunction2.ts -exportDefaultMarksIdentifierAsUsed.ts -exportDefaultStripsFreshness.ts exportEqualsOfModule.ts exportImport.ts exportImportNonInstantiatedModule2.ts exportSameNameFuncVar.ts exportSpecifierAndExportedMemberDeclaration.ts exportSpecifierAndLocalMemberDeclaration.ts -exportStarFromEmptyModule.ts -exportStarNotElided.ts expressionsForbiddenInParameterInitializers.ts extendingClassFromAliasAndUsageInIndexer.ts extendsClauseAlreadySeen.ts @@ -239,8 +130,6 @@ functionExpressionInWithBlock.ts functionExpressionWithResolutionOfTypeNamedArguments01.ts gettersAndSettersErrors.ts giant.ts -globalThisDeclarationEmit.ts -globalThisDeclarationEmit2.ts implementClausePrecedingExtends.ts implementsClauseAlreadySeen.ts importAndVariableDeclarationConflict1.ts @@ -251,17 +140,7 @@ importDecl.ts importDeclWithClassModifiers.ts importDeclWithDeclareModifierInAmbientContext.ts importEqualsError45874.ts -importHelpers.ts -importHelpersAmd.ts -importHelpersES6.ts importHelpersInAmbientContext.ts -importHelpersInIsolatedModules.ts -importHelpersInTsx.tsx -importHelpersNoHelpers.ts -importHelpersNoHelpersForPrivateFields.ts -importHelpersNoModule.ts -importHelpersOutFile.ts -importHelpersSystem.ts importNonExportedMember10.ts importNonExportedMember11.ts importNonExportedMember4.ts @@ -271,7 +150,6 @@ importNonExportedMember7.ts importNonExportedMember8.ts importNonExportedMember9.ts importWithTrailingSlash.ts -importedEnumMemberMergedWithExportedAliasIsError.ts importedModuleClassNameClash.ts indexSignatureWithAccessibilityModifier.ts indexSignatureWithInitializer1.ts @@ -279,23 +157,13 @@ indexSignatureWithTrailingComma.ts indexTypeCheck.ts indexWithoutParamType.ts indexerSignatureWithRestParam.ts -inferrenceInfiniteLoopWithSubtyping.ts interfaceMayNotBeExtendedWitACall.ts interfaceWithImplements1.ts -invalidReferenceSyntax1.ts isLiteral1.ts isLiteral2.ts -isolatedModulesImportConstEnum.ts -isolatedModulesImportConstEnumTypeOnly.ts isolatedModulesReExportType.ts -jsEnumTagOnObjectFrozen.ts jsExportMemberMergedWithModuleAugmentation.ts jsFileCompilationBindDuplicateIdentifier.ts -jsFileCompilationDuplicateFunctionImplementation.ts -jsFileCompilationDuplicateFunctionImplementationFileOrderReversed.ts -jsFileCompilationExternalPackageError.ts -jsFileImportPreservedWhenUsed.ts -jsNoImplicitAnyNoCascadingReferenceErrors.ts jsdocAccessEnumType.ts jsdocPropertyTagInvalid.ts jsxAttributeWithoutExpressionReact.tsx @@ -318,62 +186,24 @@ letInLetConstDeclOfForOfAndForIn_ES5.ts letInLetConstDeclOfForOfAndForIn_ES6.ts letInLetDeclarations_ES5.ts letInLetDeclarations_ES6.ts -mergeWithImportedType.ts -mergedDeclarations6.ts metadataOfClassFromAlias.ts metadataOfClassFromAlias2.ts -metadataReferencedWithinFilteredUnion.ts mismatchedClassConstructorVariable.ts -missingSemicolonInModuleSpecifier.ts misspelledNewMetaProperty.ts modifiersOnInterfaceIndexSignature1.ts -moduleAugmentationCollidingNamesInAugmentation1.ts -moduleAugmentationDeclarationEmit1.ts -moduleAugmentationDeclarationEmit2.ts -moduleAugmentationDisallowedExtensions.ts -moduleAugmentationDuringSyntheticDefaultCheck.ts -moduleAugmentationExtendAmbientModule1.ts -moduleAugmentationExtendAmbientModule2.ts -moduleAugmentationExtendFileModule1.ts -moduleAugmentationExtendFileModule2.ts -moduleAugmentationGlobal1.ts -moduleAugmentationGlobal2.ts -moduleAugmentationGlobal3.ts -moduleAugmentationImportsAndExports1.ts -moduleAugmentationImportsAndExports2.ts -moduleAugmentationImportsAndExports3.ts -moduleAugmentationImportsAndExports4.ts -moduleAugmentationImportsAndExports5.ts -moduleAugmentationImportsAndExports6.ts -moduleAugmentationNoNewNames.ts -moduleAugmentationsBundledOutput1.ts -moduleAugmentationsImports1.ts -moduleAugmentationsImports2.ts -moduleAugmentationsImports3.ts -moduleAugmentationsImports4.ts moduleDuplicateIdentifiers.ts -moduleResolutionNoTsCJS.ts -moduleResolutionNoTsESM.ts -moduleResolutionWithSymlinks.ts -moduleResolutionWithSymlinks_withOutDir.ts moduleResolution_automaticTypeDirectiveNames.ts moduleSharesNameWithImportDeclarationInsideIt3.ts moduleSharesNameWithImportDeclarationInsideIt5.ts -module_augmentUninstantiatedModule2.ts multiImportExport.ts multipleClassPropertyModifiersErrors.ts multipleInheritance.ts nameCollisions.ts -namespacesWithTypeAliasOnlyExportsMerge.ts narrowedImports.ts noBundledEmitFromNodeModules.ts -noCrashOnImportShadowing.ts noImplicitAnyDestructuringVarDeclaration.ts noSymbolForMergeCrash.ts nodeModuleReexportFromDottedPath.ts -nodeResolution4.ts -nodeResolution6.ts -nodeResolution8.ts nonMergedOverloads.ts objectLiteralMemberWithoutBlock1.ts outModuleConcatAmd.ts @@ -386,14 +216,11 @@ outModuleTripleSlashRefs.ts parameterInitializerBeforeDestructuringEmit.ts parameterPropertyOutsideConstructor.ts parserConstructorDeclaration12.ts -pathMappingBasedModuleResolution3_classic.ts -pathMappingBasedModuleResolution3_node.ts preserveUnusedImports.ts privacyCheckExternalModuleExportAssignmentOfGenericClass.ts privacyTopLevelAmbientExternalModuleImportWithExport.ts privacyTopLevelAmbientExternalModuleImportWithoutExport.ts reExportUndefined1.ts -reExportUndefined2.ts readonlyInNonPropertyParameters.ts recursiveExportAssignmentAndFindAliasedType1.ts recursiveExportAssignmentAndFindAliasedType2.ts @@ -404,12 +231,9 @@ recursiveExportAssignmentAndFindAliasedType6.ts recursiveExportAssignmentAndFindAliasedType7.ts redeclareParameterInCatchBlock.ts reexportedMissingAlias.ts -relativeNamesInClassicResolution.ts requireAsFunctionInExternalModule.ts -reservedWords3.ts restParamModifier2.ts shadowedReservedCompilerDeclarationsWithNoEmit.ts -shorthandPropertyAssignmentInES6Module.ts sourceMap-LineBreaks.ts sourceMapValidationDecorators.ts sourceMapValidationStatements.ts @@ -419,31 +243,12 @@ staticModifierAlreadySeen.ts strictModeReservedWord.ts strictOptionalProperties1.ts superCallFromClassThatHasNoBaseType1.ts -symbolLinkDeclarationEmitModuleNames.ts symbolMergeValueAndImportedType.ts -systemExportAssignment.ts -systemExportAssignment2.ts -systemModule11.ts -systemModule15.ts -systemModule17.ts systemModuleWithSuperClass.ts -systemObjectShorthandRename.ts -targetEs6DecoratorMetadataImportNotElided.ts targetTypeCastTest.ts tsxDeepAttributeAssignabilityError.tsx -typeReferenceDirectives10.ts -typeReferenceDirectives11.ts -typeReferenceDirectives12.ts -typeReferenceDirectives13.ts -typeReferenceDirectives5.ts -typeReferenceDirectives7.ts -typeReferenceDirectives8.ts -typeReferenceDirectives9.ts uniqueSymbolPropertyDeclarationEmit.ts -unusedImportWithSpread.ts unusedImports1.ts -unusedImports11.ts -unusedImports12.ts unusedImports2.ts unusedImports3.ts unusedImports4.ts diff --git a/scripts/parser-tests/typescript/error-codes.js b/scripts/parser-tests/typescript/error-codes.js index 96885be0eaf1..26145979de50 100644 --- a/scripts/parser-tests/typescript/error-codes.js +++ b/scripts/parser-tests/typescript/error-codes.js @@ -9,7 +9,8 @@ The commented out diagnostic codes will introduce false positive cases that shou */ export default [ - // "TS1005", // '{0}' expected. + "TS1003", // Identifier expected. + "TS1005", // '{0}' expected. "TS1009", // Trailing comma not allowed. "TS1014", // A rest parameter must be last in a parameter list. "TS1019", // An index signature parameter cannot have a question mark. @@ -55,9 +56,11 @@ export default [ "TS1308", // 'await' expression is only allowed within an async function. // "TS2300", // Duplicate identifier '{0}'. "TS2337", // Super calls are not permitted outside constructors or in nested functions inside constructors. + "TS2389", // Function implementation name must be '${0}'. "TS2390", // Constructor implementation is missing. "TS2391", // Function implementation is missing or not immediately following the declaration. "TS2364", // The left-hand side of an assignment expression must be a variable or a property access. + "TS2369", // A parameter property is only allowed in a constructor implementation. // "TS2371", // A parameter initializer is only allowed in a function or constructor implementation. // "TS2393", // Duplicate function implementation. "TS2396", // Duplicate identifier 'arguments'. Compiler uses 'arguments' to initialize rest parameters. diff --git a/scripts/parser-tests/typescript/index.js b/scripts/parser-tests/typescript/index.js index df92734bddac..90869f3dc22b 100644 --- a/scripts/parser-tests/typescript/index.js +++ b/scripts/parser-tests/typescript/index.js @@ -1,37 +1,61 @@ import path from "path"; -import fs from "fs/promises"; +import fs from "fs"; import { fileURLToPath } from "url"; -import ts from "../../../build/typescript/lib/typescript.js"; import TestRunner from "../utils/parser-test-runner.js"; -import parsingErrorCodes from "./error-codes.js"; +import ErrorCodes from "./error-codes.js"; +const ErrorCodeRegExp = new RegExp(ErrorCodes.join("|")); +console.log("ERROR CODE REGEXP: ", ErrorCodeRegExp); const dirname = path.dirname(fileURLToPath(import.meta.url)); -async function* loadTests(dir) { - const names = await fs.readdir(dir); +function* loadTests(dir) { + const names = fs.readdirSync(dir); for (const name of names) { - const contents = await fs.readFile(path.join(dir, name), "utf8"); + const contents = fs.readFileSync(path.join(dir, name), "utf8"); yield { name, contents }; } } const plugins = ["typescript", "decorators-legacy", "importAssertions"]; +const pluginsWithJSX = [...plugins, "jsx"]; const TSTestsPath = path.join(dirname, "../../../build/typescript/tests"); // Check if the baseline errors contain the codes that should also be thrown from babel-parser -async function baselineContainsParserErrorCodes(testName) { +function baselineContainsParserErrorCodes(testName) { try { - const baselineErrors = await fs.readFile( - path.join( - TSTestsPath, - "baselines/reference", - testName.replace(/\.tsx?$/, ".errors.txt") - ), - "utf8" + if (testName.includes("aliasErrors")) { + console.log( + "WILL TEST " + + path.join( + TSTestsPath, + "baselines/reference", + testName.replace(/\.tsx?$/, ".errors.txt") + ) + ); + console.log( + "WILL TEST " + + fs.readFileSync( + path.join( + TSTestsPath, + "baselines/reference", + testName.replace(/\.tsx?$/, ".errors.txt") + ), + "utf8" + ) + ); + } + return ErrorCodeRegExp.test( + fs.readFileSync( + path.join( + TSTestsPath, + "baselines/reference", + testName.replace(/\.tsx?$/, ".errors.txt") + ), + "utf8" + ) ); - return parsingErrorCodes.some(code => baselineErrors.includes(code)); } catch (e) { if (e.code !== "ENOENT") { throw e; @@ -46,33 +70,71 @@ const runner = new TestRunner({ logInterval: 50, shouldUpdate: process.argv.includes("--update-allowlist"), - async *getTests() { - for await (const test of loadTests(this.testDir)) { - const isTSX = test.name.slice(-4) === ".tsx"; - - const ast = ts.createSourceFile( - test.name, - test.contents, - ts.ScriptTarget.Latest, - false, - isTSX ? ts.ScriptKind.TSX : ts.ScriptKind.TS - ); - + *getTests() { + for (const test of loadTests(this.testDir)) { yield { - contents: test.contents, + contents: splitTwoslashCodeInfoFiles( + test.contents, + "default", + `${test.name}/` + ) + .map(([filename, lines]) => [ + filename.replace(/\/default$/, ""), + lines.join("\n"), + ]) + .map(([fileName, contents]) => ({ + contents, + fileName, + sourceType: "module", + plugins: fileName.endsWith(".tsx") ? pluginsWithJSX : plugins, + })), + expectedError: baselineContainsParserErrorCodes(test.name), fileName: test.name, id: test.name, - expectedError: - ast.parseDiagnostics.length > 0 || - (await baselineContainsParserErrorCodes(test.name)), - sourceType: "module", - plugins: isTSX ? plugins.concat("jsx") : plugins, }; } }, }); +const BracketedFileRegExp = /\/\/\/\/\s*\[([^\]]+)\][^\n]*(\n|$)/; +const AtFileRegExp = /(?:^|\n)\/\/\s*@filename:\s+([^\s]*)\s*(?:\n|$)/i; + +// Modified from: https://github.com/microsoft/TypeScript-Website/blob/v2/packages/ts-twoslasher/src/index.ts +function splitTwoslashCodeInfoFiles(code, defaultFileName, root = "") { + const lines = code.split(/\r\n?|\n/g); + + let nameForFile = code.includes(`@filename: ${defaultFileName}`) + ? "global.ts" + : defaultFileName; + let currentFileContent = []; + const fileMap = []; + + for (const line of lines) { + const newFileName = BracketedFileRegExp.test(line) + ? line.match(BracketedFileRegExp)[1] + : AtFileRegExp.test(line) + ? line.match(AtFileRegExp)[1] + : false; + if (newFileName) { + fileMap.push([root + nameForFile, currentFileContent]); + nameForFile = newFileName; + currentFileContent = []; + } else { + currentFileContent.push(line); + } + } + fileMap.push([root + nameForFile, currentFileContent]); + + // Basically, strip these: + // ["index.ts", []] + // ["index.ts", [""]] + const nameContent = fileMap.filter( + n => n[1].length > 0 && (n[1].length > 1 || n[1][0] !== "") + ); + return nameContent; +} + runner.run().catch(err => { console.error(err); process.exitCode = 1; -}); +}); \ No newline at end of file diff --git a/scripts/parser-tests/utils/parser-test-runner.js b/scripts/parser-tests/utils/parser-test-runner.js index 2c5616989bd0..dc096ef6a430 100644 --- a/scripts/parser-tests/utils/parser-test-runner.js +++ b/scripts/parser-tests/utils/parser-test-runner.js @@ -58,11 +58,11 @@ class TestRunner { } parse(test, parser) { - parser(test.contents, { - sourceType: test.sourceType, - plugins: test.plugins, - sourceFilename: test.fileName, - }); + const tests = typeof test.contents === "string" ? [test] : test.contents; + + for (const { contents, sourceType, plugins, sourceFilename } of tests) { + parser(contents, { sourceType, plugins, sourceFilename }); + } } async getAllowlist() { @@ -208,7 +208,12 @@ class TestRunner { badnews.push(desc); badnewsDetails.push(desc + ":"); - badnewsDetails.push(...tests.map(test => ` ${test.id || test}`)); + badnewsDetails.push( + ...tests.map( + test => + ` ${test.id || test} ${test.expectedError} ${test.actualError}` + ) + ); }); console.log(`Testing complete (${summary.count} tests).`); From 68ed5067b725d19c8c0883237fceb06ad83cff3e Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Thu, 17 Feb 2022 18:26:07 -0800 Subject: [PATCH 049/104] Fix reading files with different encodings. Reviewed by @tolmasky. --- scripts/parser-tests/typescript/index.js | 48 ++++++++++++++++++------ 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/scripts/parser-tests/typescript/index.js b/scripts/parser-tests/typescript/index.js index 90869f3dc22b..8d7a1e27c418 100644 --- a/scripts/parser-tests/typescript/index.js +++ b/scripts/parser-tests/typescript/index.js @@ -4,16 +4,35 @@ import { fileURLToPath } from "url"; import TestRunner from "../utils/parser-test-runner.js"; import ErrorCodes from "./error-codes.js"; +import { spawnSync } from "child_process"; +const getEncoding = path => + spawnSync("file", ["--brief", "--mime-encoding", path]) + .stdout.toString() + .slice(0, -1); +const toNodeEncoding = fileEncoding => + ({ + "us-ascii": "utf-8", + "utf-8": "utf-8", + "utf-16le": "utf-16le", + "utf-16be": "utf-16be", + "iso-8859-1": "latin1", + }[fileEncoding] || false); + const ErrorCodeRegExp = new RegExp(ErrorCodes.join("|")); -console.log("ERROR CODE REGEXP: ", ErrorCodeRegExp); + const dirname = path.dirname(fileURLToPath(import.meta.url)); function* loadTests(dir) { - const names = fs.readdirSync(dir); + const names = fs.readdirSync(dir).map(name => [name, path.join(dir, name)]); + //.filter(([name]) => allowed.has(name)); - for (const name of names) { - const contents = fs.readFileSync(path.join(dir, name), "utf8"); - yield { name, contents }; + for (const [name, filename] of names) { + const encoding = getEncoding(filename); + if (encoding === "utf-16be" || encoding === "binary") continue; + yield { + name, + contents: fs.readFileSync(filename, toNodeEncoding(encoding)), + }; } } @@ -64,6 +83,8 @@ function baselineContainsParserErrorCodes(testName) { } } +const IgnoreRegExp = /@noTypesAndSymbols|ts-ignore|\n#!/; + const runner = new TestRunner({ testDir: path.join(TSTestsPath, "./cases/compiler"), allowlist: path.join(dirname, "allowlist.txt"), @@ -72,6 +93,10 @@ const runner = new TestRunner({ *getTests() { for (const test of loadTests(this.testDir)) { + if (IgnoreRegExp.test(test.contents)) { + continue; + } + yield { contents: splitTwoslashCodeInfoFiles( test.contents, @@ -82,14 +107,15 @@ const runner = new TestRunner({ filename.replace(/\/default$/, ""), lines.join("\n"), ]) - .map(([fileName, contents]) => ({ + .filter(([sourceFilename]) => !sourceFilename.endsWith(".json")) + .map(([sourceFilename, contents]) => ({ contents, - fileName, + sourceFilename, sourceType: "module", - plugins: fileName.endsWith(".tsx") ? pluginsWithJSX : plugins, + plugins: sourceFilename.endsWith(".tsx") ? pluginsWithJSX : plugins, })), expectedError: baselineContainsParserErrorCodes(test.name), - fileName: test.name, + sourceFilename: test.name, id: test.name, }; } @@ -97,7 +123,7 @@ const runner = new TestRunner({ }); const BracketedFileRegExp = /\/\/\/\/\s*\[([^\]]+)\][^\n]*(\n|$)/; -const AtFileRegExp = /(?:^|\n)\/\/\s*@filename:\s+([^\s]*)\s*(?:\n|$)/i; +const AtFileRegExp = /(?:^|\n)\/\/\s*@filename:\s*([^\s]*)\s*(?:\n|$)/i; // Modified from: https://github.com/microsoft/TypeScript-Website/blob/v2/packages/ts-twoslasher/src/index.ts function splitTwoslashCodeInfoFiles(code, defaultFileName, root = "") { @@ -137,4 +163,4 @@ function splitTwoslashCodeInfoFiles(code, defaultFileName, root = "") { runner.run().catch(err => { console.error(err); process.exitCode = 1; -}); \ No newline at end of file +}); From 8f96d649d2b79b258fc9c7a0a1eb493d3ef5ae0b Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 18 Feb 2022 11:17:18 -0800 Subject: [PATCH 050/104] Fix outside tests. Reviewed by @tolmasky. --- scripts/parser-tests/typescript/allowlist.txt | 157 ++---------------- .../parser-tests/typescript/error-codes.js | 40 ++++- scripts/parser-tests/typescript/index.js | 71 ++++---- .../parser-tests/utils/parser-test-runner.js | 4 +- 4 files changed, 77 insertions(+), 195 deletions(-) diff --git a/scripts/parser-tests/typescript/allowlist.txt b/scripts/parser-tests/typescript/allowlist.txt index bc8d98e7643c..b8c246d681bf 100644 --- a/scripts/parser-tests/typescript/allowlist.txt +++ b/scripts/parser-tests/typescript/allowlist.txt @@ -4,29 +4,10 @@ ParameterList13.ts ParameterList4.ts ParameterList5.ts ParameterList6.ts -abstractPropertyNegative.ts accessorBodyInTypeContext.ts accessorParameterAccessibilityModifier.ts -accessorWithoutBody1.ts -accessorWithoutBody2.ts -aliasUsageInAccessorsOfClass.ts -aliasUsageInArray.ts -aliasUsageInFunctionExpression.ts -aliasUsageInGenericFunction.ts -aliasUsageInIndexerOfClass.ts -aliasUsageInObjectLiteral.ts -aliasUsageInOrExpression.ts -aliasUsageInTypeArgumentOfExtendsClause.ts -aliasUsageInVarAssignment.ts -aliasUsedAsNameValue.ts anyDeclare.ts -argumentsBindsToFunctionScopeArgumentList.ts -argumentsReferenceInConstructor4_Js.ts -argumentsReferenceInMethod4_Js.ts -argumentsReferenceInObjectLiteral_Js.ts -arrayOfExportedClass.ts asiAbstract.ts -augmentExportEquals7.ts augmentedTypesClass.ts augmentedTypesClass2.ts augmentedTypesClass2a.ts @@ -36,98 +17,68 @@ augmentedTypesEnum2.ts augmentedTypesFunction.ts augmentedTypesInterface.ts augmentedTypesVar.ts -binderBinaryExpressionStress.ts -binderBinaryExpressionStressJs.ts -bundledDtsLateExportRenaming.ts -callOverloads2.ts -checkSuperCallBeforeThisAccessing9.ts +bigintIndex.ts classCannotExtendVar.ts +classExpressionWithDecorator1.ts classExtendsMultipleBaseClasses.ts classOverloadForFunction.ts classWithEmptyTypeParameter.ts -collisionExportsRequireAndClass.ts -commonSourceDir5.ts -commonSourceDir6.ts -conflictingTypeAnnotatedVar.ts +# TypeScript doesn't allow a parameter to be named arguments even in non-strict mode, which we don't catch. +collisionArgumentsArrowFunctions.ts +# TypeScript doesn't allow a parameter to be named arguments even in non-strict mode, which we don't catch. +collisionArgumentsFunction.ts +# TypeScript doesn't allow a parameter to be named arguments even in non-strict mode, which we don't catch. +collisionArgumentsFunctionExpressions.ts constDeclarations-invalidContexts.ts constDeclarations-scopes.ts constDeclarations-validContexts.ts -convertKeywordsYes.ts declarationEmitDestructuring2.ts declarationEmitDestructuringOptionalBindingParametersInOverloads.ts declarationEmitDestructuringParameterProperties.ts declarationEmitDestructuringWithOptionalBindingParameters.ts -declarationEmitExpressionInExtends6.ts declarationEmitInterfaceWithNonEntityNameExpressionHeritage.ts -declarationsIndirectGeneratedAliasReference.ts declareModifierOnImport1.ts decoratorsOnComputedProperties.ts decrementAndIncrementOperators.ts defaultArgsInOverloads.ts +defaultIsNotVisibleInLocalScope.ts defaultValueInFunctionTypes.ts -deleteOperator1.ts deleteOperatorInStrictMode.ts -dependencyViaImportAlias.ts duplicateIdentifierBindingElementInParameterDeclaration1.ts duplicateIdentifierBindingElementInParameterDeclaration2.ts duplicateIdentifierEnum.ts duplicateIdentifierInCatchBlock.ts -duplicateIdentifiersAcrossFileBoundaries.ts duplicateLabel1.ts duplicateLabel2.ts +duplicatePackage.ts +duplicatePackage_withErrors.ts duplicateVarAndImport.ts duplicateVarAndImport2.ts -duplicateVarsAcrossFileBoundaries.ts dynamicImportTrailingComma.ts -elidedEmbeddedStatementsReplacedWithSemicolon.ts -emitSuperCallBeforeEmitParameterPropertyDeclaration1.ts -emitSuperCallBeforeEmitParameterPropertyDeclaration1ES6.ts -emitSuperCallBeforeEmitPropertyDeclarationAndParameterPropertyDeclaration1.ts -emitSuperCallBeforeEmitPropertyDeclarationAndParameterPropertyDeclaration1ES6.ts emptyGenericParamList.ts emptyTypeArgumentList.ts emptyTypeArgumentListWithNew.ts enumGenericTypeClash.ts +# We don't support ES3-style octal literal errors. +es3-oldStyleOctalLiteralInEnums.ts es3-oldStyleOctalLiteralTypes.ts -es3defaultAliasIsQuoted.ts -es5-asyncFunctionWithStatements.ts -es5-oldStyleOctalLiteralInEnums.ts -es5ModuleInternalNamedImports.tses6ImportDefaultBindingFollowedWithNamedImport1InEs5.ts +es6ImportDefaultBindingFollowedWithNamedImport1InEs5.ts es6ImportDefaultBindingMergeErrors.ts es6ImportNameSpaceImportMergeErrors.ts es6ImportNamedImportMergeErrors.ts -es6ModuleInternalNamedImports.ts -es6ModuleInternalNamedImports2.ts expandoFunctionContextualTypesNoValue.ts -exportAssignClassAndModule.ts exportAssignmentImportMergeNoCrash.ts -exportAssignmentMembersVisibleInAugmentation.ts exportAssignmentWithDeclareAndExportModifiers.ts exportAssignmentWithDeclareModifier.ts exportAssignmentWithExportModifier.ts -exportAssignmentWithoutAllowSyntheticDefaultImportsError.ts -exportClassExtendingIntersection.ts exportClassWithoutName.ts -exportDeclarationsInAmbientNamespaces.ts -exportDefaultAbstractClass.ts -exportEqualsOfModule.ts -exportImport.ts -exportImportNonInstantiatedModule2.ts -exportSameNameFuncVar.ts -exportSpecifierAndExportedMemberDeclaration.ts -exportSpecifierAndLocalMemberDeclaration.ts expressionsForbiddenInParameterInitializers.ts -extendingClassFromAliasAndUsageInIndexer.ts extendsClauseAlreadySeen.ts extendsClauseAlreadySeen2.ts -externalModuleAssignToVar.ts fileWithNextLine2.ts funClodule.ts functionAndImportNameConflict.ts functionCall15.ts -functionDeclarationWithResolutionOfTypeNamedArguments01.ts -functionExpressionInWithBlock.ts -functionExpressionWithResolutionOfTypeNamedArguments01.ts gettersAndSettersErrors.ts giant.ts implementClausePrecedingExtends.ts @@ -135,21 +86,8 @@ implementsClauseAlreadySeen.ts importAndVariableDeclarationConflict1.ts importAndVariableDeclarationConflict3.ts importAndVariableDeclarationConflict4.ts -importAsBaseClass.ts -importDecl.ts importDeclWithClassModifiers.ts importDeclWithDeclareModifierInAmbientContext.ts -importEqualsError45874.ts -importHelpersInAmbientContext.ts -importNonExportedMember10.ts -importNonExportedMember11.ts -importNonExportedMember4.ts -importNonExportedMember5.ts -importNonExportedMember6.ts -importNonExportedMember7.ts -importNonExportedMember8.ts -importNonExportedMember9.ts -importWithTrailingSlash.ts importedModuleClassNameClash.ts indexSignatureWithAccessibilityModifier.ts indexSignatureWithInitializer1.ts @@ -159,25 +97,13 @@ indexWithoutParamType.ts indexerSignatureWithRestParam.ts interfaceMayNotBeExtendedWitACall.ts interfaceWithImplements1.ts -isLiteral1.ts -isLiteral2.ts -isolatedModulesReExportType.ts -jsExportMemberMergedWithModuleAugmentation.ts -jsFileCompilationBindDuplicateIdentifier.ts -jsdocAccessEnumType.ts -jsdocPropertyTagInvalid.ts +# We correctly identify this error, but we can't bring it in without bringing a bunch of other tests too. +interfaceNaming1.ts jsxAttributeWithoutExpressionReact.tsx letAndVarRedeclaration.ts letAsIdentifier.ts -letAsIdentifierInStrictMode.ts letDeclarations-invalidContexts.ts letDeclarations-scopes-duplicates.ts -letDeclarations-scopes-duplicates2.ts -letDeclarations-scopes-duplicates3.ts -letDeclarations-scopes-duplicates4.ts -letDeclarations-scopes-duplicates5.ts -letDeclarations-scopes-duplicates6.ts -letDeclarations-scopes-duplicates7.ts letDeclarations-scopes.ts letDeclarations-validContexts.ts letInConstDeclarations_ES5.ts @@ -186,78 +112,27 @@ letInLetConstDeclOfForOfAndForIn_ES5.ts letInLetConstDeclOfForOfAndForIn_ES6.ts letInLetDeclarations_ES5.ts letInLetDeclarations_ES6.ts -metadataOfClassFromAlias.ts -metadataOfClassFromAlias2.ts mismatchedClassConstructorVariable.ts misspelledNewMetaProperty.ts modifiersOnInterfaceIndexSignature1.ts moduleDuplicateIdentifiers.ts -moduleResolution_automaticTypeDirectiveNames.ts moduleSharesNameWithImportDeclarationInsideIt3.ts moduleSharesNameWithImportDeclarationInsideIt5.ts -multiImportExport.ts multipleClassPropertyModifiersErrors.ts multipleInheritance.ts nameCollisions.ts -narrowedImports.ts -noBundledEmitFromNodeModules.ts noImplicitAnyDestructuringVarDeclaration.ts -noSymbolForMergeCrash.ts -nodeModuleReexportFromDottedPath.ts -nonMergedOverloads.ts -objectLiteralMemberWithoutBlock1.ts -outModuleConcatAmd.ts -outModuleConcatCommonjs.ts -outModuleConcatCommonjsDeclarationOnly.ts -outModuleConcatES6.ts -outModuleConcatSystem.ts -outModuleConcatUmd.ts -outModuleTripleSlashRefs.ts parameterInitializerBeforeDestructuringEmit.ts parameterPropertyOutsideConstructor.ts parserConstructorDeclaration12.ts -preserveUnusedImports.ts -privacyCheckExternalModuleExportAssignmentOfGenericClass.ts -privacyTopLevelAmbientExternalModuleImportWithExport.ts -privacyTopLevelAmbientExternalModuleImportWithoutExport.ts -reExportUndefined1.ts readonlyInNonPropertyParameters.ts -recursiveExportAssignmentAndFindAliasedType1.ts -recursiveExportAssignmentAndFindAliasedType2.ts -recursiveExportAssignmentAndFindAliasedType3.ts -recursiveExportAssignmentAndFindAliasedType4.ts -recursiveExportAssignmentAndFindAliasedType5.ts -recursiveExportAssignmentAndFindAliasedType6.ts -recursiveExportAssignmentAndFindAliasedType7.ts redeclareParameterInCatchBlock.ts -reexportedMissingAlias.ts -requireAsFunctionInExternalModule.ts restParamModifier2.ts -shadowedReservedCompilerDeclarationsWithNoEmit.ts sourceMap-LineBreaks.ts sourceMapValidationDecorators.ts -sourceMapValidationStatements.ts -stackDepthLimitCastingType.ts staticAsIdentifier.ts staticModifierAlreadySeen.ts -strictModeReservedWord.ts strictOptionalProperties1.ts superCallFromClassThatHasNoBaseType1.ts symbolMergeValueAndImportedType.ts -systemModuleWithSuperClass.ts -targetTypeCastTest.ts -tsxDeepAttributeAssignabilityError.tsx -uniqueSymbolPropertyDeclarationEmit.ts -unusedImports1.ts -unusedImports2.ts -unusedImports3.ts -unusedImports4.ts -unusedImports5.ts -unusedInvalidTypeArguments.ts -usedImportNotElidedInJs.ts -varAndFunctionShareName.ts varArgConstructorMemberParameter.ts -withStatement.ts -withStatementErrors.ts -withStatementInternalComments.ts -withStatementNestedScope.ts diff --git a/scripts/parser-tests/typescript/error-codes.js b/scripts/parser-tests/typescript/error-codes.js index 26145979de50..945f79c78950 100644 --- a/scripts/parser-tests/typescript/error-codes.js +++ b/scripts/parser-tests/typescript/error-codes.js @@ -9,6 +9,7 @@ The commented out diagnostic codes will introduce false positive cases that shou */ export default [ + "TS1002", // Unterminated string literal. "TS1003", // Identifier expected. "TS1005", // '{0}' expected. "TS1009", // Trailing comma not allowed. @@ -18,6 +19,7 @@ export default [ "TS1029", // '{0}' modifier must precede '{1}' modifier. "TS1030", // '{0}' modifier already seen. "TS1031", // '{0}' modifier cannot appear on a class element. + "TS1034", // 'super' must be followed by an argument list or member access. "TS1039", // Initializers are not allowed in ambient contexts. "TS1042", // '{0}' modifier cannot be used here. "TS1048", // A rest parameter cannot have an initializer. @@ -32,47 +34,67 @@ export default [ "TS1105", // A 'break' statement can only be used within an enclosing iteration or switch statement. "TS1107", // Jump target cannot cross function boundary. "TS1108", // A 'return' statement can only be used within a function body. + "TS1109", // Expression expected. + "TS1110", // Type expected. + "TS1011", // An element access expression should take an argument. "TS1113", // A 'default' clause cannot appear more than once in a 'switch' statement. "TS1115", // A 'continue' statement can only jump to a label of an enclosing iteration statement. "TS1116", // A 'break' statement can only jump to a label of an enclosing statement. "TS1123", // Variable declaration list cannot be empty. + "TS1126", // Unexpected end of text. + "TS1127", // Invalid character. + "TS1128", // Declaration or statement expected. + "TS1135", // Argument expression expected. + "TS1136", // Property assignment expected. "TS1141", // String literal expected. "TS1142", // Line break not permitted here. + "TS1144", // '{' or ';' expected. "TS1155", // 'const' declarations must be initialized. + "TS1160", // Unterminated template literal. + "TS1161", // Unterminated regular expression literal. "TS1162", // An object member cannot be declared optional. "TS1163", // A 'yield' expression is only allowed in a generator body. "TS1184", // Modifiers cannot appear here. + "TS1185", // Merge conflict marker encountered. "TS1191", // An import declaration cannot have modifiers. "TS1196", // Catch clause variable type annotation must be 'any' or 'unknown' if specified. "TS1197", // Catch clause variable cannot have an initializer. "TS1200", // Line terminator not permitted before arrow. - "TS1312", // '=' can only be used in an object literal property inside a destructuring assignment. - // "TS1212", // Identifier expected. '{0}' is a reserved word in strict mode." - // "TS1213", // Identifier expected. '{0}' is a reserved word in strict mode. Class definitions are automatically in strict mode. - // "TS1214", // Identifier expected. '{0}' is a reserved word in strict mode. Modules are automatically in strict mode. + "TS1212", // Identifier expected. '{0}' is a reserved word in strict mode." + "TS1213", // Identifier expected. '{0}' is a reserved word in strict mode. Class definitions are automatically in strict mode. + "TS1214", // Identifier expected. '{0}' is a reserved word in strict mode. Modules are automatically in strict mode. "TS1246", // An interface property cannot have an initializer. "TS1247", // A type literal property cannot have an initializer. "TS1248", // A class member cannot have the 'const' keyword. + "TS1260", // Keywords cannot contain escape characters. "TS1308", // 'await' expression is only allowed within an async function. + "TS1312", // '=' can only be used in an object literal property inside a destructuring assignment. + "TS1384", // A 'new' expression with type arguments must always be followed by a parenthesized argument list. + "TS1385", // Function type notation must be parenthesized when used in a union type. + "TS1386", // Constructor type notation must be parenthesized when used in a union type. + "TS1437", // Namespace must be given a name. // "TS2300", // Duplicate identifier '{0}'. "TS2337", // Super calls are not permitted outside constructors or in nested functions inside constructors. - "TS2389", // Function implementation name must be '${0}'. - "TS2390", // Constructor implementation is missing. - "TS2391", // Function implementation is missing or not immediately following the declaration. + // "TS2340", // Only public and protected methods of the base class are accessible via the 'super' keyword. "TS2364", // The left-hand side of an assignment expression must be a variable or a property access. - "TS2369", // A parameter property is only allowed in a constructor implementation. + // "TS2369", // A parameter property is only allowed in a constructor implementation. // "TS2371", // A parameter initializer is only allowed in a function or constructor implementation. - // "TS2393", // Duplicate function implementation. + //"TS2393", // Duplicate function implementation. "TS2396", // Duplicate identifier 'arguments'. Compiler uses 'arguments' to initialize rest parameters. // "TS2440", // Import declaration conflicts with local declaration of '{0}'. // "TS2451", // Cannot redeclare block-scoped variable '{0}'. "TS2452", // An enum member cannot have a numeric name. "TS2566", // A rest element cannot have a property name. + //"TS2580", "TS2481", // Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{0}'. // "TS2567", // Enum declarations can only merge with namespace or other enum declarations. "TS2659", // 'super' is only allowed in members of object literal expressions when option 'target' is 'ES2015' or higher. "TS2660", // 'super' can only be referenced in members of derived classes or object literal expressions. + //"TS2693", // 'interface' only refers to a type, but is being used as a value here. "TS2699", // Static property '{0}' conflicts with built-in property 'Function.{0}' of constructor function '{1}'. + "TS2754", // 'super' may not use type arguments. + "TS2809", // Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses. + "TS2815", // 'arguments' cannot be referenced in property initializers. "TS8018", // Octal literals are not allowed in enums members initializer. // "TS17012", // '{0}' is not a valid meta-property for keyword '{1}'. Did you mean '{2}'? ]; diff --git a/scripts/parser-tests/typescript/index.js b/scripts/parser-tests/typescript/index.js index 8d7a1e27c418..8943af1c8c3e 100644 --- a/scripts/parser-tests/typescript/index.js +++ b/scripts/parser-tests/typescript/index.js @@ -24,7 +24,6 @@ const dirname = path.dirname(fileURLToPath(import.meta.url)); function* loadTests(dir) { const names = fs.readdirSync(dir).map(name => [name, path.join(dir, name)]); - //.filter(([name]) => allowed.has(name)); for (const [name, filename] of names) { const encoding = getEncoding(filename); @@ -44,27 +43,6 @@ const TSTestsPath = path.join(dirname, "../../../build/typescript/tests"); // Check if the baseline errors contain the codes that should also be thrown from babel-parser function baselineContainsParserErrorCodes(testName) { try { - if (testName.includes("aliasErrors")) { - console.log( - "WILL TEST " + - path.join( - TSTestsPath, - "baselines/reference", - testName.replace(/\.tsx?$/, ".errors.txt") - ) - ); - console.log( - "WILL TEST " + - fs.readFileSync( - path.join( - TSTestsPath, - "baselines/reference", - testName.replace(/\.tsx?$/, ".errors.txt") - ), - "utf8" - ) - ); - } return ErrorCodeRegExp.test( fs.readFileSync( path.join( @@ -84,6 +62,7 @@ function baselineContainsParserErrorCodes(testName) { } const IgnoreRegExp = /@noTypesAndSymbols|ts-ignore|\n#!/; +const AlwaysStrictRegExp = /(^|\n)\/\/\s*@alwaysStrict:\s*true/; const runner = new TestRunner({ testDir: path.join(TSTestsPath, "./cases/compiler"), @@ -94,34 +73,40 @@ const runner = new TestRunner({ *getTests() { for (const test of loadTests(this.testDir)) { if (IgnoreRegExp.test(test.contents)) { + yield { id: test.name, expectedError: false, contents: "" }; continue; } - yield { - contents: splitTwoslashCodeInfoFiles( - test.contents, - "default", - `${test.name}/` - ) - .map(([filename, lines]) => [ - filename.replace(/\/default$/, ""), - lines.join("\n"), - ]) - .filter(([sourceFilename]) => !sourceFilename.endsWith(".json")) - .map(([sourceFilename, contents]) => ({ - contents, - sourceFilename, - sourceType: "module", - plugins: sourceFilename.endsWith(".tsx") ? pluginsWithJSX : plugins, - })), - expectedError: baselineContainsParserErrorCodes(test.name), - sourceFilename: test.name, - id: test.name, - }; + const strictMode = AlwaysStrictRegExp.test(test.contents); + const files = toFiles(strictMode, test.contents, test.name); + const expectedError = + files.length > 0 && baselineContainsParserErrorCodes(test.name); + + yield { id: test.name, expectedError, contents: files }; } }, }); +function toFiles(strictMode, contents, name) { + return splitTwoslashCodeInfoFiles(contents, "default", `${name}/`) + .map(([filename, lines]) => [ + filename.replace(/\/default$/, ""), + lines.join("\n"), + ]) + .filter( + ([sourceFilename, contents]) => + !/\.(css|js|json|md)$/.test(sourceFilename) && + contents.split("\n").some(line => !/(^\s*$)|(^\/\/[^\n]*$)/.test(line)) + ) + .map(([sourceFilename, contents]) => ({ + contents, + sourceFilename, + sourceType: "script", + strictMode, + plugins: /\.(t|j)sx$/.test(sourceFilename) ? pluginsWithJSX : plugins, + })); +} + const BracketedFileRegExp = /\/\/\/\/\s*\[([^\]]+)\][^\n]*(\n|$)/; const AtFileRegExp = /(?:^|\n)\/\/\s*@filename:\s*([^\s]*)\s*(?:\n|$)/i; diff --git a/scripts/parser-tests/utils/parser-test-runner.js b/scripts/parser-tests/utils/parser-test-runner.js index dc096ef6a430..acb2bceba796 100644 --- a/scripts/parser-tests/utils/parser-test-runner.js +++ b/scripts/parser-tests/utils/parser-test-runner.js @@ -60,8 +60,8 @@ class TestRunner { parse(test, parser) { const tests = typeof test.contents === "string" ? [test] : test.contents; - for (const { contents, sourceType, plugins, sourceFilename } of tests) { - parser(contents, { sourceType, plugins, sourceFilename }); + for (const { contents, ...options } of tests) { + parser(contents, options); } } From 8eb49b96b467d12965c0e9fee859357ef5818506 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 18 Feb 2022 11:53:06 -0800 Subject: [PATCH 051/104] Change test to pass since typescript should allow import. Reviewed by @tolmasky. --- .../test/fixtures/node-extensions/import-in-cts/options.json | 3 --- .../test/fixtures/node-extensions/import-in-cts/output.js | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/options.json create mode 100644 packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/output.js diff --git a/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/options.json b/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/options.json deleted file mode 100644 index afbed324e92a..000000000000 --- a/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "'import' and 'export' may appear only with 'sourceType: \"module\"' (1:0)" -} diff --git a/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/output.js b/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/output.js new file mode 100644 index 000000000000..856f26b34c30 --- /dev/null +++ b/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/output.js @@ -0,0 +1 @@ +import "x"; From 5ffc2344c35896232fc7a2aab3bf3f4a57118cee Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 15 Feb 2022 08:38:44 -0800 Subject: [PATCH 052/104] Update tests that should fail. Reviewed by @tolmasky. --- .../cast/unparenthesized-assert-and-assign/output.json | 3 ++- .../typescript/class/modifier-name-parameters/output.json | 5 +++++ .../typescript/interface/declare-new-line/output.json | 3 ++- .../interface/invalid-abstract-interface/output.json | 1 + .../fixtures/typescript/interface/new-line-error/output.json | 1 + .../test/fixtures/typescript/interface/new-line/output.json | 3 +++ .../export-type-only-keyword/output.json | 5 +++++ .../import-invalid-named-type-as-keyword/output.json | 3 +++ .../import-invalid-type-only-as-as-keyword/output.json | 3 +++ 9 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-assert-and-assign/output.json b/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-assert-and-assign/output.json index 27f637009e37..55b9929fc45c 100644 --- a/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-assert-and-assign/output.json +++ b/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-assert-and-assign/output.json @@ -3,7 +3,8 @@ "start":0,"end":46,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":29,"index":46}}, "errors": [ "SyntaxError: Invalid left-hand side in assignment expression. (1:0)", - "SyntaxError: Invalid left-hand side in assignment expression. (2:6)" + "SyntaxError: Invalid left-hand side in assignment expression. (2:6)", + "SyntaxError: Invalid left-hand side in object destructuring pattern. (2:6)" ], "program": { "type": "Program", diff --git a/packages/babel-parser/test/fixtures/typescript/class/modifier-name-parameters/output.json b/packages/babel-parser/test/fixtures/typescript/class/modifier-name-parameters/output.json index 67f37a900168..54c5974011ac 100644 --- a/packages/babel-parser/test/fixtures/typescript/class/modifier-name-parameters/output.json +++ b/packages/babel-parser/test/fixtures/typescript/class/modifier-name-parameters/output.json @@ -1,6 +1,11 @@ { "type": "File", "start":0,"end":58,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":4,"column":1,"index":58}}, + "errors": [ + "SyntaxError: Unexpected reserved word 'private'. (2:14)", + "SyntaxError: Unexpected reserved word 'public'. (2:23)", + "SyntaxError: Unexpected reserved word 'static'. (2:31)" + ], "program": { "type": "Program", "start":0,"end":58,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":4,"column":1,"index":58}}, diff --git a/packages/babel-parser/test/fixtures/typescript/interface/declare-new-line/output.json b/packages/babel-parser/test/fixtures/typescript/interface/declare-new-line/output.json index 42b06de8a2cf..261fac9031f6 100644 --- a/packages/babel-parser/test/fixtures/typescript/interface/declare-new-line/output.json +++ b/packages/babel-parser/test/fixtures/typescript/interface/declare-new-line/output.json @@ -2,7 +2,8 @@ "type": "File", "start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":2,"index":22}}, "errors": [ - "SyntaxError: Missing semicolon. (1:7)" + "SyntaxError: Missing semicolon. (1:7)", + "SyntaxError: Unexpected reserved word 'interface'. (1:8)" ], "program": { "type": "Program", diff --git a/packages/babel-parser/test/fixtures/typescript/interface/invalid-abstract-interface/output.json b/packages/babel-parser/test/fixtures/typescript/interface/invalid-abstract-interface/output.json index 7a5e16344620..9de6d15152d2 100644 --- a/packages/babel-parser/test/fixtures/typescript/interface/invalid-abstract-interface/output.json +++ b/packages/babel-parser/test/fixtures/typescript/interface/invalid-abstract-interface/output.json @@ -3,6 +3,7 @@ "start":0,"end":25,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":6,"index":25}}, "errors": [ "SyntaxError: Missing semicolon. (1:8)", + "SyntaxError: Unexpected reserved word 'interface'. (1:9)", "SyntaxError: Missing semicolon. (2:3)" ], "program": { diff --git a/packages/babel-parser/test/fixtures/typescript/interface/new-line-error/output.json b/packages/babel-parser/test/fixtures/typescript/interface/new-line-error/output.json index 7f0f930f1ac7..2577bd0675e7 100644 --- a/packages/babel-parser/test/fixtures/typescript/interface/new-line-error/output.json +++ b/packages/babel-parser/test/fixtures/typescript/interface/new-line-error/output.json @@ -2,6 +2,7 @@ "type": "File", "start":0,"end":14,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":4,"index":14}}, "errors": [ + "SyntaxError: Unexpected reserved word 'interface'. (1:0)", "SyntaxError: Missing semicolon. (2:1)" ], "program": { diff --git a/packages/babel-parser/test/fixtures/typescript/interface/new-line/output.json b/packages/babel-parser/test/fixtures/typescript/interface/new-line/output.json index cfb1ae21b44d..ecfc4a65ae62 100644 --- a/packages/babel-parser/test/fixtures/typescript/interface/new-line/output.json +++ b/packages/babel-parser/test/fixtures/typescript/interface/new-line/output.json @@ -1,6 +1,9 @@ { "type": "File", "start":0,"end":14,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":2,"index":14}}, + "errors": [ + "SyntaxError: Unexpected reserved word 'interface'. (1:0)" + ], "program": { "type": "Program", "start":0,"end":14,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":2,"index":14}}, diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json index 4754df21d84c..2f9439c2bf9e 100644 --- a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json @@ -1,6 +1,11 @@ { "type": "File", "start":0,"end":34,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":19,"index":34}}, + "errors": [ + "SyntaxError: Unexpected keyword 'if'. (1:6)", + "SyntaxError: Unexpected keyword 'if'. (2:14)", + "SyntaxError: Unexpected keyword 'if'. (2:14)" + ], "program": { "type": "Program", "start":0,"end":34,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":19,"index":34}}, diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/output.json index a86152d9dd87..97af608a6ac2 100644 --- a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/output.json +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/output.json @@ -1,6 +1,9 @@ { "type": "File", "start":0,"end":33,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":33,"index":33}}, + "errors": [ + "SyntaxError: Unexpected keyword 'if'. (1:17)" + ], "program": { "type": "Program", "start":0,"end":33,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":33,"index":33}}, diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/output.json index 5c16de83ee3d..4a3eaa15fda9 100644 --- a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/output.json +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/output.json @@ -1,6 +1,9 @@ { "type": "File", "start":0,"end":36,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":36,"index":36}}, + "errors": [ + "SyntaxError: Unexpected keyword 'if'. (1:20)" + ], "program": { "type": "Program", "start":0,"end":36,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":36,"index":36}}, From 4632b28a52c679ed24fff417fb09ed934baa2c4a Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 19 Feb 2022 11:49:58 -0800 Subject: [PATCH 053/104] Add tests for complex let assignments. Reviewed by @tolmasky. --- .../let/let-at-binding-list-fail-10/input.js | 1 + .../let-at-binding-list-fail-10/output.json | 56 +++++++++++++++ .../let/let-at-binding-list-fail-11/input.js | 1 + .../let-at-binding-list-fail-11/output.json | 47 ++++++++++++ .../let/let-at-binding-list-fail-12/input.js | 1 + .../let-at-binding-list-fail-12/output.json | 47 ++++++++++++ .../let/let-at-binding-list-fail-7/input.js | 1 + .../let-at-binding-list-fail-7/output.json | 71 +++++++++++++++++++ .../let/let-at-binding-list-fail-8/input.js | 1 + .../let-at-binding-list-fail-8/output.json | 71 +++++++++++++++++++ .../let/let-at-binding-list-fail-9/input.js | 1 + .../let-at-binding-list-fail-9/output.json | 56 +++++++++++++++ 12 files changed, 354 insertions(+) create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-10/input.js create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-10/output.json create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-11/input.js create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-11/output.json create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-12/input.js create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-12/output.json create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-7/input.js create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-7/output.json create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-8/input.js create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-8/output.json create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-9/input.js create mode 100644 packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-9/output.json diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-10/input.js b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-10/input.js new file mode 100644 index 000000000000..9367b5d6ac80 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-10/input.js @@ -0,0 +1 @@ +const [let = 10] = []; diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-10/output.json b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-10/output.json new file mode 100644 index 000000000000..28f4b76d6588 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-10/output.json @@ -0,0 +1,56 @@ +{ + "type": "File", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}}, + "errors": [ + "SyntaxError: 'let' is not allowed to be used as a name in 'let' or 'const' declarations. (1:7)" + ], + "program": { + "type": "Program", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":6,"end":21,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":21,"index":21}}, + "id": { + "type": "ArrayPattern", + "start":6,"end":16,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":16,"index":16}}, + "elements": [ + { + "type": "AssignmentPattern", + "start":7,"end":15,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":15,"index":15}}, + "left": { + "type": "Identifier", + "start":7,"end":10,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":10,"index":10},"identifierName":"let"}, + "name": "let" + }, + "right": { + "type": "NumericLiteral", + "start":13,"end":15,"loc":{"start":{"line":1,"column":13,"index":13},"end":{"line":1,"column":15,"index":15}}, + "extra": { + "rawValue": 10, + "raw": "10" + }, + "value": 10 + } + } + ] + }, + "init": { + "type": "ArrayExpression", + "start":19,"end":21,"loc":{"start":{"line":1,"column":19,"index":19},"end":{"line":1,"column":21,"index":21}}, + "elements": [] + } + } + ], + "kind": "const" + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-11/input.js b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-11/input.js new file mode 100644 index 000000000000..2b523cbca1d1 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-11/input.js @@ -0,0 +1 @@ +let [...let] = []; diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-11/output.json b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-11/output.json new file mode 100644 index 000000000000..f15724629df5 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-11/output.json @@ -0,0 +1,47 @@ +{ + "type": "File", + "start":0,"end":18,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":18,"index":18}}, + "errors": [ + "SyntaxError: 'let' is not allowed to be used as a name in 'let' or 'const' declarations. (1:8)" + ], + "program": { + "type": "Program", + "start":0,"end":18,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":18,"index":18}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":18,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":18,"index":18}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":4,"end":17,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":17,"index":17}}, + "id": { + "type": "ArrayPattern", + "start":4,"end":12,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":12,"index":12}}, + "elements": [ + { + "type": "RestElement", + "start":5,"end":11,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":11,"index":11}}, + "argument": { + "type": "Identifier", + "start":8,"end":11,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":1,"column":11,"index":11},"identifierName":"let"}, + "name": "let" + } + } + ] + }, + "init": { + "type": "ArrayExpression", + "start":15,"end":17,"loc":{"start":{"line":1,"column":15,"index":15},"end":{"line":1,"column":17,"index":17}}, + "elements": [] + } + } + ], + "kind": "let" + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-12/input.js b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-12/input.js new file mode 100644 index 000000000000..d0356c49fe26 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-12/input.js @@ -0,0 +1 @@ +const [...let] = []; diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-12/output.json b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-12/output.json new file mode 100644 index 000000000000..343ee3a4e706 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-12/output.json @@ -0,0 +1,47 @@ +{ + "type": "File", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":20,"index":20}}, + "errors": [ + "SyntaxError: 'let' is not allowed to be used as a name in 'let' or 'const' declarations. (1:10)" + ], + "program": { + "type": "Program", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":20,"index":20}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":20,"index":20}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":6,"end":19,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":19,"index":19}}, + "id": { + "type": "ArrayPattern", + "start":6,"end":14,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":14,"index":14}}, + "elements": [ + { + "type": "RestElement", + "start":7,"end":13,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":13,"index":13}}, + "argument": { + "type": "Identifier", + "start":10,"end":13,"loc":{"start":{"line":1,"column":10,"index":10},"end":{"line":1,"column":13,"index":13},"identifierName":"let"}, + "name": "let" + } + } + ] + }, + "init": { + "type": "ArrayExpression", + "start":17,"end":19,"loc":{"start":{"line":1,"column":17,"index":17},"end":{"line":1,"column":19,"index":19}}, + "elements": [] + } + } + ], + "kind": "const" + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-7/input.js b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-7/input.js new file mode 100644 index 000000000000..a36c3ad65c40 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-7/input.js @@ -0,0 +1 @@ +let { let = 10 } = {}; diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-7/output.json b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-7/output.json new file mode 100644 index 000000000000..9bc335e6439c --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-7/output.json @@ -0,0 +1,71 @@ +{ + "type": "File", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}}, + "errors": [ + "SyntaxError: 'let' is not allowed to be used as a name in 'let' or 'const' declarations. (1:6)" + ], + "program": { + "type": "Program", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":22,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":22,"index":22}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":4,"end":21,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":21,"index":21}}, + "id": { + "type": "ObjectPattern", + "start":4,"end":16,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":16,"index":16}}, + "properties": [ + { + "type": "ObjectProperty", + "start":6,"end":14,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":14,"index":14}}, + "key": { + "type": "Identifier", + "start":6,"end":9,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":9,"index":9},"identifierName":"let"}, + "name": "let" + }, + "computed": false, + "method": false, + "shorthand": true, + "value": { + "type": "AssignmentPattern", + "start":6,"end":14,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":14,"index":14}}, + "left": { + "type": "Identifier", + "start":6,"end":9,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":9,"index":9},"identifierName":"let"}, + "name": "let" + }, + "right": { + "type": "NumericLiteral", + "start":12,"end":14,"loc":{"start":{"line":1,"column":12,"index":12},"end":{"line":1,"column":14,"index":14}}, + "extra": { + "rawValue": 10, + "raw": "10" + }, + "value": 10 + } + }, + "extra": { + "shorthand": true + } + } + ] + }, + "init": { + "type": "ObjectExpression", + "start":19,"end":21,"loc":{"start":{"line":1,"column":19,"index":19},"end":{"line":1,"column":21,"index":21}}, + "properties": [] + } + } + ], + "kind": "let" + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-8/input.js b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-8/input.js new file mode 100644 index 000000000000..e7332f39e363 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-8/input.js @@ -0,0 +1 @@ +const { let = 10 } = {}; diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-8/output.json b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-8/output.json new file mode 100644 index 000000000000..59221a2026d8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-8/output.json @@ -0,0 +1,71 @@ +{ + "type": "File", + "start":0,"end":24,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":24,"index":24}}, + "errors": [ + "SyntaxError: 'let' is not allowed to be used as a name in 'let' or 'const' declarations. (1:8)" + ], + "program": { + "type": "Program", + "start":0,"end":24,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":24,"index":24}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":24,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":24,"index":24}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":6,"end":23,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":23,"index":23}}, + "id": { + "type": "ObjectPattern", + "start":6,"end":18,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":18,"index":18}}, + "properties": [ + { + "type": "ObjectProperty", + "start":8,"end":16,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":1,"column":16,"index":16}}, + "key": { + "type": "Identifier", + "start":8,"end":11,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":1,"column":11,"index":11},"identifierName":"let"}, + "name": "let" + }, + "computed": false, + "method": false, + "shorthand": true, + "value": { + "type": "AssignmentPattern", + "start":8,"end":16,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":1,"column":16,"index":16}}, + "left": { + "type": "Identifier", + "start":8,"end":11,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":1,"column":11,"index":11},"identifierName":"let"}, + "name": "let" + }, + "right": { + "type": "NumericLiteral", + "start":14,"end":16,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":16,"index":16}}, + "extra": { + "rawValue": 10, + "raw": "10" + }, + "value": 10 + } + }, + "extra": { + "shorthand": true + } + } + ] + }, + "init": { + "type": "ObjectExpression", + "start":21,"end":23,"loc":{"start":{"line":1,"column":21,"index":21},"end":{"line":1,"column":23,"index":23}}, + "properties": [] + } + } + ], + "kind": "const" + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-9/input.js b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-9/input.js new file mode 100644 index 000000000000..35d09470596a --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-9/input.js @@ -0,0 +1 @@ +let [let = 10] = []; diff --git a/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-9/output.json b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-9/output.json new file mode 100644 index 000000000000..0054c4e8f4ff --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/let/let-at-binding-list-fail-9/output.json @@ -0,0 +1,56 @@ +{ + "type": "File", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":20,"index":20}}, + "errors": [ + "SyntaxError: 'let' is not allowed to be used as a name in 'let' or 'const' declarations. (1:5)" + ], + "program": { + "type": "Program", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":20,"index":20}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":20,"index":20}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":4,"end":19,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":19,"index":19}}, + "id": { + "type": "ArrayPattern", + "start":4,"end":14,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":14,"index":14}}, + "elements": [ + { + "type": "AssignmentPattern", + "start":5,"end":13,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":13,"index":13}}, + "left": { + "type": "Identifier", + "start":5,"end":8,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":8,"index":8},"identifierName":"let"}, + "name": "let" + }, + "right": { + "type": "NumericLiteral", + "start":11,"end":13,"loc":{"start":{"line":1,"column":11,"index":11},"end":{"line":1,"column":13,"index":13}}, + "extra": { + "rawValue": 10, + "raw": "10" + }, + "value": 10 + } + } + ] + }, + "init": { + "type": "ArrayExpression", + "start":17,"end":19,"loc":{"start":{"line":1,"column":17,"index":17},"end":{"line":1,"column":19,"index":19}}, + "elements": [] + } + } + ], + "kind": "let" + } + ], + "directives": [] + } +} From 40c51b633c6bee227af97b596e593685fd5a54f0 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 20 Feb 2022 08:15:36 -0800 Subject: [PATCH 054/104] Choose a better name for these. Reviewed by @tolmasky. --- .../src/parse-error/strict-mode-errors.js | 20 +++++++++++++++---- packages/babel-parser/src/parser/lval.js | 16 +++++++++------ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/babel-parser/src/parse-error/strict-mode-errors.js b/packages/babel-parser/src/parse-error/strict-mode-errors.js index a760dbb553db..62bfe047acd2 100644 --- a/packages/babel-parser/src/parse-error/strict-mode-errors.js +++ b/packages/babel-parser/src/parse-error/strict-mode-errors.js @@ -4,12 +4,24 @@ import { toParseErrorClass } from "../parse-error"; export default (_: typeof toParseErrorClass) => ({ StrictDelete: _("Deleting local variable in strict mode."), - StrictEvalArguments: _<{| binding: string |}>( - ({ binding }) => `Assigning to '${binding}' in strict mode.`, + + // `bindingName` is the StringValue[1] of an IdentifierReference[2], which is + // represented as just an `Identifier`[3] in the Babel AST. + // 1. https://tc39.es/ecma262/#sec-static-semantics-stringvalue + // 2. https://tc39.es/ecma262/#prod-IdentifierReference + // 3. https://github.com/babel/babel/blob/main/packages/babel-parser/ast/spec.md#identifier + StrictEvalArguments: _<{| referenceName: string |}>( + ({ referenceName }) => `Assigning to '${referenceName}' in strict mode.`, ), - StrictEvalArgumentsBinding: _<{| binding: string |}>( - ({ binding }) => `Binding '${binding}' in strict mode.`, + // `bindingName` is the StringValue[1] of a BindingIdentifier[2], which is + // represented as just an `Identifier`[3] in the Babel AST. + // 1. https://tc39.es/ecma262/#sec-static-semantics-stringvalue + // 2. https://tc39.es/ecma262/#prod-BindingIdentifier + // 3. https://github.com/babel/babel/blob/main/packages/babel-parser/ast/spec.md#identifier + StrictEvalArgumentsBinding: _<{| bindingName: string |}>( + ({ bindingName }) => `Binding '${bindingName}' in strict mode.`, ), + StrictFunction: _( "In strict mode code, functions can only be declared at top level or inside a block.", ), diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index d7026f048048..e23e60d00eb6 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -555,12 +555,16 @@ export default class LValParser extends NodeUtils { ? isStrictBindReservedWord(name, this.inModule) : isStrictBindOnlyReservedWord(name)) ) { - this.raise( - bindingType === BIND_NONE - ? Errors.StrictEvalArguments - : Errors.StrictEvalArgumentsBinding, - { at: expr, binding: name }, - ); + const at = expr; + + if (bindingType === BIND_NONE) { + this.raise(Errors.StrictEvalArguments, { at, referenceName: name }); + } else { + this.raise(Errors.StrictEvalArgumentsBinding, { + at, + bindingName: name, + }); + } } if (checkClashes) { From c108c7e470adb046af52cb3dcdacd57cc1fec1da Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 20 Feb 2022 12:03:09 -0800 Subject: [PATCH 055/104] Add tests for a number of left-hand side expressions. Reviewed by @tolmasky. --- .../invalid-assignment-pattern-6/input.js | 1 + .../invalid-assignment-pattern-6/output.json | 53 +++++++++++++++ .../invalid-assignment-pattern-7/input.js | 1 + .../invalid-assignment-pattern-7/output.json | 53 +++++++++++++++ .../invalid-assignment-pattern-8/input.js | 1 + .../invalid-assignment-pattern-8/output.json | 66 +++++++++++++++++++ .../input.js | 1 + .../output.json | 41 ++++++++++++ .../input.js | 1 + .../output.json | 41 ++++++++++++ 10 files changed, 259 insertions(+) create mode 100644 packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-6/input.js create mode 100644 packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-6/output.json create mode 100644 packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-7/input.js create mode 100644 packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-7/output.json create mode 100644 packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-8/input.js create mode 100644 packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-8/output.json create mode 100644 packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-postfix-operation/input.js create mode 100644 packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-postfix-operation/output.json create mode 100644 packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-prefix-operation/input.js create mode 100644 packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-prefix-operation/output.json diff --git a/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-6/input.js b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-6/input.js new file mode 100644 index 000000000000..b87d4aead5d1 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-6/input.js @@ -0,0 +1 @@ +(a += 1) = t \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-6/output.json b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-6/output.json new file mode 100644 index 000000000000..b924fb6f6ed4 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-6/output.json @@ -0,0 +1,53 @@ +{ + "type": "File", + "start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}}, + "errors": [ + "SyntaxError: Invalid parenthesized assignment pattern. (1:1)", + "SyntaxError: Only '=' operator can be used for specifying default value. (1:2)" + ], + "program": { + "type": "Program", + "start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}}, + "expression": { + "type": "AssignmentExpression", + "start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}}, + "operator": "=", + "left": { + "type": "AssignmentPattern", + "start":1,"end":7,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":7,"index":7}}, + "left": { + "type": "Identifier", + "start":1,"end":2,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":2,"index":2},"identifierName":"a"}, + "name": "a" + }, + "right": { + "type": "NumericLiteral", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":7,"index":7}}, + "extra": { + "rawValue": 1, + "raw": "1" + }, + "value": 1 + }, + "extra": { + "parenthesized": true, + "parenStart": 0 + } + }, + "right": { + "type": "Identifier", + "start":11,"end":12,"loc":{"start":{"line":1,"column":11,"index":11},"end":{"line":1,"column":12,"index":12},"identifierName":"t"}, + "name": "t" + } + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-7/input.js b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-7/input.js new file mode 100644 index 000000000000..72749bdebdd6 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-7/input.js @@ -0,0 +1 @@ +(a -= 1) = t \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-7/output.json b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-7/output.json new file mode 100644 index 000000000000..b924fb6f6ed4 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-7/output.json @@ -0,0 +1,53 @@ +{ + "type": "File", + "start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}}, + "errors": [ + "SyntaxError: Invalid parenthesized assignment pattern. (1:1)", + "SyntaxError: Only '=' operator can be used for specifying default value. (1:2)" + ], + "program": { + "type": "Program", + "start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}}, + "expression": { + "type": "AssignmentExpression", + "start":0,"end":12,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":12,"index":12}}, + "operator": "=", + "left": { + "type": "AssignmentPattern", + "start":1,"end":7,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":7,"index":7}}, + "left": { + "type": "Identifier", + "start":1,"end":2,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":2,"index":2},"identifierName":"a"}, + "name": "a" + }, + "right": { + "type": "NumericLiteral", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":7,"index":7}}, + "extra": { + "rawValue": 1, + "raw": "1" + }, + "value": 1 + }, + "extra": { + "parenthesized": true, + "parenStart": 0 + } + }, + "right": { + "type": "Identifier", + "start":11,"end":12,"loc":{"start":{"line":1,"column":11,"index":11},"end":{"line":1,"column":12,"index":12},"identifierName":"t"}, + "name": "t" + } + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-8/input.js b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-8/input.js new file mode 100644 index 000000000000..b68d94e47c7a --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-8/input.js @@ -0,0 +1 @@ +(b = (a -= 1)) = t \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-8/output.json b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-8/output.json new file mode 100644 index 000000000000..0af83cf4d603 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/invalid-assignment-pattern-8/output.json @@ -0,0 +1,66 @@ +{ + "type": "File", + "start":0,"end":18,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":18,"index":18}}, + "errors": [ + "SyntaxError: Invalid parenthesized assignment pattern. (1:1)" + ], + "program": { + "type": "Program", + "start":0,"end":18,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":18,"index":18}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":18,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":18,"index":18}}, + "expression": { + "type": "AssignmentExpression", + "start":0,"end":18,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":18,"index":18}}, + "operator": "=", + "left": { + "type": "AssignmentPattern", + "start":1,"end":13,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":13,"index":13}}, + "left": { + "type": "Identifier", + "start":1,"end":2,"loc":{"start":{"line":1,"column":1,"index":1},"end":{"line":1,"column":2,"index":2},"identifierName":"b"}, + "name": "b" + }, + "right": { + "type": "AssignmentExpression", + "start":6,"end":12,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":12,"index":12}}, + "operator": "-=", + "left": { + "type": "Identifier", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":7,"index":7},"identifierName":"a"}, + "name": "a" + }, + "right": { + "type": "NumericLiteral", + "start":11,"end":12,"loc":{"start":{"line":1,"column":11,"index":11},"end":{"line":1,"column":12,"index":12}}, + "extra": { + "rawValue": 1, + "raw": "1" + }, + "value": 1 + }, + "extra": { + "parenthesized": true, + "parenStart": 5 + } + }, + "extra": { + "parenthesized": true, + "parenStart": 0 + } + }, + "right": { + "type": "Identifier", + "start":17,"end":18,"loc":{"start":{"line":1,"column":17,"index":17},"end":{"line":1,"column":18,"index":18},"identifierName":"t"}, + "name": "t" + } + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-postfix-operation/input.js b/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-postfix-operation/input.js new file mode 100644 index 000000000000..4316929d0fa8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-postfix-operation/input.js @@ -0,0 +1 @@ +a++ = t \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-postfix-operation/output.json b/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-postfix-operation/output.json new file mode 100644 index 000000000000..5534fdbb0550 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-postfix-operation/output.json @@ -0,0 +1,41 @@ +{ + "type": "File", + "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}, + "errors": [ + "SyntaxError: Invalid left-hand side in assignment expression. (1:0)" + ], + "program": { + "type": "Program", + "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}, + "expression": { + "type": "AssignmentExpression", + "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}, + "operator": "=", + "left": { + "type": "UpdateExpression", + "start":0,"end":3,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":3,"index":3}}, + "operator": "++", + "prefix": false, + "argument": { + "type": "Identifier", + "start":0,"end":1,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":1,"index":1},"identifierName":"a"}, + "name": "a" + } + }, + "right": { + "type": "Identifier", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":7,"index":7},"identifierName":"t"}, + "name": "t" + } + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-prefix-operation/input.js b/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-prefix-operation/input.js new file mode 100644 index 000000000000..3e635e2e1733 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-prefix-operation/input.js @@ -0,0 +1 @@ +++a = t \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-prefix-operation/output.json b/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-prefix-operation/output.json new file mode 100644 index 000000000000..583c0a9ffab2 --- /dev/null +++ b/packages/babel-parser/test/fixtures/core/categorized/invalid-left-hand-side-in-prefix-operation/output.json @@ -0,0 +1,41 @@ +{ + "type": "File", + "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}, + "errors": [ + "SyntaxError: Invalid left-hand side in assignment expression. (1:0)" + ], + "program": { + "type": "Program", + "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}, + "expression": { + "type": "AssignmentExpression", + "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}}, + "operator": "=", + "left": { + "type": "UpdateExpression", + "start":0,"end":3,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":3,"index":3}}, + "operator": "++", + "prefix": true, + "argument": { + "type": "Identifier", + "start":2,"end":3,"loc":{"start":{"line":1,"column":2,"index":2},"end":{"line":1,"column":3,"index":3},"identifierName":"a"}, + "name": "a" + } + }, + "right": { + "type": "Identifier", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":7,"index":7},"identifierName":"t"}, + "name": "t" + } + } + } + ], + "directives": [] + } +} From 660971f3a9148c4edbfd3f7725139be78eff0e66 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 20 Feb 2022 15:09:38 -0800 Subject: [PATCH 056/104] Change name to kind. Reviewed by @tolmasky. --- .../babel-parser/src/plugins/typescript/index.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 12bf578be472..eac6fa6d6367 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -99,9 +99,8 @@ const TSErrors = toParseErrorClasses( ConstructorHasTypeParameters: _( "Type parameters cannot appear on a constructor declaration.", ), - // kind? - DeclareAccessor: _<{| accessorKind: "get" | "set" |}>( - ({ accessorKind }) => `'declare' is not allowed in ${accessorKind}ters.`, + DeclareAccessor: _<{| kind: "get" | "set" |}>( + ({ kind }) => `'declare' is not allowed in ${kind}ters.`, ), DeclareClassFieldHasInitializer: _( "Initializers are not allowed in ambient contexts.", @@ -2993,11 +2992,10 @@ export default (superClass: Class): Class => } // $FlowIgnore - if (method.declare && (method.kind === "get" || method.kind === "set")) { - this.raise(TSErrors.DeclareAccessor, { - at: method, - accessorKind: method.kind, - }); + const { declare = false, kind } = method; + + if (declare && (kind === "get" || kind === "set")) { + this.raise(TSErrors.DeclareAccessor, { at: method, kind }); } if (typeParameters) method.typeParameters = typeParameters; super.pushClassMethod( From 690c3c3da121d08bef6cfac1e8ad81e074dd3865 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 22 Feb 2022 08:49:24 -0800 Subject: [PATCH 057/104] Choose better parameter names for these errors. Reviewed by @tolmasky. --- .../src/plugins/typescript/index.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index eac6fa6d6367..1f69976f2844 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -109,15 +109,17 @@ const TSErrors = toParseErrorClasses( "An implementation cannot be declared in ambient contexts.", ), DuplicateAccessibilityModifier: _<{| modifier: N.Accessibility |}>( - // `Accessibility modifier ${modifier} already seen.` would be more helpful. + // `Accessibility modifier already seen: ${modifier}` would be more helpful. // eslint-disable-next-line no-unused-vars ({ modifier }) => `Accessibility modifier already seen.`, ), DuplicateModifier: _<{| modifier: TsModifier |}>( ({ modifier }) => `Duplicate modifier: '${modifier}'.`, ), - EmptyHeritageClauseType: _<{| descriptor: string |}>( - ({ descriptor }) => `'${descriptor}' list cannot be empty.`, + // `token` matches the terminology used by typescript: + // https://github.com/microsoft/TypeScript/blob/main/src/compiler/types.ts#L2915 + EmptyHeritageClauseType: _<{| token: "extends" | "implements" |}>( + ({ token }) => `'${token}' list cannot be empty.`, ), EmptyTypeArguments: _("Type argument list cannot be empty."), EmptyTypeParameters: _("Type parameter list cannot be empty."), @@ -239,11 +241,9 @@ const TSErrors = toParseErrorClasses( UnsupportedParameterPropertyKind: _( "A parameter property may not be declared using a binding pattern.", ), - UnsupportedSignatureParameterKind: _<{| - unsupportedParameterType: string, - |}>( - ({ unsupportedParameterType }) => - `Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got ${unsupportedParameterType}.`, + UnsupportedSignatureParameterKind: _<{| type: string |}>( + ({ type }) => + `Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got ${type}.`, ), }), { syntaxPlugin: "typescript" }, @@ -743,7 +743,7 @@ export default (superClass: Class): Class => ) { this.raise(TSErrors.UnsupportedSignatureParameterKind, { at: pattern, - unsupportedParameterType: pattern.type, + type: pattern.type, }); } return (pattern: any); @@ -1583,7 +1583,7 @@ export default (superClass: Class): Class => } tsParseHeritageClause( - descriptor: string, + token: "extends" | "implements", ): $ReadOnlyArray { const originalStartLoc = this.state.startLoc; @@ -1595,7 +1595,7 @@ export default (superClass: Class): Class => if (!delimitedList.length) { this.raise(TSErrors.EmptyHeritageClauseType, { at: originalStartLoc, - descriptor, + token, }); } From e4137ee9a14f3af5eac9eab29f2fc6d7467e3f72 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 22 Feb 2022 17:15:12 -0800 Subject: [PATCH 058/104] Use error credentials and error details. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 211 +++++++---- .../src/parse-error/credentials.js | 16 + .../src/parse-error/module-errors.js | 4 +- .../src/parse-error/standard-errors.js | 4 +- .../src/parse-error/strict-mode-errors.js | 4 +- .../babel-parser/src/parser/error-codes.js | 8 - .../babel-parser/src/plugins/flow/index.js | 345 +++++++++-------- .../babel-parser/src/plugins/jsx/index.js | 57 ++- .../babel-parser/src/plugins/placeholders.js | 11 +- .../src/plugins/typescript/index.js | 354 +++++++++--------- packages/babel-parser/src/tokenizer/index.js | 32 +- 11 files changed, 550 insertions(+), 496 deletions(-) create mode 100644 packages/babel-parser/src/parse-error/credentials.js delete mode 100644 packages/babel-parser/src/parser/error-codes.js diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 5c50d7d440b9..7913fa6425c9 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -2,122 +2,181 @@ import { Position } from "./util/location"; import type { NodeBase } from "./types"; +import { + type ParseErrorCode, + ParseErrorCodes, + type ParseErrorCredentials, +} from "./parse-error/credentials"; + +const ArrayIsArray = Array.isArray; +const { + assign: ObjectAssign, + defineProperty: ObjectDefineProperty, + keys: ObjectKeys, +} = Object; + +type ToMessage = (self: ErrorDetails) => string; + +const NoMessage = Symbol("NoMessage"); + +// This should really be an abstract class, but that concept doesn't exist in +// Flow, outside of just creating an interface, but then you can't specify that +// it's a subclass of `SyntaxError`. You could do something like: +// +// interface IParseError { ... } +// type ParseError = SyntaxError & IParseError; +// +// But this is just a more complicated way of getting the same behavior, with +// the added clumsiness of not being able to extends ParseError directly. So, +// to keep things simple and prepare for a Typescript future, we just make it a +// "private" superclass that we exclusively subclass: + +export class ParseError extends SyntaxError { + code: ParseErrorCode; + reasonCode: string; -const { assign: ObjectAssign, keys: ObjectKeys } = Object; + loc: Position; + details: ErrorDetails; -const ManuallyAssignedErrorMessages = new WeakMap, string>(); + // There are no optional fields in classes in Flow, so we can't list these + // here: https://github.com/facebook/flow/issues/2859 + // syntaxPlugin?: SyntaxPlugin + // missingPlugin?: string[] -export const ParseErrorCodes = Object.freeze({ - SyntaxError: "BABEL_PARSER_SYNTAX_ERROR", - SourceTypeModuleError: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", -}); + constructor({ + loc, + details, + }: { + loc: Position, + details: ErrorDetails, + }): ParseError { + super(); -export type ParseErrorCode = $Values; + this.loc = loc; -type ToMessage = (self: ErrorProperties) => string; + ObjectDefineProperty(this, "details", { + value: details, + enumerable: false, + }); -export class ParseError extends SyntaxError { - static code: ParseErrorCode; - static reasonCode: string; - static syntaxPlugin: ?string; - static toMessage: ToMessage; + // $FlowIgnore + if (details.missingPlugin) { + ObjectDefineProperty(this, "missingPlugin", { + get() { + return this.details.missingPlugin; + }, + enumerable: true, + }); + } - // Unfortunately babel-standalone turns this into a straight assigmnent - // instead of a defineProperty, so it throws an error. We do it manually - // below instead. - // - // static name: string = "SyntaxError"; + return this; + } - code: ParseErrorCode = this.constructor.code; - reasonCode: string = this.constructor.reasonCode; - syntaxPlugin: ?string = this.constructor.syntaxPlugin; + get pos() { + return this.loc.index; + } +} - loc: Position; - pos: number; +function toParseErrorClass( + toMessage: ToMessage, + credentials: ParseErrorCredentials, +): Class> { + return class extends ParseError { + #message: typeof NoMessage | string = NoMessage; + + constructor(...args): ParseError { + super(...args); + // $FlowIgnore - Only necessary because we can't make syntaxPlugin optional. + ObjectAssign(this, credentials); + return this; + } - constructor(properties: { - ...ErrorProperties, - loc: Position, - }): ParseError { - super(); + get message() { + return this.#message !== NoMessage + ? String(this.#message) + : `${toMessage(this.details)} (${this.loc.line}:${this.loc.column})`; + } - return ObjectAssign(this, { - ...properties, - pos: properties.loc.index, - }); - } + set message(message) { + this.#message = message; + } + }; } -declare function toParseErrorClass( +// This part is tricky, and only necessary due to some bugs in Flow that won't +// be fixed for a year(?): https://github.com/facebook/flow/issues/8838 +// Flow has a very difficult time extracting the parameter types of functions, +// so we are forced to pretend the class exists earlier than it does. +// `toParseErrorCredentials` *does not* actuall return a +// `Class>`, but we simply mark it as such to "carry" +// the `ErrorDetails` type parameter around. This is not a problem in Typescript +// where this intermediate function actually won't be needed at all. +declare function toParseErrorCredentials( T, - ?{ code?: ParseErrorCode } | boolean, + ?{ code?: ParseErrorCode, reasonCode?: string } | boolean, ): Class>; +// ESLint seems to erroneously think that Flow's overloading syntax is an +// accidental redeclaration of the function: +// https://github.com/babel/eslint-plugin-babel/issues/162 // eslint-disable-next-line no-redeclare -declare function toParseErrorClass( +declare function toParseErrorCredentials( (T) => string, - ?{ code?: ParseErrorCode } | boolean, + ?{ code?: ParseErrorCode, reasonCode?: string } | boolean, ): Class>; +// See comment about eslint and Flow overloading above. // eslint-disable-next-line no-redeclare -export function toParseErrorClass(toMessageOrMessage, properties) { - return fromToMessage( +export function toParseErrorCredentials(toMessageOrMessage, credentials) { + return [ typeof toMessageOrMessage === "string" ? () => toMessageOrMessage : toMessageOrMessage, - properties, - ); + credentials, + ]; } -function fromToMessage( - toMessage: ToMessage, - { code = ParseErrorCodes.SyntaxError, reasonCode = "" } = {}, -): Class> { - return class extends ParseError { - static code: ParseErrorCode = code; - static reasonCode: string = reasonCode; +declare function toParseErrorClasses(string[]): typeof toParseErrorClasses; - get message() { - return ManuallyAssignedErrorMessages.has(this) - ? // $FlowIgnore - ManuallyAssignedErrorMessages.get(this) - : // $FlowIgnore - `${toMessage(this)} (${this.loc.line}:${this.loc.column})`; - } - set message(message) { - ManuallyAssignedErrorMessages.set(this, message); - } - }; -} +// See comment about eslint and Flow overloading above. +// eslint-disable-next-line no-redeclare +declare function toParseErrorClasses( + toClasses: (typeof toParseErrorCredentials) => T, + syntaxPlugin?: string, +): T; + +// See comment about eslint and Flow overloading above. +// eslint-disable-next-line no-redeclare +export function toParseErrorClasses(argument, syntaxPlugin) { + if (ArrayIsArray(argument)) { + return toClasses => toParseErrorClasses(toClasses, argument[0]); + } -export function toParseErrorClasses( - toClasses: (typeof toParseErrorClass) => T, - { syntaxPlugin }: Object = {}, -): T { - const classes = toClasses(toParseErrorClass); + const classes = argument(toParseErrorCredentials); for (const reasonCode of ObjectKeys(classes)) { - const ParseErrorClass = classes[reasonCode]; + const [toMessage, credentials = {}] = classes[reasonCode]; + const ParseErrorClass = toParseErrorClass(toMessage, { + code: credentials.code || ParseErrorCodes.SyntaxError, + reasonCode: credentials.reasonCode || reasonCode, + ...(syntaxPlugin ? { syntaxPlugin } : {}), + }); + + classes[reasonCode] = ParseErrorClass; // We do this for backwards compatibility so that all errors just have the // "SyntaxError" name in their messages instead of leaking the private // subclass name. - Object.defineProperty(ParseErrorClass.prototype.constructor, "name", { + ObjectDefineProperty(ParseErrorClass.prototype.constructor, "name", { value: "SyntaxError", }); - - classes[reasonCode] = ObjectAssign( - ParseErrorClass, - !ParseErrorClass.reasonCode && { reasonCode }, - !!syntaxPlugin && { syntaxPlugin }, - ); } return classes; } -export type RaiseProperties = {| - ...ErrorProperties, +export type RaiseProperties = {| + ...ErrorDetails, at: Position | NodeBase, |}; @@ -130,3 +189,5 @@ export const Errors = { ...toParseErrorClasses(StandardErrors), ...toParseErrorClasses(StrictModeErrors), }; + +export * from "./parse-error/credentials"; diff --git a/packages/babel-parser/src/parse-error/credentials.js b/packages/babel-parser/src/parse-error/credentials.js new file mode 100644 index 000000000000..d2b6073b4835 --- /dev/null +++ b/packages/babel-parser/src/parse-error/credentials.js @@ -0,0 +1,16 @@ +// @flow + +export const ParseErrorCodes = Object.freeze({ + SyntaxError: "BABEL_PARSER_SYNTAX_ERROR", + SourceTypeModuleError: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", +}); + +export type ParseErrorCode = $Values; + +export type SyntaxPlugin = "flow" | "typescript" | "jsx" | "placeholders"; + +export type ParseErrorCredentials = { + code: ParseErrorCode, + reasonCode: string, + syntaxPlugin?: SyntaxPlugin, +}; diff --git a/packages/babel-parser/src/parse-error/module-errors.js b/packages/babel-parser/src/parse-error/module-errors.js index c68d25d472bc..82f83c222731 100644 --- a/packages/babel-parser/src/parse-error/module-errors.js +++ b/packages/babel-parser/src/parse-error/module-errors.js @@ -1,8 +1,8 @@ // @flow -import { ParseErrorCodes, toParseErrorClass } from "../parse-error"; +import { ParseErrorCodes, toParseErrorCredentials } from "../parse-error"; -export default (_: typeof toParseErrorClass) => ({ +export default (_: typeof toParseErrorCredentials) => ({ ImportMetaOutsideModule: _( `import.meta may appear only with 'sourceType: "module"'`, { code: ParseErrorCodes.SourceTypeModuleError }, diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.js index 19f25db03d5b..e4a52d4e7605 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.js @@ -1,8 +1,8 @@ // @flow -import { toParseErrorClass } from "../parse-error"; +import { toParseErrorCredentials } from "../parse-error"; -export default (_: typeof toParseErrorClass) => ({ +export default (_: typeof toParseErrorCredentials) => ({ AccessorIsGenerator: _<{| kind: "get" | "set" |}>( ({ kind }) => `A ${kind}ter cannot be a generator.`, ), diff --git a/packages/babel-parser/src/parse-error/strict-mode-errors.js b/packages/babel-parser/src/parse-error/strict-mode-errors.js index 62bfe047acd2..05931ec07c14 100644 --- a/packages/babel-parser/src/parse-error/strict-mode-errors.js +++ b/packages/babel-parser/src/parse-error/strict-mode-errors.js @@ -1,8 +1,8 @@ // @flow -import { toParseErrorClass } from "../parse-error"; +import { toParseErrorCredentials } from "../parse-error"; -export default (_: typeof toParseErrorClass) => ({ +export default (_: typeof toParseErrorCredentials) => ({ StrictDelete: _("Deleting local variable in strict mode."), // `bindingName` is the StringValue[1] of an IdentifierReference[2], which is diff --git a/packages/babel-parser/src/parser/error-codes.js b/packages/babel-parser/src/parser/error-codes.js deleted file mode 100644 index 4e89302aae11..000000000000 --- a/packages/babel-parser/src/parser/error-codes.js +++ /dev/null @@ -1,8 +0,0 @@ -// @flow - -export const ErrorCodes = Object.freeze({ - SyntaxError: "BABEL_PARSER_SYNTAX_ERROR", - SourceTypeModuleError: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", -}); - -export type ErrorCode = $Values; diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 75f22641947d..b4862b6655e1 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -53,180 +53,177 @@ 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 = toParseErrorClasses( - _ => ({ - 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: _<{| reservedType: string |}>( - ({ reservedType }) => `Cannot overwrite reserved type ${reservedType}.`, - ), - DeclareClassElement: _( - "The `declare` modifier can only appear on class fields.", - ), - DeclareClassFieldInitializer: _( - "Initializers are not allowed in fields with the `declare` modifier.", - ), - DuplicateDeclareModuleExports: _( - "Duplicate `declare module.exports` statement.", - ), - EnumBooleanMemberNotInitialized: _<{| - memberName: string, - enumName: string, - |}>( - ({ memberName, enumName }) => - `Boolean enum members need to be initialized. Use either \`${memberName} = true,\` or \`${memberName} = false,\` in enum \`${enumName}\`.`, - ), - EnumDuplicateMemberName: _<{| memberName: string, enumName: string |}>( - ({ memberName, enumName }) => - `Enum member names need to be unique, but the name \`${memberName}\` has already been used before in enum \`${enumName}\`.`, - ), - EnumInconsistentMemberValues: _<{| enumName: string |}>( - ({ enumName }) => - `Enum \`${enumName}\` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers.`, - ), - EnumInvalidExplicitType: _<{| invalidEnumType: string, enumName: string |}>( - ({ invalidEnumType, enumName }) => - `Enum type \`${invalidEnumType}\` is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`, - ), - EnumInvalidExplicitTypeUnknownSupplied: _<{| enumName: string |}>( - ({ enumName }) => - `Supplied enum type is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`, - ), - EnumInvalidMemberInitializerPrimaryType: _<{| - enumName: string, - memberName: string, - explicitType: string, - |}>( - ({ enumName, memberName, explicitType }) => - `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of \`${memberName}\` needs to be a ${explicitType} literal.`, - ), - EnumInvalidMemberInitializerSymbolType: _<{| - enumName: string, - memberName: string, - explicitType: string, - |}>( - ({ enumName, memberName }) => - `Symbol enum members cannot be initialized. Use \`${memberName},\` in enum \`${enumName}\`.`, - ), - EnumInvalidMemberInitializerUnknownType: _<{| - enumName: string, - memberName: string, - // eslint-disable-next-line no-unused-vars - explicitType: string, - |}>( - ({ enumName, memberName }) => - `The enum member initializer for \`${memberName}\` needs to be a literal (either a boolean, number, or string) in enum \`${enumName}\`.`, - ), - EnumInvalidMemberName: _<{| - enumName: string, - memberName: string, - suggestion: string, - |}>( - ({ enumName, memberName, suggestion }) => - `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${memberName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`, - ), - EnumNumberMemberNotInitialized: _<{| - enumName: string, - memberName: string, - |}>( - ({ enumName, memberName }) => - `Number enum members need to be initialized, e.g. \`${memberName} = 1\` in enum \`${enumName}\`.`, - ), - EnumStringMemberInconsistentlyInitailized: _<{| enumName: string |}>( - ({ enumName }) => - `String enum members need to consistently either all use initializers, or use no initializers, in enum \`${enumName}\`.`, - ), - GetterMayNotHaveThisParam: _("A getter cannot have a `this` parameter."), - 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.", - ), - PatternIsOptional: _( - "A binding pattern parameter cannot be optional in an implementation signature.", - // For consistency in TypeScript and Flow error codes - !process.env.BABEL_8_BREAKING - ? { reasonCode: "OptionalBindingPattern" } - : {}, - ), - SetterMayNotHaveThisParam: _("A setter cannot have a `this` parameter."), - SpreadVariance: _("Spread properties cannot have variance."), - ThisParamAnnotationRequired: _( - "A type annotation is required for the `this` parameter.", - ), - ThisParamBannedInConstructor: _( - "Constructors cannot have a `this` parameter; constructors don't bind `this` like other functions.", - ), - ThisParamMayNotBeOptional: _("The `this` parameter cannot be optional."), - ThisParamMustBeFirst: _( - "The `this` parameter must be the first function parameter.", - ), - ThisParamNoDefault: _("The `this` parameter may not have a default value."), - 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: _<{| reservedType: string |}>( - ({ reservedType }) => `Unexpected reserved type ${reservedType}.`, - ), - UnexpectedReservedUnderscore: _( - "`_` is only allowed as a type argument to call or new.", - ), - 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.", - ), - UnexpectedTypeParameterBeforeAsyncArrowFunction: _( - "Type parameters must come after the async keyword, e.g. instead of ` async () => {}`, use `async () => {}`.", - ), - UnsupportedDeclareExportKind: _<{| - unsupported: string, - suggestion: string, - |}>( - ({ unsupported, suggestion }) => - `\`declare export ${unsupported}\` is not supported. Use \`${suggestion}\` instead.`, - ), - UnsupportedStatementInDeclareModule: _( - "Only declares and type imports are allowed inside declare module.", - ), - UnterminatedFlowComment: _("Unterminated flow-comment."), - }), - { syntaxPlugin: "flow" }, -); +const FlowErrors = toParseErrorClasses`flow`(_ => ({ + 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: _<{| reservedType: string |}>( + ({ reservedType }) => `Cannot overwrite reserved type ${reservedType}.`, + ), + DeclareClassElement: _( + "The `declare` modifier can only appear on class fields.", + ), + DeclareClassFieldInitializer: _( + "Initializers are not allowed in fields with the `declare` modifier.", + ), + DuplicateDeclareModuleExports: _( + "Duplicate `declare module.exports` statement.", + ), + EnumBooleanMemberNotInitialized: _<{| + memberName: string, + enumName: string, + |}>( + ({ memberName, enumName }) => + `Boolean enum members need to be initialized. Use either \`${memberName} = true,\` or \`${memberName} = false,\` in enum \`${enumName}\`.`, + ), + EnumDuplicateMemberName: _<{| memberName: string, enumName: string |}>( + ({ memberName, enumName }) => + `Enum member names need to be unique, but the name \`${memberName}\` has already been used before in enum \`${enumName}\`.`, + ), + EnumInconsistentMemberValues: _<{| enumName: string |}>( + ({ enumName }) => + `Enum \`${enumName}\` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers.`, + ), + EnumInvalidExplicitType: _<{| invalidEnumType: string, enumName: string |}>( + ({ invalidEnumType, enumName }) => + `Enum type \`${invalidEnumType}\` is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`, + ), + EnumInvalidExplicitTypeUnknownSupplied: _<{| enumName: string |}>( + ({ enumName }) => + `Supplied enum type is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`, + ), + EnumInvalidMemberInitializerPrimaryType: _<{| + enumName: string, + memberName: string, + explicitType: string, + |}>( + ({ enumName, memberName, explicitType }) => + `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of \`${memberName}\` needs to be a ${explicitType} literal.`, + ), + EnumInvalidMemberInitializerSymbolType: _<{| + enumName: string, + memberName: string, + explicitType: string, + |}>( + ({ enumName, memberName }) => + `Symbol enum members cannot be initialized. Use \`${memberName},\` in enum \`${enumName}\`.`, + ), + EnumInvalidMemberInitializerUnknownType: _<{| + enumName: string, + memberName: string, + // eslint-disable-next-line no-unused-vars + explicitType: string, + |}>( + ({ enumName, memberName }) => + `The enum member initializer for \`${memberName}\` needs to be a literal (either a boolean, number, or string) in enum \`${enumName}\`.`, + ), + EnumInvalidMemberName: _<{| + enumName: string, + memberName: string, + suggestion: string, + |}>( + ({ enumName, memberName, suggestion }) => + `Enum member names cannot start with lowercase 'a' through 'z'. Instead of using \`${memberName}\`, consider using \`${suggestion}\`, in enum \`${enumName}\`.`, + ), + EnumNumberMemberNotInitialized: _<{| + enumName: string, + memberName: string, + |}>( + ({ enumName, memberName }) => + `Number enum members need to be initialized, e.g. \`${memberName} = 1\` in enum \`${enumName}\`.`, + ), + EnumStringMemberInconsistentlyInitailized: _<{| enumName: string |}>( + ({ enumName }) => + `String enum members need to consistently either all use initializers, or use no initializers, in enum \`${enumName}\`.`, + ), + GetterMayNotHaveThisParam: _("A getter cannot have a `this` parameter."), + 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.", + ), + PatternIsOptional: _( + "A binding pattern parameter cannot be optional in an implementation signature.", + // For consistency in TypeScript and Flow error codes + !process.env.BABEL_8_BREAKING + ? { reasonCode: "OptionalBindingPattern" } + : {}, + ), + SetterMayNotHaveThisParam: _("A setter cannot have a `this` parameter."), + SpreadVariance: _("Spread properties cannot have variance."), + ThisParamAnnotationRequired: _( + "A type annotation is required for the `this` parameter.", + ), + ThisParamBannedInConstructor: _( + "Constructors cannot have a `this` parameter; constructors don't bind `this` like other functions.", + ), + ThisParamMayNotBeOptional: _("The `this` parameter cannot be optional."), + ThisParamMustBeFirst: _( + "The `this` parameter must be the first function parameter.", + ), + ThisParamNoDefault: _("The `this` parameter may not have a default value."), + 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: _<{| reservedType: string |}>( + ({ reservedType }) => `Unexpected reserved type ${reservedType}.`, + ), + UnexpectedReservedUnderscore: _( + "`_` is only allowed as a type argument to call or new.", + ), + 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.", + ), + UnexpectedTypeParameterBeforeAsyncArrowFunction: _( + "Type parameters must come after the async keyword, e.g. instead of ` async () => {}`, use `async () => {}`.", + ), + UnsupportedDeclareExportKind: _<{| + unsupported: string, + suggestion: string, + |}>( + ({ unsupported, suggestion }) => + `\`declare export ${unsupported}\` is not supported. Use \`${suggestion}\` 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 { diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index 892701db10be..6727e6ea7287 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -20,36 +20,33 @@ import { isNewLine } from "../../util/whitespace"; import { Errors, toParseErrorClasses } from "../../parse-error"; /* eslint sort-keys: "error" */ -const JsxErrors = toParseErrorClasses( - _ => ({ - AttributeIsEmpty: _( - "JSX attributes must only be assigned a non-empty expression.", - ), - MissingClosingTagElement: _<{| openingTagName: string |}>( - ({ openingTagName }) => - `Expected corresponding JSX closing tag for <${openingTagName}>.`, - ), - MissingClosingTagFragment: _( - "Expected corresponding JSX closing tag for <>.", - ), - UnexpectedSequenceExpression: _( - "Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?", - ), - // FIXME: Unify with Errors.UnexpectedToken - UnexpectedToken: _<{| found: string, HTMLEntity: string |}>( - ({ found, HTMLEntity }) => - `Unexpected token \`${found}\`. Did you mean \`${HTMLEntity}\` or \`{'${found}'}\`?`, - ), - 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 <>...?", - ), - }), - { syntaxPlugin: "jsx" }, -); +const JsxErrors = toParseErrorClasses`jsx`(_ => ({ + AttributeIsEmpty: _( + "JSX attributes must only be assigned a non-empty expression.", + ), + MissingClosingTagElement: _<{| openingTagName: string |}>( + ({ openingTagName }) => + `Expected corresponding JSX closing tag for <${openingTagName}>.`, + ), + MissingClosingTagFragment: _( + "Expected corresponding JSX closing tag for <>.", + ), + UnexpectedSequenceExpression: _( + "Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?", + ), + // FIXME: Unify with Errors.UnexpectedToken + UnexpectedToken: _<{| found: string, HTMLEntity: string |}>( + ({ found, HTMLEntity }) => + `Unexpected token \`${found}\`. Did you mean \`${HTMLEntity}\` or \`{'${found}'}\`?`, + ), + 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 <>...?", + ), +})); /* eslint-disable sort-keys */ diff --git a/packages/babel-parser/src/plugins/placeholders.js b/packages/babel-parser/src/plugins/placeholders.js index 5740957ef105..9859e4a96d37 100644 --- a/packages/babel-parser/src/plugins/placeholders.js +++ b/packages/babel-parser/src/plugins/placeholders.js @@ -47,13 +47,10 @@ type NodeOf = $Switch< type MaybePlaceholder = NodeOf; // | Placeholder /* eslint sort-keys: "error" */ -const PlaceholderErrors = toParseErrorClasses( - _ => ({ - ClassNameIsRequired: _("A class name is required."), - UnexpectedSpace: _("Unexpected space in placeholder."), - }), - { syntaxPlugin: "placeholders" }, -); +const PlaceholderErrors = toParseErrorClasses`placeholders`(_ => ({ + ClassNameIsRequired: _("A class name is required."), + UnexpectedSpace: _("Unexpected space in placeholder."), +})); /* eslint-disable sort-keys */ export default (superClass: Class): Class => diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 1f69976f2844..96c123400713 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -68,186 +68,180 @@ type ParsingContext = | "TypeParametersOrArguments"; /* eslint sort-keys: "error" */ -const TSErrors = toParseErrorClasses( - _ => ({ - AbstractMethodHasImplementation: _<{| methodName: string |}>( - ({ methodName }) => - `Method '${methodName}' cannot have an implementation because it is marked abstract.`, - ), - AbstractPropertyHasInitializer: _<{| propertyName: string |}>( - ({ propertyName }) => - `Property '${propertyName}' cannot have an initializer because it is marked abstract.`, - ), - AccesorCannotDeclareThisParameter: _( - "'get' and 'set' accessors cannot declare 'this' parameters.", - ), - AccesorCannotHaveTypeParameters: _( - "An accessor cannot have type parameters.", - ), - CannotFindName: _<{| name: string |}>( - ({ name }) => `Cannot find name '${name}'.`, - ), - ClassMethodHasDeclare: _( - "Class methods cannot have the 'declare' modifier.", - ), - ClassMethodHasReadonly: _( - "Class methods cannot have the 'readonly' modifier.", - ), - ConstInitiailizerMustBeStringOrNumericLiteralOrLiteralEnumReference: _( - "A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.", - ), - ConstructorHasTypeParameters: _( - "Type parameters cannot appear on a constructor declaration.", - ), - DeclareAccessor: _<{| kind: "get" | "set" |}>( - ({ kind }) => `'declare' is not allowed in ${kind}ters.`, - ), - DeclareClassFieldHasInitializer: _( - "Initializers are not allowed in ambient contexts.", - ), - DeclareFunctionHasImplementation: _( - "An implementation cannot be declared in ambient contexts.", - ), - DuplicateAccessibilityModifier: _<{| modifier: N.Accessibility |}>( - // `Accessibility modifier already seen: ${modifier}` would be more helpful. - // eslint-disable-next-line no-unused-vars - ({ modifier }) => `Accessibility modifier already seen.`, - ), - DuplicateModifier: _<{| modifier: TsModifier |}>( - ({ modifier }) => `Duplicate modifier: '${modifier}'.`, - ), - // `token` matches the terminology used by typescript: - // https://github.com/microsoft/TypeScript/blob/main/src/compiler/types.ts#L2915 - EmptyHeritageClauseType: _<{| token: "extends" | "implements" |}>( - ({ token }) => `'${token}' list cannot be empty.`, - ), - EmptyTypeArguments: _("Type argument list cannot be empty."), - EmptyTypeParameters: _("Type parameter list cannot be empty."), - ExpectedAmbientAfterExportDeclare: _( - "'export declare' must be followed by an ambient declaration.", - ), - ImportAliasHasImportType: _("An import alias can not use 'import type'."), - IncompatibleModifiers: _<{| modifiers: [TsModifier, TsModifier] |}>( - ({ modifiers }) => - `'${modifiers[0]}' modifier cannot be used with '${modifiers[1]}' modifier.`, - ), - IndexSignatureHasAbstract: _( - "Index signatures cannot have the 'abstract' modifier.", - ), - IndexSignatureHasAccessibility: _<{| modifier: N.Accessibility |}>( - ({ modifier }) => - `Index signatures cannot have an accessibility modifier ('${modifier}').`, - ), - IndexSignatureHasDeclare: _( - "Index signatures cannot have the 'declare' modifier.", - ), - IndexSignatureHasOverride: _( - "'override' modifier cannot appear on an index signature.", - ), - IndexSignatureHasStatic: _( - "Index signatures cannot have the 'static' modifier.", - ), - InitializerNotAllowedInAmbientContext: _( - "Initializers are not allowed in ambient contexts.", - ), - InvalidModifierOnTypeMember: _<{| modifier: TsModifier |}>( - ({ modifier }) => - `'${modifier}' modifier cannot appear on a type member.`, - ), - InvalidModifiersOrder: _<{| orderedModifiers: [TsModifier, TsModifier] |}>( - ({ orderedModifiers }) => - `'${orderedModifiers[0]}' modifier must precede '${orderedModifiers[1]}' modifier.`, - ), - InvalidTupleMemberLabel: _( - "Tuple members must be labeled with a simple identifier.", - ), - MissingInterfaceName: _( - "'interface' declarations must be followed by an identifier.", - ), - MixedLabeledAndUnlabeledElements: _( - "Tuple members must all have names or all not have names.", - ), - NonAbstractClassHasAbstractMethod: _( - "Abstract methods can only appear within an abstract class.", - ), - NonClassMethodPropertyHasAbstractModifer: _( - "'abstract' modifier can only appear on a class, method, or property declaration.", - ), - OptionalTypeBeforeRequired: _( - "A required element cannot follow an optional element.", - ), - OverrideNotInSubClass: _( - "This member cannot have an 'override' modifier because its containing class does not extend another class.", - ), - PatternIsOptional: _( - "A binding pattern parameter cannot be optional in an implementation signature.", - ), - PrivateElementHasAbstract: _( - "Private elements cannot have the 'abstract' modifier.", - ), - PrivateElementHasAccessibility: _<{| modifier: N.Accessibility |}>( - ({ modifier }) => - `Private elements cannot have an accessibility modifier ('${modifier}').`, - ), - ReadonlyForMethodSignature: _( - "'readonly' modifier can only appear on a property declaration or index signature.", - ), - ReservedArrowTypeParam: _( - "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `() => ...`.", - ), - ReservedTypeAssertion: _( - "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead.", - ), - SetAccesorCannotHaveOptionalParameter: _( - "A 'set' accessor cannot have an optional parameter.", - ), - SetAccesorCannotHaveRestParameter: _( - "A 'set' accessor cannot have rest parameter.", - ), - SetAccesorCannotHaveReturnType: _( - "A 'set' accessor cannot have a return type annotation.", - ), - SingleTypeParameterWithoutTrailingComma: _<{ name: string }>(({ name }) => - `Single type parameter ${name} should have a trailing comma. Example usage: <${name},>.` - ), - StaticBlockCannotHaveModifier: _( - "Static class blocks cannot have any modifier.", - ), - TypeAnnotationAfterAssign: _( - "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`.", - ), - TypeImportCannotSpecifyDefaultAndNamed: _( - "A type-only import can specify a default import or named bindings, but not both.", - ), - TypeModifierIsUsedInTypeExports: _( - "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.", - ), - TypeModifierIsUsedInTypeImports: _( - "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.", - ), - UnexpectedParameterModifier: _( - "A parameter property is only allowed in a constructor implementation.", - ), - 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: _<{| type: string |}>( - ({ type }) => - `Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got ${type}.`, - ), - }), - { syntaxPlugin: "typescript" }, -); +const TSErrors = toParseErrorClasses`typescript`(_ => ({ + AbstractMethodHasImplementation: _<{| methodName: string |}>( + ({ methodName }) => + `Method '${methodName}' cannot have an implementation because it is marked abstract.`, + ), + AbstractPropertyHasInitializer: _<{| propertyName: string |}>( + ({ propertyName }) => + `Property '${propertyName}' cannot have an initializer because it is marked abstract.`, + ), + AccesorCannotDeclareThisParameter: _( + "'get' and 'set' accessors cannot declare 'this' parameters.", + ), + AccesorCannotHaveTypeParameters: _( + "An accessor cannot have type parameters.", + ), + CannotFindName: _<{| name: string |}>( + ({ name }) => `Cannot find name '${name}'.`, + ), + ClassMethodHasDeclare: _("Class methods cannot have the 'declare' modifier."), + ClassMethodHasReadonly: _( + "Class methods cannot have the 'readonly' modifier.", + ), + ConstInitiailizerMustBeStringOrNumericLiteralOrLiteralEnumReference: _( + "A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.", + ), + ConstructorHasTypeParameters: _( + "Type parameters cannot appear on a constructor declaration.", + ), + DeclareAccessor: _<{| kind: "get" | "set" |}>( + ({ kind }) => `'declare' is not allowed in ${kind}ters.`, + ), + DeclareClassFieldHasInitializer: _( + "Initializers are not allowed in ambient contexts.", + ), + DeclareFunctionHasImplementation: _( + "An implementation cannot be declared in ambient contexts.", + ), + DuplicateAccessibilityModifier: _<{| modifier: N.Accessibility |}>( + // `Accessibility modifier already seen: ${modifier}` would be more helpful. + // eslint-disable-next-line no-unused-vars + ({ modifier }) => `Accessibility modifier already seen.`, + ), + DuplicateModifier: _<{| modifier: TsModifier |}>( + ({ modifier }) => `Duplicate modifier: '${modifier}'.`, + ), + // `token` matches the terminology used by typescript: + // https://github.com/microsoft/TypeScript/blob/main/src/compiler/types.ts#L2915 + EmptyHeritageClauseType: _<{| token: "extends" | "implements" |}>( + ({ token }) => `'${token}' list cannot be empty.`, + ), + EmptyTypeArguments: _("Type argument list cannot be empty."), + EmptyTypeParameters: _("Type parameter list cannot be empty."), + ExpectedAmbientAfterExportDeclare: _( + "'export declare' must be followed by an ambient declaration.", + ), + ImportAliasHasImportType: _("An import alias can not use 'import type'."), + IncompatibleModifiers: _<{| modifiers: [TsModifier, TsModifier] |}>( + ({ modifiers }) => + `'${modifiers[0]}' modifier cannot be used with '${modifiers[1]}' modifier.`, + ), + IndexSignatureHasAbstract: _( + "Index signatures cannot have the 'abstract' modifier.", + ), + IndexSignatureHasAccessibility: _<{| modifier: N.Accessibility |}>( + ({ modifier }) => + `Index signatures cannot have an accessibility modifier ('${modifier}').`, + ), + IndexSignatureHasDeclare: _( + "Index signatures cannot have the 'declare' modifier.", + ), + IndexSignatureHasOverride: _( + "'override' modifier cannot appear on an index signature.", + ), + IndexSignatureHasStatic: _( + "Index signatures cannot have the 'static' modifier.", + ), + InitializerNotAllowedInAmbientContext: _( + "Initializers are not allowed in ambient contexts.", + ), + InvalidModifierOnTypeMember: _<{| modifier: TsModifier |}>( + ({ modifier }) => `'${modifier}' modifier cannot appear on a type member.`, + ), + InvalidModifiersOrder: _<{| orderedModifiers: [TsModifier, TsModifier] |}>( + ({ orderedModifiers }) => + `'${orderedModifiers[0]}' modifier must precede '${orderedModifiers[1]}' modifier.`, + ), + InvalidTupleMemberLabel: _( + "Tuple members must be labeled with a simple identifier.", + ), + MissingInterfaceName: _( + "'interface' declarations must be followed by an identifier.", + ), + MixedLabeledAndUnlabeledElements: _( + "Tuple members must all have names or all not have names.", + ), + NonAbstractClassHasAbstractMethod: _( + "Abstract methods can only appear within an abstract class.", + ), + NonClassMethodPropertyHasAbstractModifer: _( + "'abstract' modifier can only appear on a class, method, or property declaration.", + ), + OptionalTypeBeforeRequired: _( + "A required element cannot follow an optional element.", + ), + OverrideNotInSubClass: _( + "This member cannot have an 'override' modifier because its containing class does not extend another class.", + ), + PatternIsOptional: _( + "A binding pattern parameter cannot be optional in an implementation signature.", + ), + PrivateElementHasAbstract: _( + "Private elements cannot have the 'abstract' modifier.", + ), + PrivateElementHasAccessibility: _<{| modifier: N.Accessibility |}>( + ({ modifier }) => + `Private elements cannot have an accessibility modifier ('${modifier}').`, + ), + SingleTypeParameterWithoutTrailingComma: _<{ name: string }>(({ name }) => + `Single type parameter ${name} should have a trailing comma. Example usage: <${name},>.` + ), + ReadonlyForMethodSignature: _( + "'readonly' modifier can only appear on a property declaration or index signature.", + ), + ReservedArrowTypeParam: _( + "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `() => ...`.", + ), + ReservedTypeAssertion: _( + "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead.", + ), + SetAccesorCannotHaveOptionalParameter: _( + "A 'set' accessor cannot have an optional parameter.", + ), + SetAccesorCannotHaveRestParameter: _( + "A 'set' accessor cannot have rest parameter.", + ), + SetAccesorCannotHaveReturnType: _( + "A 'set' accessor cannot have a return type annotation.", + ), + StaticBlockCannotHaveModifier: _( + "Static class blocks cannot have any modifier.", + ), + TypeAnnotationAfterAssign: _( + "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`.", + ), + TypeImportCannotSpecifyDefaultAndNamed: _( + "A type-only import can specify a default import or named bindings, but not both.", + ), + TypeModifierIsUsedInTypeExports: _( + "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.", + ), + TypeModifierIsUsedInTypeImports: _( + "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.", + ), + UnexpectedParameterModifier: _( + "A parameter property is only allowed in a constructor implementation.", + ), + 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: _<{| type: string |}>( + ({ type }) => + `Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got ${type}.`, + ), +})); /* eslint-disable sort-keys */ diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index bfd810a841b0..e2f6a05b01f2 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -1748,18 +1748,18 @@ export default class Tokenizer extends CommentsParser { * If `errorRecovery` is `true`, the error is pushed to the errors array and * returned. If `errorRecovery` is `false`, the error is instead thrown. * - * @param {Class>>} ParseErrorClass - * @param {RaiseProperties} raiseProperties - * @returns {(ParseError | empty)} + * @param {Class>>} ParseErrorClass + * @param {RaiseProperties} raiseProperties + * @returns {(ParseError | empty)} * @memberof Tokenizer */ - raise>>( + raise>>( ParseErrorClass: T, - raiseProperties: RaiseProperties, - ): ParseError { - const { at, ...rest } = raiseProperties; + raiseProperties: RaiseProperties, + ): ParseError { + const { at, ...details } = raiseProperties; const loc = at instanceof Position ? at : at.loc.start; - const error = new ParseErrorClass({ ...rest, loc }); + const error = new ParseErrorClass({ loc, details }); if (!this.options.errorRecovery) throw error; if (!this.isLookahead) this.state.errors.push(error); @@ -1773,16 +1773,16 @@ export default class Tokenizer extends CommentsParser { * already an error stored at the same `Position`, and replaces it with the * one generated here. * - * @param {Class>>} ParseErrorClass - * @param {RaiseProperties} raiseProperties - * @returns {(ParseError | empty)} + * @param {Class>>} ParseErrorClass + * @param {RaiseProperties} raiseProperties + * @returns {(ParseError | empty)} * @memberof Tokenizer */ - raiseOverwrite>>( + raiseOverwrite>>( ParseErrorClass: T, - raiseProperties: RaiseProperties, - ): ParseError | empty { - const { at, ...rest } = raiseProperties; + raiseProperties: RaiseProperties, + ): ParseError | empty { + const { at, ...details } = raiseProperties; const loc = at instanceof Position ? at : at.loc.start; const pos = loc.index; const errors = this.state.errors; @@ -1790,7 +1790,7 @@ export default class Tokenizer extends CommentsParser { for (let i = errors.length - 1; i >= 0; i--) { const error = errors[i]; if (error.pos === pos) { - return (errors[i] = new ParseErrorClass({ ...rest, loc })); + return (errors[i] = new ParseErrorClass({ loc, details })); } if (error.pos < pos) break; } From e87144fbeb1c849a516ddfc0b4bc7d2afc18152b Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 23 Feb 2022 08:26:19 -0800 Subject: [PATCH 059/104] Fix some names and add comments. Reviewed by @tolmasky. --- .../babel-parser/src/plugins/flow/index.js | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index b4862b6655e1..84b0c860d20f 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -60,6 +60,8 @@ const FlowErrors = toParseErrorClasses`flow`(_ => ({ 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.", ), + // TODO: When we get proper string enums in typescript make this ReservedType. + // Not really worth it to do the whole $Values dance with reservedTypes set. AssignReservedType: _<{| reservedType: string |}>( ({ reservedType }) => `Cannot overwrite reserved type ${reservedType}.`, ), @@ -95,18 +97,31 @@ const FlowErrors = toParseErrorClasses`flow`(_ => ({ ({ enumName }) => `Supplied enum type is not valid. Use one of \`boolean\`, \`number\`, \`string\`, or \`symbol\` in enum \`${enumName}\`.`, ), + + // TODO: When moving to typescript, we should either have each of the + // following errors only accept the specific strings they want: + // + // ...PrimaryType: explicitType: "string" | "number" | "boolean" + // ...SymbolType: explicitType: "symbol" + // ...UnknownType: explicitType: null + // + // Or, alternatively, merge these three errors together into one + // `EnumInvalidMemberInitializer` error that can accept `EnumExplicitType` + // without alteration, and then just have its message change based on the + // explicitType. EnumInvalidMemberInitializerPrimaryType: _<{| enumName: string, memberName: string, - explicitType: string, + explicitType: EnumExplicitType, |}>( ({ enumName, memberName, explicitType }) => + // $FlowIgnore (coercing null which never actually happens) `Enum \`${enumName}\` has type \`${explicitType}\`, so the initializer of \`${memberName}\` needs to be a ${explicitType} literal.`, ), EnumInvalidMemberInitializerSymbolType: _<{| enumName: string, memberName: string, - explicitType: string, + explicitType: EnumExplicitType, |}>( ({ enumName, memberName }) => `Symbol enum members cannot be initialized. Use \`${memberName},\` in enum \`${enumName}\`.`, @@ -114,8 +129,7 @@ const FlowErrors = toParseErrorClasses`flow`(_ => ({ EnumInvalidMemberInitializerUnknownType: _<{| enumName: string, memberName: string, - // eslint-disable-next-line no-unused-vars - explicitType: string, + explicitType: EnumExplicitType, |}>( ({ enumName, memberName }) => `The enum member initializer for \`${memberName}\` needs to be a literal (either a boolean, number, or string) in enum \`${enumName}\`.`, @@ -213,11 +227,11 @@ const FlowErrors = toParseErrorClasses`flow`(_ => ({ "Type parameters must come after the async keyword, e.g. instead of ` async () => {}`, use `async () => {}`.", ), UnsupportedDeclareExportKind: _<{| - unsupported: string, + unsupportedExportKind: string, suggestion: string, |}>( - ({ unsupported, suggestion }) => - `\`declare export ${unsupported}\` is not supported. Use \`${suggestion}\` instead.`, + ({ unsupportedExportKind, suggestion }) => + `\`declare export ${unsupportedExportKind}\` is not supported. Use \`${suggestion}\` instead.`, ), UnsupportedStatementInDeclareModule: _( "Only declares and type imports are allowed inside declare module.", @@ -579,7 +593,7 @@ export default (superClass: Class): Class => const label = this.state.value; throw this.raise(FlowErrors.UnsupportedDeclareExportKind, { at: this.state.startLoc, - unsupported: label, + unsupportedExportKind: label, suggestion: exportSuggestions[label], }); } @@ -3325,20 +3339,17 @@ export default (superClass: Class): Class => flowEnumErrorInvalidMemberInitializer( loc: Position, - { enumName, explicitType, memberName }: EnumContext, + enumContext: EnumContext, ) { return this.raise( - /^(boolean|number|string)$/.test(explicitType || "") - ? FlowErrors.EnumInvalidMemberInitializerPrimaryType - : explicitType === "symbol" + !enumContext.explicitType + ? FlowErrors.EnumInvalidMemberInitializerUnknownType + : enumContext.explicitType === "symbol" ? FlowErrors.EnumInvalidMemberInitializerSymbolType - : FlowErrors.EnumInvalidMemberInitializerUnknownType, + : FlowErrors.EnumInvalidMemberInitializerPrimaryType, { at: loc, - enumName, - memberName, - // FIXME: Handle this being missing better. - explicitType: explicitType || "unknown", + ...enumContext, }, ); } From 4de1abd3a3a92bad05f8b5f486b70c70e34059d0 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 23 Feb 2022 08:59:23 -0800 Subject: [PATCH 060/104] Get rid of unused option. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error/standard-errors.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.js index e4a52d4e7605..9aaa906dfe49 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.js @@ -391,8 +391,6 @@ const nodeTypeToDescription = type => ? "array destructuring pattern" : type === "ObjectPattern" ? "object destructuring pattern" - : type === "ImportDefaultSpecifier" - ? "default import specifier" : type .replace(/^[A-Z]+(?=[A-Z][a-z])/, "") .replace(/[a-z][A-Z]/, ([left, right]) => `${left} ${right}`) From 8357cb3355f4b8718f949992f1e1be21b7c2d2c6 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 23 Feb 2022 10:02:17 -0800 Subject: [PATCH 061/104] Better names. Reviewed by @tolmasky. --- .../src/parse-error/standard-errors.js | 26 +++++++++--------- packages/babel-parser/src/parser/statement.js | 27 +++++++++---------- .../babel-parser/src/plugins/flow/index.js | 2 +- .../babel-parser/src/plugins/jsx/index.js | 8 +++--- packages/babel-parser/src/tokenizer/index.js | 2 +- 5 files changed, 31 insertions(+), 34 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.js index 9aaa906dfe49..fa4eca317ce5 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.js @@ -58,17 +58,17 @@ export default (_: typeof toParseErrorCredentials) => ({ ), DuplicateConstructor: _("Duplicate constructor in the same class."), DuplicateDefaultExport: _("Only one default export allowed per module."), - DuplicateExport: _<{| exportedBinding: string |}>( - ({ exportedBinding }) => - `\`${exportedBinding}\` has already been exported. Exported identifiers must be unique.`, + DuplicateExport: _<{| exportName: string |}>( + ({ exportName }) => + `\`${exportName}\` 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."), - ExportBindingIsString: _<{| localBinding: string, exportedBinding: string |}>( - ({ localBinding, exportedBinding }) => - `A string literal cannot be used as an exported binding without \`from\`.\n- Did you mean \`export { '${localBinding}' as '${exportedBinding}' } from 'some-module'\`?`, + ExportBindingIsString: _<{| localName: string, exportName: string |}>( + ({ localName, exportName }) => + `A string literal cannot be used as an exported binding without \`from\`.\n- Did you mean \`export { '${localName}' as '${exportName}' } from 'some-module'\`?`, ), ExportDefaultFromAsIdentifier: _( "'from' is not allowed as an identifier after 'export default'.", @@ -94,9 +94,9 @@ export default (_: typeof toParseErrorCredentials) => ({ "Illegal 'use strict' directive in function with non-simple parameter list.", ), IllegalReturn: _("'return' outside of function."), - ImportBindingIsString: _<{| importedBinding: string |}>( - ({ importedBinding }) => - `A string literal cannot be used as an imported binding.\n- Did you mean \`import { "${importedBinding}" as foo }\`?`, + ImportBindingIsString: _<{| importName: string |}>( + ({ importName }) => + `A string literal cannot be used as an imported binding.\n- Did you mean \`import { "${importName}" as foo }\`?`, ), ImportCallArgumentTrailingComma: _( "Trailing comma is disallowed inside import(...) arguments.", @@ -139,8 +139,8 @@ export default (_: typeof toParseErrorCredentials) => ({ InvalidOrMissingExponent: _( "Floating-point numbers require a valid exponent after the 'e'.", ), - InvalidOrUnexpectedToken: _<{| found: string |}>( - ({ found }) => `Unexpected character '${found}'.`, + InvalidOrUnexpectedToken: _<{| unexpected: string |}>( + ({ unexpected }) => `Unexpected character '${unexpected}'.`, ), InvalidParenthesizedAssignment: _( "Invalid parenthesized assignment pattern.", @@ -199,8 +199,8 @@ export default (_: typeof toParseErrorCredentials) => ({ 16, )}'.`, ), - ModuleExportUndefined: _<{| exportedBinding: string |}>( - ({ exportedBinding }) => `Export '${exportedBinding}' is not defined.`, + ModuleExportUndefined: _<{| localName: string |}>( + ({ localName }) => `Export '${localName}' is not defined.`, ), MultipleDefaultsInSwitch: _("Multiple default clauses."), NewlineAfterThrow: _("Illegal newline after throw."), diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index b024554d0647..173ebe12781a 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -217,11 +217,8 @@ export default class StatementParser extends ExpressionParser { !this.options.allowUndeclaredExports && this.scope.undefinedExports.size > 0 ) { - for (const [name, loc] of Array.from(this.scope.undefinedExports)) { - this.raise(Errors.ModuleExportUndefined, { - at: loc, - exportedBinding: name, - }); + for (const [localName, at] of Array.from(this.scope.undefinedExports)) { + this.raise(Errors.ModuleExportUndefined, { at, localName }); } } return this.finishNode(program, "Program"); @@ -2242,17 +2239,17 @@ export default class StatementParser extends ExpressionParser { // Named exports for (const specifier of node.specifiers) { const { exported } = specifier; - const exportedName = + const exportName = exported.type === "Identifier" ? exported.name : exported.value; - this.checkDuplicateExports(specifier, exportedName); + this.checkDuplicateExports(specifier, exportName); // $FlowIgnore if (!isFrom && specifier.local) { const { local } = specifier; if (local.type !== "Identifier") { this.raise(Errors.ExportBindingIsString, { at: specifier, - localBinding: local.value, - exportedBinding: exportedName, + localName: local.value, + exportName, }); } else { // check for keywords used as local names @@ -2318,16 +2315,16 @@ export default class StatementParser extends ExpressionParser { | N.ExportNamedDeclaration | N.ExportSpecifier | N.ExportDefaultSpecifier, - name: string, + exportName: string, ): void { - if (this.exportedIdentifiers.has(name)) { - if (name === "default") { + if (this.exportedIdentifiers.has(exportName)) { + if (exportName === "default") { this.raise(Errors.DuplicateDefaultExport, { at: node }); } else { - this.raise(Errors.DuplicateExport, { at: node, exportedBinding: name }); + this.raise(Errors.DuplicateExport, { at: node, exportName }); } } - this.exportedIdentifiers.add(name); + this.exportedIdentifiers.add(exportName); } // Parses a comma-separated list of module exports. @@ -2656,7 +2653,7 @@ export default class StatementParser extends ExpressionParser { if (importedIsString) { throw this.raise(Errors.ImportBindingIsString, { at: specifier, - importedBinding: imported.value, + importName: imported.value, }); } this.checkReservedWord(imported.name, specifier.loc.start, true, true); diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 84b0c860d20f..83a77bcfc78c 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -2764,7 +2764,7 @@ export default (superClass: Class): Class => /*:: invariant(firstIdent instanceof N.StringLiteral) */ throw this.raise(Errors.ImportBindingIsString, { at: specifier, - importedBinding: firstIdent.value, + importName: firstIdent.value, }); } /*:: invariant(firstIdent instanceof N.Node) */ diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index 6727e6ea7287..739eb5e16e40 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -35,9 +35,9 @@ const JsxErrors = toParseErrorClasses`jsx`(_ => ({ "Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?", ), // FIXME: Unify with Errors.UnexpectedToken - UnexpectedToken: _<{| found: string, HTMLEntity: string |}>( - ({ found, HTMLEntity }) => - `Unexpected token \`${found}\`. Did you mean \`${HTMLEntity}\` or \`{'${found}'}\`?`, + UnexpectedToken: _<{| unexpected: string, HTMLEntity: string |}>( + ({ unexpected, HTMLEntity }) => + `Unexpected token \`${unexpected}\`. Did you mean \`${HTMLEntity}\` or \`{'${unexpected}'}\`?`, ), UnsupportedJsxValue: _( "JSX value should be either an expression or a quoted JSX text.", @@ -122,7 +122,7 @@ export default (superClass: Class): Class => if (process.env.BABEL_8_BREAKING) { this.raise(JsxErrors.UnexpectedToken, { at: this.state.curPosition(), - found: this.input[this.state.pos], + unexpected: this.input[this.state.pos], HTMLEntity: ch === charCodes.rightCurlyBrace ? "}" : ">", }); diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index e2f6a05b01f2..87fd1aa8987c 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -1068,7 +1068,7 @@ export default class Tokenizer extends CommentsParser { throw this.raise(Errors.InvalidOrUnexpectedToken, { at: this.state.curPosition(), - found: String.fromCodePoint(code), + unexpected: String.fromCodePoint(code), }); } From f81a1395f497db742be3dc5c29d156b4b728a49d Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 23 Feb 2022 10:42:47 -0800 Subject: [PATCH 062/104] Split out pipeline errors and choose better names. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 2 + .../parse-error/pipeline-operator-errors.js | 61 +++++++++++++++++++ .../src/parse-error/standard-errors.js | 46 -------------- .../babel-parser/src/parser/expression.js | 15 ++--- 4 files changed, 67 insertions(+), 57 deletions(-) create mode 100644 packages/babel-parser/src/parse-error/pipeline-operator-errors.js diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 7913fa6425c9..09877cd5b3b1 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -183,11 +183,13 @@ export type RaiseProperties = {| import ModuleErrors from "./parse-error/module-errors"; import StandardErrors from "./parse-error/standard-errors"; import StrictModeErrors from "./parse-error/strict-mode-errors"; +import PipelineOperatorErrors from "./parse-error/pipeline-operator-errors"; export const Errors = { ...toParseErrorClasses(ModuleErrors), ...toParseErrorClasses(StandardErrors), ...toParseErrorClasses(StrictModeErrors), + ...toParseErrorClasses`pipelineOperator`(PipelineOperatorErrors), }; export * from "./parse-error/credentials"; diff --git a/packages/babel-parser/src/parse-error/pipeline-operator-errors.js b/packages/babel-parser/src/parse-error/pipeline-operator-errors.js new file mode 100644 index 000000000000..8ba468be8fd4 --- /dev/null +++ b/packages/babel-parser/src/parse-error/pipeline-operator-errors.js @@ -0,0 +1,61 @@ +// @flow + +import { toParseErrorCredentials } from "../parse-error"; + +export const UnparenthesizedPipeBodyDescriptions = { + ArrowFunctionExpression: "arrow function", + AssignmentExpression: "assignment", + ConditionalExpression: "conditional", + YieldExpression: "yield", +}; + +type UnparenthesizedPipeBodyTypes = $Keys< + typeof UnparenthesizedPipeBodyDescriptions, +>; + +export default (_: typeof toParseErrorCredentials) => ({ + // This error is only used by the smart-mix proposal + PipeBodyIsTighter: _( + `Unexpected yield after pipeline body; any yield expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence.`, + ), + PipeTopicRequiresHackPipes: _( + 'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" or "smart" option.', + ), + PipeTopicUnbound: _( + "Topic reference is unbound; it must be inside a pipe body.", + ), + PipeTopicUnconfiguredToken: _<{| token: string |}>( + ({ token }) => + `Invalid topic token ${token}. In order to use ${token} as a topic reference, the pipelineOperator plugin must be configured with { "proposal": "hack", "topicToken": "${token}" }.`, + ), + PipeTopicUnused: _( + "Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once.", + ), + PipeUnparenthesizedBody: _<{| type: UnparenthesizedPipeBodyTypes |}>( + ({ type }) => + `Hack-style pipe body cannot be an unparenthesized ${UnparenthesizedPipeBodyDescriptions[type]} expression; please wrap it in parentheses.`, + ), + + // Messages whose codes start with “Pipeline” or “PrimaryTopic” + // are retained for backwards compatibility + // with the deprecated smart-mix pipe operator proposal plugin. + // They are subject to removal in a future major version. + 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: _( + 'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" or "smart" option.', + ), +}); diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.js index fa4eca317ce5..cd3d62992e09 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.js @@ -224,52 +224,6 @@ export default (_: typeof toParseErrorCredentials) => ({ ParamDupe: _("Argument name clash."), PatternHasAccessor: _("Object pattern can't contain getter or setter."), PatternHasMethod: _("Object pattern can't contain methods."), - // This error is only used by the smart-mix proposal - PipeBodyIsTighter: _<{| expressionDescription: string |}>( - ({ expressionDescription }) => - `Unexpected ${expressionDescription} after pipeline body; any ${expressionDescription} expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence.`, - ), - PipeTopicRequiresHackPipes: _( - 'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" or "smart" option.', - ), - PipeTopicUnbound: _( - "Topic reference is unbound; it must be inside a pipe body.", - ), - PipeTopicUnconfiguredToken: _<{| token: string |}>( - ({ token }) => - `Invalid topic token ${token}. In order to use ${token} as a topic reference, the pipelineOperator plugin must be configured with { "proposal": "hack", "topicToken": "${token}" }.`, - ), - PipeTopicUnused: _( - "Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once.", - ), - PipeUnparenthesizedBody: _<{| expressionDescription: string |}>( - ({ expressionDescription }) => - `Hack-style pipe body cannot be an unparenthesized ${expressionDescription} expression; please wrap it in parentheses.`, - ), - - // Messages whose codes start with “Pipeline” or “PrimaryTopic” - // are retained for backwards compatibility - // with the deprecated smart-mix pipe operator proposal plugin. - // They are subject to removal in a future major version. - 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: _( - 'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" or "smart" option.', - ), - PrivateInExpectedIn: _<{| name: string |}>( ({ name }) => `Private names are only allowed in property accesses (\`obj.#${name}\`) or in \`in\` expressions (\`#${name} in obj\`).`, diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 681c991d78a7..f2ddafd12d2b 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -68,6 +68,7 @@ import { newExpressionScope, } from "../util/expression-scope"; import { Errors, ParseError } from "../parse-error"; +import { UnparenthesizedPipeBodyDescriptions } from "../parse-error/pipeline-operator-errors"; import { setInnerComments } from "./comments"; import { cloneIdentifier } from "./node"; @@ -76,13 +77,6 @@ import type { SourceType } from "../options"; declare var invariant; */ -const invalidHackPipeBodies = new Map([ - ["ArrowFunctionExpression", "arrow function"], - ["AssignmentExpression", "assignment"], - ["ConditionalExpression", "conditional"], - ["YieldExpression", "yield"], -]); - export default class ExpressionParser extends LValParser { // Forward-declaration: defined in statement.js /*:: @@ -515,7 +509,6 @@ export default class ExpressionParser extends LValParser { if (this.prodParam.hasYield && this.isContextual(tt._yield)) { throw this.raise(Errors.PipeBodyIsTighter, { at: this.state.startLoc, - expressionDescription: this.state.value, }); } return this.parseSmartPipelineBodyInStyle( @@ -555,13 +548,13 @@ export default class ExpressionParser extends LValParser { parseHackPipeBody(): N.Expression { const { startLoc } = this.state; const body = this.parseMaybeAssign(); - const description = invalidHackPipeBodies.get(body.type); + const requiredParentheses = UnparenthesizedPipeBodyDescriptions[body.type]; // TODO: Check how to handle type casts in Flow and TS once they are supported - if (!!description && !body.extra?.parenthesized) { + if (requiredParentheses && !body.extra?.parenthesized) { this.raise(Errors.PipeUnparenthesizedBody, { at: startLoc, - expressionDescription: description, + type: body.type, }); } if (!this.topicReferenceWasUsedInCurrentContext()) { From d5522fd978ddee48286ab92b05df043acc62ca81 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Thu, 24 Feb 2022 16:30:41 -0800 Subject: [PATCH 063/104] Clean up LVal stuff. reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 2 + .../src/parse-error/standard-errors.js | 40 +++-- .../src/parse-error/to-node-description.js | 34 ++++ .../babel-parser/src/parser/expression.js | 47 ++--- packages/babel-parser/src/parser/lval.js | 163 +++++++++++------- packages/babel-parser/src/parser/statement.js | 28 +-- .../src/plugins/typescript/index.js | 23 +-- 7 files changed, 203 insertions(+), 134 deletions(-) create mode 100644 packages/babel-parser/src/parse-error/to-node-description.js diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 09877cd5b3b1..cb1a0386d5ee 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -192,4 +192,6 @@ export const Errors = { ...toParseErrorClasses`pipelineOperator`(PipelineOperatorErrors), }; +export type { LHSParent } from "./parse-error/standard-errors"; + export * from "./parse-error/credentials"; diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.js index cd3d62992e09..4d79aa50bb53 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.js @@ -1,6 +1,24 @@ // @flow import { toParseErrorCredentials } from "../parse-error"; +import toNodeDescription from "./to-node-description"; + +export type LHSParent = + | { type: "UpdateExpression", prefix: boolean } + | { + type: + | "ArrayPattern" + | "AssignmentExpression" + | "CatchClause" + | "ForOfStatement" + | "FormalParameters" + | "ForInStatement" + | "ForStatement" + | "Identfier" + | "ObjectPattern" + | "RestElement" + | "VariableDeclarator", + }; export default (_: typeof toParseErrorCredentials) => ({ AccessorIsGenerator: _<{| kind: "get" | "set" |}>( @@ -127,13 +145,12 @@ export default (_: typeof toParseErrorCredentials) => ({ InvalidIdentifier: _<{| identifier: string |}>( ({ identifier }) => `Invalid identifier ${identifier}.`, ), - InvalidLhs: _<{| inNodeType: string |}>( - ({ inNodeType }) => - `Invalid left-hand side in ${nodeTypeToDescription(inNodeType)}.`, + InvalidLhs: _<{| context: LHSParent |}>( + ({ context }) => `Invalid left-hand side in ${toNodeDescription(context)}.`, ), - InvalidLhsBinding: _<{| inNodeType: string |}>( - ({ inNodeType }) => - `Binding invalid left-hand side in ${nodeTypeToDescription(inNodeType)}.`, + InvalidLhsBinding: _<{| context: LHSParent |}>( + ({ context }) => + `Binding invalid left-hand side in ${toNodeDescription(context)}.`, ), InvalidNumber: _("Invalid number."), InvalidOrMissingExponent: _( @@ -338,14 +355,3 @@ export default (_: typeof toParseErrorCredentials) => ({ "Numeric separator can not be used after leading 0.", ), }); - -// eslint-disable-next-line no-confusing-arrow -const nodeTypeToDescription = type => - type === "ArrayPattern" - ? "array destructuring pattern" - : type === "ObjectPattern" - ? "object destructuring pattern" - : type - .replace(/^[A-Z]+(?=[A-Z][a-z])/, "") - .replace(/[a-z][A-Z]/, ([left, right]) => `${left} ${right}`) - .toLowerCase(); diff --git a/packages/babel-parser/src/parse-error/to-node-description.js b/packages/babel-parser/src/parse-error/to-node-description.js new file mode 100644 index 000000000000..b8708a69e6a7 --- /dev/null +++ b/packages/babel-parser/src/parse-error/to-node-description.js @@ -0,0 +1,34 @@ +const NodeDescriptions = { + ArrayPattern: "array destructuring pattern", + AssignmentExpression: "assignment expression", + AssignmentPattern: "assignment expression", + ArrowFunctionExpression: "arrow function expression", + ConditionalExpression: "conditional expression", + ForOfStatement: "for-of statement", + ForInStatement: "for-in statement", + ForStatement: "for-loop", + FormalParameters: "function parameter list", + Identifier: "identifier", + ObjectPattern: "object destructuring pattern", + ParenthesizedExpression: "parenthesized expression", + RestElement: "rest element", + UpdateExpression: { + true: "prefix operation", + false: "postfix operation", + }, + VariableDeclarator: "variable declaration", + YieldExpression: "yield expression", +}; + +type NodeTypesWithDescriptions = $Keys; +type NodeWithDescription = + | { type: "UpdateExpression", prefix: boolean } + | { type: NodeTypesWithDescriptions }; + +// eslint-disable-next-line no-confusing-arrow +const toNodeDescription = ({ type, prefix = false }: NodeWithDescription) => + type === "UpdateExpression" + ? NodeDescriptions.UpdateExpression[String(prefix)] + : NodeDescriptions[type]; + +export default toNodeDescription; diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index f2ddafd12d2b..f199266261e9 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -328,10 +328,12 @@ export default class ExpressionParser extends LValParser { node.left = left; } - this.checkLVal(left, "AssignmentExpression"); this.next(); node.right = this.parseMaybeAssign(); - return this.finishNode(node, "AssignmentExpression"); + this.checkLVal(left, { + in: this.finishNode(node, "AssignmentExpression"), + }); + return node; } else if (ownExpressionErrors) { this.checkExpressionErrors(refExpressionErrors, true); } @@ -644,8 +646,10 @@ export default class ExpressionParser extends LValParser { refExpressionErrors: ?ExpressionErrors, ): N.Expression { if (update) { - this.checkLVal(node.argument, "prefix operation"); - return this.finishNode(node, "UpdateExpression"); + this.checkLVal(node.argument, { + in: (node = this.finishNode(node, "UpdateExpression")), + }); + return node; } const startPos = this.state.start; @@ -657,9 +661,10 @@ export default class ExpressionParser extends LValParser { node.operator = this.state.value; node.prefix = false; node.argument = expr; - this.checkLVal(expr, "postfix operation"); this.next(); - expr = this.finishNode(node, "UpdateExpression"); + this.checkLVal(expr, { + in: (expr = this.finishNode(node, "UpdateExpression")), + }); } return expr; } @@ -2476,14 +2481,7 @@ export default class ExpressionParser extends LValParser { // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval' if (this.state.strict && node.id) { - this.checkLVal( - node.id, - "function name", - BIND_OUTSIDE, - undefined, - undefined, - strictModeChanged, - ); + this.checkIdentifier(node.id, BIND_OUTSIDE, strictModeChanged); } }, ); @@ -2513,16 +2511,21 @@ export default class ExpressionParser extends LValParser { isArrowFunction: ?boolean, strictModeChanged?: boolean = true, ): void { - const checkClashes = new Set(); + const checkClashes = !allowDuplicates && new Set(); + // We create a fake node with the "ephemeral" type `FormalParameters`[1] + // since we just store an array of parameters. Perhaps someday we can have + // something like class FormalParameters extends Array { ... }, which would + // also be helpful when traversing this node. + // + // 1. https://tc39.es/ecma262/#prod-FormalParameters + const formalParameters = { type: "FormalParameters" }; for (const param of node.params) { - this.checkLVal( - param, - "function parameter list", - BIND_VAR, - allowDuplicates ? null : checkClashes, - undefined, + this.checkLVal(param, { + in: formalParameters, + binding: BIND_VAR, + checkClashes, strictModeChanged, - ); + }); } } diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index e23e60d00eb6..2c8d6e25117c 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -7,6 +7,7 @@ import type { TSParameterProperty, Decorator, Expression, + Identifier, Node, Pattern, RestElement, @@ -15,7 +16,6 @@ import type { /*:: ClassMember, */ ObjectMember, /*:: TsNamedTypeElementBase, */ - Identifier, /*:: PrivateName, */ /*:: ObjectExpression, */ /*:: ObjectPattern, */ @@ -26,9 +26,13 @@ import { isStrictBindReservedWord, } from "../util/identifier"; import { NodeUtils } from "./node"; -import { type BindingTypes, BIND_NONE } from "../util/scopeflags"; +import { + type BindingTypes, + BIND_NONE, + BIND_SCOPE_LEXICAL, +} from "../util/scopeflags"; import { ExpressionErrors } from "./util"; -import { Errors } from "../parse-error"; +import { Errors, type LHSParent } from "../parse-error"; const unwrapParenthesizedExpression = (node: Node): Node => { return node.type === "ParenthesizedExpression" @@ -522,110 +526,135 @@ export default class LValParser extends NodeUtils { */ checkLVal( - expr: Expression | ObjectMember | RestElement, - inNodeType: string = expr.type, - bindingType: BindingTypes = BIND_NONE, - checkClashes: ?Set, - disallowLetBinding?: boolean, - strictModeChanged?: boolean = false, - parentIsParenthesizedExpression?: boolean = false, + expression: Expression | ObjectMember | RestElement, + { + in: ancestor, + binding = BIND_NONE, + checkClashes = false, + strictModeChanged = false, + allowingSloppyLetBinding = !(binding & BIND_SCOPE_LEXICAL), + parentIsParenthesizedExpression = false, + }: { + in: LHSParent, + binding?: BindingTypes, + checkClashes?: Set | false, + strictModeChanged?: boolean, + allowingSloppyLetBinding?: boolean, + parentIsParenthesizedExpression?: boolean, + }, ): void { - const type = expr.type; + const type = expression.type; // If we find here an ObjectMethod, it's because this was originally // an ObjectExpression which has then been converted. // toAssignable already reported this error with a nicer message. - if (this.isObjectMethod(expr)) return; + if (this.isObjectMethod(expression)) return; if (type === "MemberExpression") { - if (bindingType !== BIND_NONE) { - this.raise(Errors.InvalidPropertyBindingPattern, { at: expr }); + if (binding !== BIND_NONE) { + this.raise(Errors.InvalidPropertyBindingPattern, { at: expression }); } return; } - if (expr.type === "Identifier") { - const { name } = expr; - if ( - this.state.strict && - // "Global" reserved words have already been checked by parseIdentifier, - // unless they have been found in the id or parameters of a strict-mode - // function in a sloppy context. - (strictModeChanged - ? isStrictBindReservedWord(name, this.inModule) - : isStrictBindOnlyReservedWord(name)) - ) { - const at = expr; - - if (bindingType === BIND_NONE) { - this.raise(Errors.StrictEvalArguments, { at, referenceName: name }); - } else { - this.raise(Errors.StrictEvalArgumentsBinding, { - at, - bindingName: name, - }); - } - } + if (expression.type === "Identifier") { + this.checkIdentifier( + expression, + binding, + strictModeChanged, + allowingSloppyLetBinding, + ); + + const { name } = expression; if (checkClashes) { if (checkClashes.has(name)) { - this.raise(Errors.ParamDupe, { at: expr }); + this.raise(Errors.ParamDupe, { at: expression }); } else { checkClashes.add(name); } } - if (disallowLetBinding && name === "let") { - this.raise(Errors.LetInLexicalBinding, { at: expr }); - } - if (!(bindingType & BIND_NONE)) { - this.declareNameFromIdentifier((expr: Identifier), bindingType); - } + return; } const validity = this.isValidLVal( - expr.type, - parentIsParenthesizedExpression || expr.extra?.parenthesized, - bindingType, + expression.type, + parentIsParenthesizedExpression || expression.extra?.parenthesized, + binding, ); if (validity === true) return; if (validity === false) { - this.raise( - bindingType === BIND_NONE - ? Errors.InvalidLhs - : Errors.InvalidLhsBinding, - { at: expr, inNodeType }, - ); + const ParseErrorClass = + binding === BIND_NONE ? Errors.InvalidLhs : Errors.InvalidLhsBinding; + + this.raise(ParseErrorClass, { + at: expression, + context: + ancestor.type === "UpdateExpression" + ? { type: "UpdateExpression", prefix: ancestor.prefix } + : { type: ancestor.type }, + }); return; } const isParenthesizedExpression = type === "ParenthesizedExpression" || Array.isArray(validity); const key = Array.isArray(validity) ? validity[1] : validity; - const relevantType = - type === "ArrayPattern" || - type === "ObjectPattern" || - type === "ParenthesizedExpression" - ? type - : inNodeType; - - for (const child of [].concat(expr[key])) { + const nextAncestor = + expression.type === "ArrayPattern" || + expression.type === "ObjectPattern" || + expression.type === "ParenthesizedExpression" + ? expression + : ancestor; + + for (const child of [].concat(expression[key])) { if (child) { - this.checkLVal( - child, - relevantType, - bindingType, + this.checkLVal(child, { + in: nextAncestor, + binding, checkClashes, - disallowLetBinding, + allowingSloppyLetBinding, strictModeChanged, - isParenthesizedExpression, - ); + parentIsParenthesizedExpression: isParenthesizedExpression, + }); } } } + checkIdentifier( + at: Identifier, + bindingType: BindingTypes, + strictModeChanged: boolean = false, + allowLetBinding: boolean = !(bindingType & BIND_SCOPE_LEXICAL), + ) { + if ( + this.state.strict && + (strictModeChanged + ? isStrictBindReservedWord(at.name, this.inModule) + : isStrictBindOnlyReservedWord(at.name)) + ) { + if (bindingType === BIND_NONE) { + this.raise(Errors.StrictEvalArguments, { at, referenceName: at.name }); + } else { + this.raise(Errors.StrictEvalArgumentsBinding, { + at, + bindingName: at.name, + }); + } + } + + if (!allowLetBinding && at.name === "let") { + this.raise(Errors.LetInLexicalBinding, { at }); + } + + if (!(bindingType & BIND_NONE)) { + this.declareNameFromIdentifier(at, bindingType); + } + } + declareNameFromIdentifier(identifier: Identifier, binding: BindingTypes) { this.scope.declareName(identifier.name, binding, identifier.loc.start); } diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 173ebe12781a..bb8163f9b643 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -725,8 +725,8 @@ export default class StatementParser extends ExpressionParser { if (isForOf || this.match(tt._in)) { this.checkDestructuringPrivate(refExpressionErrors); this.toAssignable(init, /* isLHS */ true); - const description = isForOf ? "for-of statement" : "for-in statement"; - this.checkLVal(init, description); + const type = isForOf ? "ForOfStatement" : "ForInStatement"; + this.checkLVal(init, { in: { type } }); return this.parseForIn(node, init, awaitAt); } else { this.checkExpressionErrors(refExpressionErrors, true); @@ -841,7 +841,11 @@ export default class StatementParser extends ExpressionParser { const simple = param.type === "Identifier"; this.scope.enter(simple ? SCOPE_SIMPLE_CATCH : 0); - this.checkLVal(param, "catch clause", BIND_LEXICAL); + this.checkLVal(param, { + in: { type: "CatchClause" }, + binding: BIND_LEXICAL, + allowingSloppyLetBinding: true, + }); return param; } @@ -1171,7 +1175,7 @@ export default class StatementParser extends ExpressionParser { if (init.type === "AssignmentPattern") { this.raise(Errors.InvalidLhs, { at: init, - inNodeType: "for-loop", + context: { type: "ForStatement" }, }); } @@ -1243,13 +1247,10 @@ export default class StatementParser extends ExpressionParser { parseVarId(decl: N.VariableDeclarator, kind: "var" | "let" | "const"): void { decl.id = this.parseBindingAtom(); - this.checkLVal( - decl.id, - "variable declaration", - kind === "var" ? BIND_VAR : BIND_LEXICAL, - undefined, - kind !== "var", - ); + this.checkLVal(decl.id, { + in: { type: "VariableDeclarator" }, + binding: kind === "var" ? BIND_VAR : BIND_LEXICAL, + }); } // Parse a function declaration or literal (depending on the @@ -2457,7 +2458,10 @@ export default class StatementParser extends ExpressionParser { } finishImportSpecifier(specifier: N.Node, type: string) { - this.checkLVal(specifier.local, type, BIND_LEXICAL); + this.checkLVal(specifier.local, { + in: specifier, + binding: BIND_LEXICAL, + }); return this.finishNode(specifier, type); } diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 96c123400713..cb499f2234fa 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -1620,11 +1620,7 @@ export default (superClass: Class): Class => if (properties.declare) node.declare = properties.declare; if (tokenIsIdentifier(this.state.type)) { node.id = this.parseIdentifier(); - this.checkLVal( - node.id, - "typescript interface declaration", - BIND_TS_INTERFACE, - ); + this.checkIdentifier(node.id, BIND_TS_INTERFACE); } else { node.id = null; this.raise(TSErrors.MissingInterfaceName, { at: this.state.startLoc }); @@ -1644,7 +1640,7 @@ export default (superClass: Class): Class => node: N.TsTypeAliasDeclaration, ): N.TsTypeAliasDeclaration { node.id = this.parseIdentifier(); - this.checkLVal(node.id, "typescript type alias", BIND_TS_TYPE); + this.checkIdentifier(node.id, BIND_TS_TYPE); node.typeParameters = this.tsTryParseTypeParameters(); node.typeAnnotation = this.tsInType(() => { @@ -1730,9 +1726,8 @@ export default (superClass: Class): Class => if (properties.declare) node.declare = properties.declare; this.expectContextual(tt._enum); node.id = this.parseIdentifier(); - this.checkLVal( + this.checkIdentifier( node.id, - "typescript enum declaration", node.const ? BIND_TS_CONST_ENUM : BIND_TS_ENUM, ); @@ -1768,11 +1763,7 @@ export default (superClass: Class): Class => node.id = this.parseIdentifier(); if (!nested) { - this.checkLVal( - node.id, - "module or namespace declaration", - BIND_TS_NAMESPACE, - ); + this.checkIdentifier(node.id, BIND_TS_NAMESPACE); } if (this.eat(tt.dot)) { @@ -1819,7 +1810,7 @@ export default (superClass: Class): Class => ): N.TsImportEqualsDeclaration { node.isExport = isExport || false; node.id = this.parseIdentifier(); - this.checkLVal(node.id, "import equals declaration", BIND_LEXICAL); + this.checkIdentifier(node.id, BIND_LEXICAL); this.expect(tt.eq); const moduleReference = this.tsParseModuleReference(); if ( @@ -2242,7 +2233,7 @@ export default (superClass: Class): Class => if (!node.body && node.id) { // Function ids are validated after parsing their body. // For bodyless function, we need to do it here. - this.checkLVal(node.id, "function name", BIND_TS_AMBIENT); + this.checkIdentifier(node.id, BIND_TS_AMBIENT); } else { super.registerFunctionStatementId(...arguments); } @@ -3757,7 +3748,7 @@ export default (superClass: Class): Class => node[rightOfAsKey] = cloneIdentifier(node[leftOfAsKey]); } if (isImport) { - this.checkLVal(node[rightOfAsKey], "ImportSpecifier", BIND_LEXICAL); + this.checkIdentifier(node[rightOfAsKey], BIND_LEXICAL); } } }; From 58a7cf86e64e21fcad434cf4b704587ebe48a69b Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 25 Feb 2022 08:43:48 -0800 Subject: [PATCH 064/104] Use "unambiguous" to determine whether to parse typescript tests as module or script, and stop always allowing imports in Typescript. Reviewed by @tolmasky. --- packages/babel-parser/src/plugins/typescript/index.js | 7 ------- scripts/parser-tests/typescript/error-codes.js | 1 + scripts/parser-tests/typescript/index.js | 4 ++-- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index cb499f2234fa..767acf68e4fa 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -288,13 +288,6 @@ export default (superClass: Class): Class => return TypeScriptScopeHandler; } - // import and export are always allowed in TypeScript, regardless of whether - // you are parsing as "script" or "module". However, this is different than - // `allowImportExportEverywhere`, as it is only allowed in the same places - // that it would be allowed in a module context, not *anywhere*. - // eslint-disable-next-line no-unused-vars - assertModuleNodeAllowed(node: N.Node): void {} - tsIsIdentifier(): boolean { // TODO: actually a bit more complex in TypeScript, but shouldn't matter. // See https://github.com/Microsoft/TypeScript/issues/15008 diff --git a/scripts/parser-tests/typescript/error-codes.js b/scripts/parser-tests/typescript/error-codes.js index 945f79c78950..42631c593601 100644 --- a/scripts/parser-tests/typescript/error-codes.js +++ b/scripts/parser-tests/typescript/error-codes.js @@ -73,6 +73,7 @@ export default [ "TS1385", // Function type notation must be parenthesized when used in a union type. "TS1386", // Constructor type notation must be parenthesized when used in a union type. "TS1437", // Namespace must be given a name. + "TS1194", // Export declarations are not permitted in a namespace. // "TS2300", // Duplicate identifier '{0}'. "TS2337", // Super calls are not permitted outside constructors or in nested functions inside constructors. // "TS2340", // Only public and protected methods of the base class are accessible via the 'super' keyword. diff --git a/scripts/parser-tests/typescript/index.js b/scripts/parser-tests/typescript/index.js index 8943af1c8c3e..ad433afb0418 100644 --- a/scripts/parser-tests/typescript/index.js +++ b/scripts/parser-tests/typescript/index.js @@ -77,7 +77,7 @@ const runner = new TestRunner({ continue; } - const strictMode = AlwaysStrictRegExp.test(test.contents); + const strictMode = AlwaysStrictRegExp.test(test.contents) || void 0; const files = toFiles(strictMode, test.contents, test.name); const expectedError = files.length > 0 && baselineContainsParserErrorCodes(test.name); @@ -101,7 +101,7 @@ function toFiles(strictMode, contents, name) { .map(([sourceFilename, contents]) => ({ contents, sourceFilename, - sourceType: "script", + sourceType: "unambiguous", strictMode, plugins: /\.(t|j)sx$/.test(sourceFilename) ? pluginsWithJSX : plugins, })); From 852b89dae8455361e80e28f485e31b859318dfda Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 26 Feb 2022 12:14:04 -0800 Subject: [PATCH 065/104] Revert test to previous state. Reviewed by @tolmasky. --- .../test/fixtures/node-extensions/import-in-cts/options.json | 3 +++ .../test/fixtures/node-extensions/import-in-cts/output.js | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/options.json delete mode 100644 packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/output.js diff --git a/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/options.json b/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/options.json new file mode 100644 index 000000000000..afbed324e92a --- /dev/null +++ b/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/options.json @@ -0,0 +1,3 @@ +{ + "throws": "'import' and 'export' may appear only with 'sourceType: \"module\"' (1:0)" +} diff --git a/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/output.js b/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/output.js deleted file mode 100644 index 856f26b34c30..000000000000 --- a/packages/babel-preset-typescript/test/fixtures/node-extensions/import-in-cts/output.js +++ /dev/null @@ -1 +0,0 @@ -import "x"; From 7da1432cdc2efd8585bac01c1d8e69e22917ad13 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 26 Feb 2022 12:23:50 -0800 Subject: [PATCH 066/104] Better name for this. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/lval.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 2c8d6e25117c..c4836ebadfb2 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -533,14 +533,14 @@ export default class LValParser extends NodeUtils { checkClashes = false, strictModeChanged = false, allowingSloppyLetBinding = !(binding & BIND_SCOPE_LEXICAL), - parentIsParenthesizedExpression = false, + hasParenthesizedAncestor = false, }: { in: LHSParent, binding?: BindingTypes, checkClashes?: Set | false, strictModeChanged?: boolean, allowingSloppyLetBinding?: boolean, - parentIsParenthesizedExpression?: boolean, + hasParenthesizedAncestor?: boolean, }, ): void { const type = expression.type; @@ -580,7 +580,7 @@ export default class LValParser extends NodeUtils { const validity = this.isValidLVal( expression.type, - parentIsParenthesizedExpression || expression.extra?.parenthesized, + hasParenthesizedAncestor || expression.extra?.parenthesized, binding, ); @@ -618,7 +618,7 @@ export default class LValParser extends NodeUtils { checkClashes, allowingSloppyLetBinding, strictModeChanged, - parentIsParenthesizedExpression: isParenthesizedExpression, + hasParenthesizedAncestor: isParenthesizedExpression, }); } } From 3cfd9b1a63012bde0d8fb64ec3665dbe9153efcf Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 26 Feb 2022 13:15:48 -0800 Subject: [PATCH 067/104] Add these back in. Reviewed by @tolmasky. --- scripts/parser-tests/typescript/allowlist.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/parser-tests/typescript/allowlist.txt b/scripts/parser-tests/typescript/allowlist.txt index b8c246d681bf..d99d25621cc1 100644 --- a/scripts/parser-tests/typescript/allowlist.txt +++ b/scripts/parser-tests/typescript/allowlist.txt @@ -50,7 +50,6 @@ duplicateIdentifierEnum.ts duplicateIdentifierInCatchBlock.ts duplicateLabel1.ts duplicateLabel2.ts -duplicatePackage.ts duplicatePackage_withErrors.ts duplicateVarAndImport.ts duplicateVarAndImport2.ts @@ -72,6 +71,12 @@ exportAssignmentWithDeclareAndExportModifiers.ts exportAssignmentWithDeclareModifier.ts exportAssignmentWithExportModifier.ts exportClassWithoutName.ts +exportDeclarationsInAmbientNamespaces.ts +exportDefaultAsyncFunction2.ts +exportEqualsOfModule.ts +exportSameNameFuncVar.ts +exportSpecifierAndExportedMemberDeclaration.ts +exportSpecifierAndLocalMemberDeclaration.ts expressionsForbiddenInParameterInitializers.ts extendsClauseAlreadySeen.ts extendsClauseAlreadySeen2.ts @@ -122,14 +127,19 @@ multipleClassPropertyModifiersErrors.ts multipleInheritance.ts nameCollisions.ts noImplicitAnyDestructuringVarDeclaration.ts +nonMergedOverloads.ts parameterInitializerBeforeDestructuringEmit.ts parameterPropertyOutsideConstructor.ts parserConstructorDeclaration12.ts readonlyInNonPropertyParameters.ts redeclareParameterInCatchBlock.ts +reExportGlobalDeclaration1.ts +reExportUndefined1.ts restParamModifier2.ts +shadowedReservedCompilerDeclarationsWithNoEmit.ts sourceMap-LineBreaks.ts sourceMapValidationDecorators.ts +stackDepthLimitCastingType.ts staticAsIdentifier.ts staticModifierAlreadySeen.ts strictOptionalProperties1.ts From 71c13fb2291c3fbdb52ff93b84175ff83650e7aa Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 26 Feb 2022 13:36:39 -0800 Subject: [PATCH 068/104] Add these. Reviewed by @tolmasky. --- scripts/parser-tests/typescript/allowlist.txt | 12 ++++++++++++ scripts/parser-tests/typescript/index.js | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/scripts/parser-tests/typescript/allowlist.txt b/scripts/parser-tests/typescript/allowlist.txt index d99d25621cc1..624e022dc382 100644 --- a/scripts/parser-tests/typescript/allowlist.txt +++ b/scripts/parser-tests/typescript/allowlist.txt @@ -72,11 +72,16 @@ exportAssignmentWithDeclareModifier.ts exportAssignmentWithExportModifier.ts exportClassWithoutName.ts exportDeclarationsInAmbientNamespaces.ts +exportDeclarationsInAmbientNamespaces2.ts exportDefaultAsyncFunction2.ts exportEqualsOfModule.ts exportSameNameFuncVar.ts exportSpecifierAndExportedMemberDeclaration.ts exportSpecifierAndLocalMemberDeclaration.ts +# We handle this fine, but it doesn't consider the different files together +exportSpecifierForAGlobal.ts +# We handle this fine, but it doesn't consider the different files together +exportSpecifierReferencingOuterDeclaration2.ts expressionsForbiddenInParameterInitializers.ts extendsClauseAlreadySeen.ts extendsClauseAlreadySeen2.ts @@ -125,6 +130,7 @@ moduleSharesNameWithImportDeclarationInsideIt3.ts moduleSharesNameWithImportDeclarationInsideIt5.ts multipleClassPropertyModifiersErrors.ts multipleInheritance.ts +multipleExports.ts nameCollisions.ts noImplicitAnyDestructuringVarDeclaration.ts nonMergedOverloads.ts @@ -134,6 +140,12 @@ parserConstructorDeclaration12.ts readonlyInNonPropertyParameters.ts redeclareParameterInCatchBlock.ts reExportGlobalDeclaration1.ts +# We handle this fine, but it doesn't consider the different files together +reExportGlobalDeclaration2.ts +# We handle this fine, but it doesn't consider the different files together +reExportGlobalDeclaration3.ts +# We handle this fine, but it doesn't consider the different files together +reExportGlobalDeclaration4.ts reExportUndefined1.ts restParamModifier2.ts shadowedReservedCompilerDeclarationsWithNoEmit.ts diff --git a/scripts/parser-tests/typescript/index.js b/scripts/parser-tests/typescript/index.js index ad433afb0418..fdb2468be825 100644 --- a/scripts/parser-tests/typescript/index.js +++ b/scripts/parser-tests/typescript/index.js @@ -140,7 +140,10 @@ function splitTwoslashCodeInfoFiles(code, defaultFileName, root = "") { // ["index.ts", []] // ["index.ts", [""]] const nameContent = fileMap.filter( - n => n[1].length > 0 && (n[1].length > 1 || n[1][0] !== "") + n => + n[1].length > 2 || + (n[1].length === 1 && n[1][0] !== "") || + (n[1].length === 2 && n[1][0] !== "content not parsed" && n[1][0] !== "") ); return nameContent; } From e212e22fce7474d37964daef3380ed209c843d7e Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 27 Feb 2022 16:14:35 -0800 Subject: [PATCH 069/104] Fix flow errors and use toNodeDescription in more places. Reviewed by @tolmasky. --- .../parse-error/pipeline-operator-errors.js | 23 +++++++++---------- .../babel-parser/src/parser/expression.js | 4 +++- .../src/plugins/typescript/index.js | 7 +++--- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/babel-parser/src/parse-error/pipeline-operator-errors.js b/packages/babel-parser/src/parse-error/pipeline-operator-errors.js index 8ba468be8fd4..ea6450f349b3 100644 --- a/packages/babel-parser/src/parse-error/pipeline-operator-errors.js +++ b/packages/babel-parser/src/parse-error/pipeline-operator-errors.js @@ -1,17 +1,14 @@ // @flow import { toParseErrorCredentials } from "../parse-error"; +import toNodeDescription from "./to-node-description"; -export const UnparenthesizedPipeBodyDescriptions = { - ArrowFunctionExpression: "arrow function", - AssignmentExpression: "assignment", - ConditionalExpression: "conditional", - YieldExpression: "yield", -}; - -type UnparenthesizedPipeBodyTypes = $Keys< - typeof UnparenthesizedPipeBodyDescriptions, ->; +export const UnparenthesizedPipeBodyDescriptions = new Set([ + "ArrowFunctionExpression", + "AssignmentExpression", + "ConditionalExpression", + "YieldExpression", +]); export default (_: typeof toParseErrorCredentials) => ({ // This error is only used by the smart-mix proposal @@ -31,9 +28,11 @@ export default (_: typeof toParseErrorCredentials) => ({ PipeTopicUnused: _( "Hack-style pipe body does not contain a topic reference; Hack-style pipes must use topic at least once.", ), - PipeUnparenthesizedBody: _<{| type: UnparenthesizedPipeBodyTypes |}>( + PipeUnparenthesizedBody: _<{| type: string |}>( ({ type }) => - `Hack-style pipe body cannot be an unparenthesized ${UnparenthesizedPipeBodyDescriptions[type]} expression; please wrap it in parentheses.`, + `Hack-style pipe body cannot be an unparenthesized ${toNodeDescription({ + type, + })}; please wrap it in parentheses.`, ), // Messages whose codes start with “Pipeline” or “PrimaryTopic” diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index f199266261e9..557103f9a625 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -550,7 +550,9 @@ export default class ExpressionParser extends LValParser { parseHackPipeBody(): N.Expression { const { startLoc } = this.state; const body = this.parseMaybeAssign(); - const requiredParentheses = UnparenthesizedPipeBodyDescriptions[body.type]; + const requiredParentheses = UnparenthesizedPipeBodyDescriptions.has( + body.type, + ); // TODO: Check how to handle type casts in Flow and TS once they are supported if (requiredParentheses && !body.extra?.parenthesized) { diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 767acf68e4fa..67d403d588e9 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -185,9 +185,6 @@ const TSErrors = toParseErrorClasses`typescript`(_ => ({ ({ modifier }) => `Private elements cannot have an accessibility modifier ('${modifier}').`, ), - SingleTypeParameterWithoutTrailingComma: _<{ name: string }>(({ name }) => - `Single type parameter ${name} should have a trailing comma. Example usage: <${name},>.` - ), ReadonlyForMethodSignature: _( "'readonly' modifier can only appear on a property declaration or index signature.", ), @@ -206,6 +203,10 @@ const TSErrors = toParseErrorClasses`typescript`(_ => ({ SetAccesorCannotHaveReturnType: _( "A 'set' accessor cannot have a return type annotation.", ), + SingleTypeParameterWithoutTrailingComma: _<{| name: string |}>( + ({ name }) => + `Single type parameter ${name} should have a trailing comma. Example usage: <${name},>.`, + ), StaticBlockCannotHaveModifier: _( "Static class blocks cannot have any modifier.", ), From 50314f60e2feceaae531dede1325ad8d9527b056 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 27 Feb 2022 16:52:14 -0800 Subject: [PATCH 070/104] Rename LHSParent to LValAncestor. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 2 +- .../babel-parser/src/parse-error/standard-errors.js | 13 +++++++------ packages/babel-parser/src/parser/lval.js | 6 +++--- packages/babel-parser/src/parser/statement.js | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index cb1a0386d5ee..f3122a2a3f7e 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -192,6 +192,6 @@ export const Errors = { ...toParseErrorClasses`pipelineOperator`(PipelineOperatorErrors), }; -export type { LHSParent } from "./parse-error/standard-errors"; +export type { LValAncestor } from "./parse-error/standard-errors"; export * from "./parse-error/credentials"; diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.js index 4d79aa50bb53..1106b155c350 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.js @@ -3,7 +3,7 @@ import { toParseErrorCredentials } from "../parse-error"; import toNodeDescription from "./to-node-description"; -export type LHSParent = +export type LValAncestor = | { type: "UpdateExpression", prefix: boolean } | { type: @@ -145,12 +145,13 @@ export default (_: typeof toParseErrorCredentials) => ({ InvalidIdentifier: _<{| identifier: string |}>( ({ identifier }) => `Invalid identifier ${identifier}.`, ), - InvalidLhs: _<{| context: LHSParent |}>( - ({ context }) => `Invalid left-hand side in ${toNodeDescription(context)}.`, + InvalidLhs: _<{| ancestor: LValAncestor |}>( + ({ ancestor }) => + `Invalid left-hand side in ${toNodeDescription(ancestor)}.`, ), - InvalidLhsBinding: _<{| context: LHSParent |}>( - ({ context }) => - `Binding invalid left-hand side in ${toNodeDescription(context)}.`, + InvalidLhsBinding: _<{| ancestor: LValAncestor |}>( + ({ ancestor }) => + `Binding invalid left-hand side in ${toNodeDescription(ancestor)}.`, ), InvalidNumber: _("Invalid number."), InvalidOrMissingExponent: _( diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index c4836ebadfb2..32f3ca88234e 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -32,7 +32,7 @@ import { BIND_SCOPE_LEXICAL, } from "../util/scopeflags"; import { ExpressionErrors } from "./util"; -import { Errors, type LHSParent } from "../parse-error"; +import { Errors, type LValAncestor } from "../parse-error"; const unwrapParenthesizedExpression = (node: Node): Node => { return node.type === "ParenthesizedExpression" @@ -535,7 +535,7 @@ export default class LValParser extends NodeUtils { allowingSloppyLetBinding = !(binding & BIND_SCOPE_LEXICAL), hasParenthesizedAncestor = false, }: { - in: LHSParent, + in: LValAncestor, binding?: BindingTypes, checkClashes?: Set | false, strictModeChanged?: boolean, @@ -592,7 +592,7 @@ export default class LValParser extends NodeUtils { this.raise(ParseErrorClass, { at: expression, - context: + ancestor: ancestor.type === "UpdateExpression" ? { type: "UpdateExpression", prefix: ancestor.prefix } : { type: ancestor.type }, diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index bb8163f9b643..07fd67ee1e5d 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -1175,7 +1175,7 @@ export default class StatementParser extends ExpressionParser { if (init.type === "AssignmentPattern") { this.raise(Errors.InvalidLhs, { at: init, - context: { type: "ForStatement" }, + ancestor: { type: "ForStatement" }, }); } From a52943b49e354dbb4fb87ffe93015c9966fe6700 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 28 Feb 2022 08:43:22 -0800 Subject: [PATCH 071/104] Add ParseError.clone and change symbol name to DefaultMessage from NoMessage. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 20 ++++++++++++++++--- .../src/parse-error/credentials.js | 7 ++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index f3122a2a3f7e..e48c7dd84dd8 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -12,12 +12,13 @@ const ArrayIsArray = Array.isArray; const { assign: ObjectAssign, defineProperty: ObjectDefineProperty, + getPrototypeOf: ObjectGetPrototypeOf, keys: ObjectKeys, } = Object; type ToMessage = (self: ErrorDetails) => string; -const NoMessage = Symbol("NoMessage"); +const DefaultMessage = Symbol("DefaultMessage"); // This should really be an abstract class, but that concept doesn't exist in // Flow, outside of just creating an interface, but then you can't specify that @@ -72,6 +73,19 @@ export class ParseError extends SyntaxError { return this; } + clone({ + loc, + details, + }: { + loc?: Position, + details?: ErrorDetails, + } = {}) { + return new (ObjectGetPrototypeOf(this).constructor)({ + loc: loc || this.loc, + details: { ...this.details, ...details }, + }); + } + get pos() { return this.loc.index; } @@ -82,7 +96,7 @@ function toParseErrorClass( credentials: ParseErrorCredentials, ): Class> { return class extends ParseError { - #message: typeof NoMessage | string = NoMessage; + #message: typeof DefaultMessage | string = DefaultMessage; constructor(...args): ParseError { super(...args); @@ -92,7 +106,7 @@ function toParseErrorClass( } get message() { - return this.#message !== NoMessage + return this.#message !== DefaultMessage ? String(this.#message) : `${toMessage(this.details)} (${this.loc.line}:${this.loc.column})`; } diff --git a/packages/babel-parser/src/parse-error/credentials.js b/packages/babel-parser/src/parse-error/credentials.js index d2b6073b4835..437dc7a97742 100644 --- a/packages/babel-parser/src/parse-error/credentials.js +++ b/packages/babel-parser/src/parse-error/credentials.js @@ -7,7 +7,12 @@ export const ParseErrorCodes = Object.freeze({ export type ParseErrorCode = $Values; -export type SyntaxPlugin = "flow" | "typescript" | "jsx" | "placeholders"; +export type SyntaxPlugin = + | "flow" + | "typescript" + | "jsx" + | "pipelineOperator" + | "placeholders"; export type ParseErrorCredentials = { code: ParseErrorCode, From a64ab0385d9366050797232e4db97520377e8f0f Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 28 Feb 2022 09:46:40 -0800 Subject: [PATCH 072/104] Change name to identifierName in a bunch of places. Reviewed by @tolmasky. --- .../src/parse-error/standard-errors.js | 23 ++++++++++--------- .../babel-parser/src/parser/expression.js | 7 ++++-- .../babel-parser/src/plugins/flow/index.js | 2 +- packages/babel-parser/src/util/class-scope.js | 9 +++++--- packages/babel-parser/src/util/scope.js | 5 +++- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.js index 1106b155c350..ac29e3983718 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.js @@ -142,8 +142,8 @@ export default (_: typeof toParseErrorCredentials) => ({ InvalidEscapedReservedWord: _<{| reservedWord: string |}>( ({ reservedWord }) => `Escape sequence in keyword ${reservedWord}.`, ), - InvalidIdentifier: _<{| identifier: string |}>( - ({ identifier }) => `Invalid identifier ${identifier}.`, + InvalidIdentifier: _<{| identifierName: string |}>( + ({ identifierName }) => `Invalid identifier ${identifierName}.`, ), InvalidLhs: _<{| ancestor: LValAncestor |}>( ({ ancestor }) => @@ -163,8 +163,8 @@ export default (_: typeof toParseErrorCredentials) => ({ InvalidParenthesizedAssignment: _( "Invalid parenthesized assignment pattern.", ), - InvalidPrivateFieldResolution: _<{| name: string |}>( - ({ name }) => `Private name #${name} is not defined.`, + InvalidPrivateFieldResolution: _<{| identifierName: string |}>( + ({ identifierName }) => `Private name #${identifierName} is not defined.`, ), InvalidPropertyBindingPattern: _("Binding member expression."), InvalidRecordProperty: _( @@ -242,12 +242,12 @@ export default (_: typeof toParseErrorCredentials) => ({ ParamDupe: _("Argument name clash."), PatternHasAccessor: _("Object pattern can't contain getter or setter."), PatternHasMethod: _("Object pattern can't contain methods."), - PrivateInExpectedIn: _<{| name: string |}>( - ({ name }) => - `Private names are only allowed in property accesses (\`obj.#${name}\`) or in \`in\` expressions (\`#${name} in obj\`).`, + PrivateInExpectedIn: _<{| identifierName: string |}>( + ({ identifierName }) => + `Private names are only allowed in property accesses (\`obj.#${identifierName}\`) or in \`in\` expressions (\`#${identifierName} in obj\`).`, ), - PrivateNameRedeclaration: _<{| name: string |}>( - ({ name }) => `Duplicate private name #${name}.`, + PrivateNameRedeclaration: _<{| identifierName: string |}>( + ({ identifierName }) => `Duplicate private name #${identifierName}.`, ), RecordExpressionBarIncorrectEndSyntaxType: _( "Record expressions ending with '|}' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'.", @@ -345,8 +345,9 @@ export default (_: typeof toParseErrorCredentials) => ({ UnterminatedRegExp: _("Unterminated regular expression."), UnterminatedString: _("Unterminated string constant."), UnterminatedTemplate: _("Unterminated template."), - VarRedeclaration: _<{| name: string |}>( - ({ name }) => `Identifier '${name}' has already been declared.`, + VarRedeclaration: _<{| identifierName: string |}>( + ({ identifierName }) => + `Identifier '${identifierName}' has already been declared.`, ), YieldBindingIdentifier: _( "Can not use 'yield' as identifier inside a generator.", diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 557103f9a625..675ddbc7edb4 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -423,7 +423,10 @@ export default class ExpressionParser extends LValParser { !this.prodParam.hasIn || !this.match(tt._in) ) { - this.raise(Errors.PrivateInExpectedIn, { at: left, name: value }); + this.raise(Errors.PrivateInExpectedIn, { + at: left, + identifierName: value, + }); } this.classScope.usePrivateName(value, left.loc.start); @@ -1193,7 +1196,7 @@ export default class ExpressionParser extends LValParser { // behavior of this big switch statement). this.raise(Errors.PrivateInExpectedIn, { at: this.state.startLoc, - name: this.state.value, + identifierName: this.state.value, }); return this.parsePrivateName(); } diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 83a77bcfc78c..38fd875af437 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -2321,7 +2321,7 @@ export default (superClass: Class): Class => if (!this.isIterator(word) || !this.state.inType) { this.raise(Errors.InvalidIdentifier, { at: this.state.curPosition(), - identifier: fullWord, + identifierName: fullWord, }); } diff --git a/packages/babel-parser/src/util/class-scope.js b/packages/babel-parser/src/util/class-scope.js index 2b8419ba2a35..41fc5e87dddb 100644 --- a/packages/babel-parser/src/util/class-scope.js +++ b/packages/babel-parser/src/util/class-scope.js @@ -55,7 +55,7 @@ export default class ClassScopeHandler { } else { this.parser.raise(Errors.InvalidPrivateFieldResolution, { at: loc, - name, + identifierName: name, }); } } @@ -91,7 +91,10 @@ export default class ClassScopeHandler { } if (redefined) { - this.parser.raise(Errors.PrivateNameRedeclaration, { at: loc, name }); + this.parser.raise(Errors.PrivateNameRedeclaration, { + at: loc, + identifierName: name, + }); } privateNames.add(name); @@ -110,7 +113,7 @@ export default class ClassScopeHandler { // top-level this.parser.raise(Errors.InvalidPrivateFieldResolution, { at: loc, - name, + identifierName: name, }); } } diff --git a/packages/babel-parser/src/util/scope.js b/packages/babel-parser/src/util/scope.js index 5fef485e935c..5547699ba23c 100644 --- a/packages/babel-parser/src/util/scope.js +++ b/packages/babel-parser/src/util/scope.js @@ -150,7 +150,10 @@ export default class ScopeHandler { loc: Position, ) { if (this.isRedeclaredInScope(scope, name, bindingType)) { - this.parser.raise(Errors.VarRedeclaration, { at: loc, name }); + this.parser.raise(Errors.VarRedeclaration, { + at: loc, + identifierName: name, + }); } } From 7af22eab334ba3ffeea6a34315d1262bc1268cfe Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 28 Feb 2022 17:53:08 -0800 Subject: [PATCH 073/104] Fix for using type. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error/standard-errors.js | 8 +++++--- packages/babel-parser/src/parser/statement.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.js index ac29e3983718..86582a075805 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.js @@ -92,9 +92,11 @@ export default (_: typeof toParseErrorCredentials) => ({ "'from' is not allowed as an identifier after 'export default'.", ), - ForInOfLoopInitializer: _<{| construct: "for-in" | "for-of" |}>( - ({ construct }) => - `'${construct}' loop variable declaration may not have an initializer.`, + ForInOfLoopInitializer: _<{| type: "ForInStatement" | "ForOfStatement" |}>( + ({ type }) => + `'${ + type === "ForInStatement" ? "for-in" : "for-of" + }' loop variable declaration may not have an initializer.`, ), ForOfAsync: _("The left-hand side of a for-of loop may not be 'async'."), diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 07fd67ee1e5d..e1c406fbc349 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -1168,7 +1168,7 @@ export default class StatementParser extends ExpressionParser { ) { this.raise(Errors.ForInOfLoopInitializer, { at: init, - construct: isForIn ? "for-in" : "for-of", + type: isForIn ? "ForInStatement" : "ForOfStatement", }); } From 4af3e28cd8b16cbfea9a059a8af7310e4758c089 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 1 Mar 2022 08:17:45 -0800 Subject: [PATCH 074/104] Better names. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error/standard-errors.js | 9 ++++++--- packages/babel-parser/src/parser/expression.js | 2 +- packages/babel-parser/src/plugins/typescript/index.js | 8 ++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.js index 86582a075805..90430b0fce64 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.js @@ -330,9 +330,12 @@ export default (_: typeof toParseErrorCredentials) => ({ UnsupportedImport: _( "`import` can only be used in `import()` or `import.meta`.", ), - UnsupportedMetaProperty: _<{| target: string, onlyValidProperty: string |}>( - ({ target, onlyValidProperty }) => - `The only valid meta property for ${target} is ${target}.${onlyValidProperty}.`, + UnsupportedMetaProperty: _<{| + target: string, + onlyValidPropertyName: string, + |}>( + ({ target, onlyValidPropertyName }) => + `The only valid meta property for ${target} is ${target}.${onlyValidPropertyName}.`, ), UnsupportedParameterDecorator: _( "Decorators cannot be used to decorate parameters.", diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 675ddbc7edb4..e4b50158f418 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -1591,7 +1591,7 @@ export default class ExpressionParser extends LValParser { this.raise(Errors.UnsupportedMetaProperty, { at: node.property, target: meta.name, - onlyValidProperty: propertyName, + onlyValidPropertyName: propertyName, }); } diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 67d403d588e9..a124f67e8cbf 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -203,9 +203,9 @@ const TSErrors = toParseErrorClasses`typescript`(_ => ({ SetAccesorCannotHaveReturnType: _( "A 'set' accessor cannot have a return type annotation.", ), - SingleTypeParameterWithoutTrailingComma: _<{| name: string |}>( - ({ name }) => - `Single type parameter ${name} should have a trailing comma. Example usage: <${name},>.`, + SingleTypeParameterWithoutTrailingComma: _<{| typeParameterName: string |}>( + ({ typeParameterName }) => + `Single type parameter ${typeParameterName} should have a trailing comma. Example usage: <${typeParameterName},>.`, ), StaticBlockCannotHaveModifier: _( "Static class blocks cannot have any modifier.", @@ -3143,7 +3143,7 @@ export default (superClass: Class): Class => if (invalidSingleType) { this.raise(TSErrors.SingleTypeParameterWithoutTrailingComma, { at: createPositionWithColumnOffset(invalidSingleType.loc.end, 1), - name: process.env.BABEL_8_BREAKING + typeParameterName: process.env.BABEL_8_BREAKING ? invalidSingleType.name.name : invalidSingleType.name, }); From 75d62abcde607ae23b636ef3d924559d855183d6 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 1 Mar 2022 08:39:49 -0800 Subject: [PATCH 075/104] Clean up API and add comments. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/lval.js | 75 +++++++++++++++---- .../src/plugins/typescript/index.js | 4 +- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 32f3ca88234e..5ab6a3af6c46 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -34,6 +34,8 @@ import { import { ExpressionErrors } from "./util"; import { Errors, type LValAncestor } from "../parse-error"; +const { isArray: ArrayIsArray } = Array; + const unwrapParenthesizedExpression = (node: Node): Node => { return node.type === "ParenthesizedExpression" ? unwrapParenthesizedExpression(node.expression) @@ -493,7 +495,35 @@ export default class LValParser extends NodeUtils { node.right = this.parseMaybeAssignAllowIn(); return this.finishNode(node, "AssignmentPattern"); } - + /** + * Return information use in determining whether a Node of a given type is an LVal, + * possibly given certain additional context information. + * + * Subclasser notes: This method has kind of a lot of mixed, but related, + * responsibilities. If we can definitively determine with the information + * provided that this either *is* or *isn't* a valid `LVal`, then the return + * value is easy: just return `true` or `false`. However, if it is a valid + * LVal *ancestor*, and thus it's descendents must be subsquently visited to + * continue the "investigation", then this method should return the relevant + * child key as a `string`. In some special cases, you additionally want to + * convey that this node should be treated as if it were parenthesized. In + * that case, a tuple of [key: string, parenthesized: boolean] is returned. + * The `string`-only return option is actually just a shorthand for: + * `[key: string, parenthesized: false]`. + * + * @param {NodeType} type A Node `type` string + * @param {boolean} isParenthesized + * Whether the node in question is parenthesized. + * @param {BindingTypes} binding + * The binding operation that is being considered for this potential + * LVal. + * @returns { boolean | string | [string, boolean] } + * `true` or `false` if we can immediately determine whether the node + * type in question can be treated as an `LVal`. + * A `string` key to traverse if we must check this child. + * A `[string, boolean]` tuple if we need to check this child and + * treat is as parenthesized. + */ // eslint-disable-next-line no-unused-vars isValidLVal(type: string, isParenthesized: boolean, binding: BindingTypes) { return ( @@ -509,19 +539,32 @@ export default class LValParser extends NodeUtils { } /** - * Verify that if a node is an lval - something that can be assigned to. + * Verify that a target expression is an val (something that can be assigned to). * - * @param {Expression} expr The given node - * @param {string} contextDescription The auxiliary context information printed when error is thrown - * @param {BindingTypes} [bindingType=BIND_NONE] The desired binding type. If the given node is an identifier and `bindingType` is not - BIND_NONE, `checkLVal` will register binding to the parser scope - See also src/util/scopeflags.js - * @param {?Set} checkClashes An optional string set to check if an identifier name is included. `checkLVal` will add checked - identifier name to `checkClashes` It is used in tracking duplicates in function parameter lists. If - it is nullish, `checkLVal` will skip duplicate checks - * @param {boolean} [disallowLetBinding] Whether an identifier named "let" should be disallowed - * @param {boolean} [strictModeChanged=false] Whether an identifier has been parsed in a sloppy context but should be reinterpreted as - strict-mode. e.g. `(arguments) => { "use strict "}` + * @param {Expression} expression The expression in question to check. + * @param {Object} options A set of options described below. + * @param {LValAncestor} options.in + * The relevant ancestor to provide context information for the error + * if the check fails. + * @param {BindingTypes} [options.binding=BIND_NONE] + * The desired binding type. If the given expression is an identifier + * and `binding` is not `BIND_NONE`, `checkLVal` will register binding + * to the parser scope See also `src/util/scopeflags.js` + * @param {Set|false} [options.checkClashes=false] + * An optional string set to check if an identifier name is included. + * `checkLVal` will add checked identifier name to `checkClashes` It is + * used in tracking duplicates in function parameter lists. If it is + * nullish, `checkLVal` will skip duplicate checks + * @param {boolean} [options.allowingSloppyLetBinding] + * Whether an identifier named "let" should be allowed in sloppy mode. + * Defaults to `true` unless lexical scope its being used. This property + * is only relevant if the parser's state is in sloppy mode. + * @param {boolean} [options.strictModeChanged=false] + * Whether an identifier has been parsed in a sloppy context but should + * be reinterpreted as strict-mode. e.g. `(arguments) => { "use strict "}` + * @param {boolean} [options.hasParenthesizedAncestor=false] + * This is only used internally during recursive calls, and you should + * not have to set it yourself. * @memberof LValParser */ @@ -600,9 +643,9 @@ export default class LValParser extends NodeUtils { return; } - const isParenthesizedExpression = - type === "ParenthesizedExpression" || Array.isArray(validity); - const key = Array.isArray(validity) ? validity[1] : validity; + const [key, isParenthesizedExpression] = ArrayIsArray(validity) + ? validity + : [validity, type === "ParenthesizedExpression"]; const nextAncestor = expression.type === "ArrayPattern" || expression.type === "ObjectPattern" || diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index a124f67e8cbf..3a3fc29921b1 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3313,12 +3313,12 @@ export default (superClass: Class): Class => TSParameterProperty: "parameter", TSNonNullExpression: "expression", TSAsExpression: (binding !== BIND_NONE || isParenthesized) && [ - true, "expression", + true, ], TSTypeAssertion: (binding !== BIND_NONE || isParenthesized) && [ - true, "expression", + true, ], }[type] || super.isValidLVal(type, isParenthesized, binding) ); From 124cb19213d69f83595827bb01d2cc50c11a3bb0 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 1 Mar 2022 08:59:26 -0800 Subject: [PATCH 076/104] Change name. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index e48c7dd84dd8..32cc18f6aa81 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -18,7 +18,7 @@ const { type ToMessage = (self: ErrorDetails) => string; -const DefaultMessage = Symbol("DefaultMessage"); +const StandardMessage = Symbol("StandardMessage"); // This should really be an abstract class, but that concept doesn't exist in // Flow, outside of just creating an interface, but then you can't specify that @@ -96,7 +96,7 @@ function toParseErrorClass( credentials: ParseErrorCredentials, ): Class> { return class extends ParseError { - #message: typeof DefaultMessage | string = DefaultMessage; + #message: typeof StandardMessage | string = StandardMessage; constructor(...args): ParseError { super(...args); @@ -106,7 +106,7 @@ function toParseErrorClass( } get message() { - return this.#message !== DefaultMessage + return this.#message !== StandardMessage ? String(this.#message) : `${toMessage(this.details)} (${this.loc.line}:${this.loc.column})`; } From fe1daf4befe0058fb761778122bf6992d8df9aa8 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 1 Mar 2022 12:25:20 -0800 Subject: [PATCH 077/104] Better names. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error/standard-errors.js | 4 ++-- packages/babel-parser/src/parser/statement.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.js index 90430b0fce64..8f2296a21c9c 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.js @@ -173,8 +173,8 @@ export default (_: typeof toParseErrorCredentials) => ({ "Only properties and spread elements are allowed in record definitions.", ), InvalidRestAssignmentPattern: _("Invalid rest operator's argument."), - LabelRedeclaration: _<{| label: string |}>( - ({ label }) => `Label '${label}' is already declared.`, + LabelRedeclaration: _<{| labelName: string |}>( + ({ labelName }) => `Label '${labelName}' is already declared.`, ), LetInLexicalBinding: _( "'let' is not allowed to be used as a name in 'let' or 'const' declarations.", diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index e1c406fbc349..f418464f80c4 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -957,7 +957,7 @@ export default class StatementParser extends ExpressionParser { if (label.name === maybeName) { this.raise(Errors.LabelRedeclaration, { at: expr, - label: maybeName, + labelName: maybeName, }); } } From 2cb62311d386b904f607cc052b2131bfa18b36eb Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 1 Mar 2022 17:57:37 -0800 Subject: [PATCH 078/104] Minor comment fix. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error/strict-mode-errors.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/src/parse-error/strict-mode-errors.js b/packages/babel-parser/src/parse-error/strict-mode-errors.js index 05931ec07c14..62d4fe9038f2 100644 --- a/packages/babel-parser/src/parse-error/strict-mode-errors.js +++ b/packages/babel-parser/src/parse-error/strict-mode-errors.js @@ -5,8 +5,8 @@ import { toParseErrorCredentials } from "../parse-error"; export default (_: typeof toParseErrorCredentials) => ({ StrictDelete: _("Deleting local variable in strict mode."), - // `bindingName` is the StringValue[1] of an IdentifierReference[2], which is - // represented as just an `Identifier`[3] in the Babel AST. + // `referenceName` is the StringValue[1] of an IdentifierReference[2], which + // is represented as just an `Identifier`[3] in the Babel AST. // 1. https://tc39.es/ecma262/#sec-static-semantics-stringvalue // 2. https://tc39.es/ecma262/#prod-IdentifierReference // 3. https://github.com/babel/babel/blob/main/packages/babel-parser/ast/spec.md#identifier From 9322f3f92ab25b486de90cff1b4614ed1aff8d74 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 1 Mar 2022 18:22:54 -0800 Subject: [PATCH 079/104] Remove no longer used method. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/util.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index 3d49bb44e62c..5830618d197c 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -19,7 +19,6 @@ import ProductionParameterHandler, { PARAM, } from "../util/production-parameter"; import { Errors, ParseError } from "../parse-error"; -import type { PluginConfig } from "./base"; /*:: import type ScopeHandler from "../util/scope"; */ @@ -150,16 +149,6 @@ export default class UtilParser extends Tokenizer { this.eat(type) || this.unexpected(loc, type); } - getPluginNamesFromConfigs(pluginConfigs: Array): Array { - return pluginConfigs.map(c => { - if (typeof c === "string") { - return c; - } else { - return c[0]; - } - }); - } - // tryParse will clone parser state. // It is expensive and should be used with cautions tryParse>( From 084d475de52a3ddd58df4c6cb6ddad33e188173a Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 2 Mar 2022 13:04:26 -0500 Subject: [PATCH 080/104] Update packages/babel-parser/src/parser/lval.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/lval.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 5ab6a3af6c46..d5a7cb875903 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -554,7 +554,7 @@ export default class LValParser extends NodeUtils { * An optional string set to check if an identifier name is included. * `checkLVal` will add checked identifier name to `checkClashes` It is * used in tracking duplicates in function parameter lists. If it is - * nullish, `checkLVal` will skip duplicate checks + * false, `checkLVal` will skip duplicate checks * @param {boolean} [options.allowingSloppyLetBinding] * Whether an identifier named "let" should be allowed in sloppy mode. * Defaults to `true` unless lexical scope its being used. This property From b92b54151cff8d8f93995642bed9185865614588 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 2 Mar 2022 13:04:34 -0500 Subject: [PATCH 081/104] Update packages/babel-parser/src/parser/lval.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/lval.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index d5a7cb875903..f35bfdee4198 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -539,7 +539,7 @@ export default class LValParser extends NodeUtils { } /** - * Verify that a target expression is an val (something that can be assigned to). + * Verify that a target expression is an lval (something that can be assigned to). * * @param {Expression} expression The expression in question to check. * @param {Object} options A set of options described below. From 359da559cad266b5d520a533c828cc6e35971cae Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 2 Mar 2022 10:05:10 -0800 Subject: [PATCH 082/104] Minor grammar changes to comments. Reviewed by @tolmasky. --- packages/babel-parser/src/tokenizer/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 87fd1aa8987c..3e6c098a330b 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -1741,7 +1741,7 @@ export default class Tokenizer extends CommentsParser { /** * Raise a `ParseError` given the appropriate properties. If passed a - *`Position` for the `at` property, raises the `ParseError` at that location. + * `Position` for the `at` property, raises the `ParseError` at that location. * Otherwise, if passed a `Node`, raises the `ParseError` at the start * location of that `Node`. * @@ -1769,7 +1769,7 @@ export default class Tokenizer extends CommentsParser { /** * If `errorRecovery` is `false`, this method behaves identically to `raise`. - * If `errorRecovery` is `true`, this method will first see if there is an + * If `errorRecovery` is `true`, this method will first see if there is * already an error stored at the same `Position`, and replaces it with the * one generated here. * From 7248dfd9625e04735c7a9cec82586e37087703e0 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 2 Mar 2022 10:10:47 -0800 Subject: [PATCH 083/104] Use trim instead of slice(0, -1). Reviewed by @tolmasky. --- scripts/parser-tests/typescript/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/parser-tests/typescript/index.js b/scripts/parser-tests/typescript/index.js index fdb2468be825..0d74d06f4e1e 100644 --- a/scripts/parser-tests/typescript/index.js +++ b/scripts/parser-tests/typescript/index.js @@ -8,7 +8,7 @@ import { spawnSync } from "child_process"; const getEncoding = path => spawnSync("file", ["--brief", "--mime-encoding", path]) .stdout.toString() - .slice(0, -1); + .trim(); const toNodeEncoding = fileEncoding => ({ "us-ascii": "utf-8", From e7e4393ffff4b4725932868f7d80f640b6180392 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 2 Mar 2022 10:17:17 -0800 Subject: [PATCH 084/104] Add a comment to explain the tagged template stuff. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 32cc18f6aa81..848e3ebaa4cd 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -159,9 +159,17 @@ declare function toParseErrorClasses( syntaxPlugin?: string, ): T; +// toParseErrorClasses can optionally be template tagged to provide a +// syntaxPlugin: +// +// toParseErrorClasses`syntaxPlugin` (_ => ... ) +// // See comment about eslint and Flow overloading above. // eslint-disable-next-line no-redeclare export function toParseErrorClasses(argument, syntaxPlugin) { + // If the first parameter is an array, that means we were called with a tagged + // template literal. Extract the syntaxPlugin from this, and call again in + // the "normalized" form. if (ArrayIsArray(argument)) { return toClasses => toParseErrorClasses(toClasses, argument[0]); } From 6a418de7bfdcd1b2b32c5fb839f76498d0906d5f Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 2 Mar 2022 11:36:26 -0800 Subject: [PATCH 085/104] Use maxArgumentCount. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error/standard-errors.js | 6 +++--- packages/babel-parser/src/parser/expression.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/babel-parser/src/parse-error/standard-errors.js b/packages/babel-parser/src/parse-error/standard-errors.js index 8f2296a21c9c..b17d6bfc2ef0 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.js +++ b/packages/babel-parser/src/parse-error/standard-errors.js @@ -121,10 +121,10 @@ export default (_: typeof toParseErrorCredentials) => ({ ImportCallArgumentTrailingComma: _( "Trailing comma is disallowed inside import(...) arguments.", ), - ImportCallArity: _<{| required: 1 | 2 |}>( - ({ required }) => + ImportCallArity: _<{| maxArgumentCount: 1 | 2 |}>( + ({ maxArgumentCount }) => `\`import()\` requires exactly ${ - required === 1 ? "one argument" : "one or two arguments" + maxArgumentCount === 1 ? "one argument" : "one or two arguments" }.`, ), ImportCallNotNewExpression: _("Cannot use new with import(...)."), diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index e4b50158f418..f698b255a8b2 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -949,7 +949,7 @@ export default class ExpressionParser extends LValParser { if (node.arguments.length === 0 || node.arguments.length > 2) { this.raise(Errors.ImportCallArity, { at: node, - required: + maxArgumentCount: this.hasPlugin("importAssertions") || this.hasPlugin("moduleAttributes") ? 2 From 54ddad53645b3797f7dc2146d1a654cfa2e6a9c9 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 2 Mar 2022 13:41:04 -0800 Subject: [PATCH 086/104] Don't allow UpdateExpression without prefix. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error/to-node-description.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/babel-parser/src/parse-error/to-node-description.js b/packages/babel-parser/src/parse-error/to-node-description.js index b8708a69e6a7..9443c5ac32c0 100644 --- a/packages/babel-parser/src/parse-error/to-node-description.js +++ b/packages/babel-parser/src/parse-error/to-node-description.js @@ -20,7 +20,9 @@ const NodeDescriptions = { YieldExpression: "yield expression", }; -type NodeTypesWithDescriptions = $Keys; +type NodeTypesWithDescriptions = $Keys< + $Diff, +>; type NodeWithDescription = | { type: "UpdateExpression", prefix: boolean } | { type: NodeTypesWithDescriptions }; From 69ec1d38a7875db3cb908003db35565276bfa9b9 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 2 Mar 2022 13:51:58 -0800 Subject: [PATCH 087/104] Add tds test for no-initializer. Reviewed by @tolmasky. --- .../typescript/dts/no-initializer/input.ts | 1 + .../typescript/dts/no-initializer/output.json | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 packages/babel-parser/test/fixtures/typescript/dts/no-initializer/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/dts/no-initializer/output.json diff --git a/packages/babel-parser/test/fixtures/typescript/dts/no-initializer/input.ts b/packages/babel-parser/test/fixtures/typescript/dts/no-initializer/input.ts new file mode 100644 index 000000000000..8caae258b97c --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/dts/no-initializer/input.ts @@ -0,0 +1 @@ +const x: number; diff --git a/packages/babel-parser/test/fixtures/typescript/dts/no-initializer/output.json b/packages/babel-parser/test/fixtures/typescript/dts/no-initializer/output.json new file mode 100644 index 000000000000..a9192cb1af2c --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/dts/no-initializer/output.json @@ -0,0 +1,38 @@ +{ + "type": "File", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":16,"index":16}}, + "program": { + "type": "Program", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":16,"index":16}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":16,"index":16}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":6,"end":15,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":15,"index":15}}, + "id": { + "type": "Identifier", + "start":6,"end":15,"loc":{"start":{"line":1,"column":6,"index":6},"end":{"line":1,"column":15,"index":15},"identifierName":"x"}, + "name": "x", + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start":7,"end":15,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":15,"index":15}}, + "typeAnnotation": { + "type": "TSNumberKeyword", + "start":9,"end":15,"loc":{"start":{"line":1,"column":9,"index":9},"end":{"line":1,"column":15,"index":15}} + } + } + }, + "init": null + } + ], + "kind": "const" + } + ], + "directives": [] + } +} From 7159c84c3c31c27815c037a7ddcab8741501766d Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 2 Mar 2022 14:02:21 -0800 Subject: [PATCH 088/104] Make this test test ASI and not const initializers. Reviewed by @tolmasky. --- .../declare/const-new-line/input.ts | 2 +- .../declare/const-new-line/output.json | 40 ++++++++++++------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/input.ts b/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/input.ts index f8d2e9cb8ae9..8f71b245823c 100644 --- a/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/input.ts +++ b/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/input.ts @@ -1,2 +1,2 @@ declare -const x: number, y: string; +const x: number = 10, y: string = "something"; diff --git a/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/output.json b/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/output.json index 2dba7ec555da..6c1f3faa717b 100644 --- a/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/output.json +++ b/packages/babel-parser/test/fixtures/typescript/declare/const-new-line/output.json @@ -1,13 +1,9 @@ { "type": "File", - "start":0,"end":35,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":27,"index":35}}, - "errors": [ - "SyntaxError: Missing initializer in const declaration. (2:15)", - "SyntaxError: Missing initializer in const declaration. (2:26)" - ], + "start":0,"end":54,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":46,"index":54}}, "program": { "type": "Program", - "start":0,"end":35,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":27,"index":35}}, + "start":0,"end":54,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":46,"index":54}}, "sourceType": "module", "interpreter": null, "body": [ @@ -22,11 +18,11 @@ }, { "type": "VariableDeclaration", - "start":8,"end":35,"loc":{"start":{"line":2,"column":0,"index":8},"end":{"line":2,"column":27,"index":35}}, + "start":8,"end":54,"loc":{"start":{"line":2,"column":0,"index":8},"end":{"line":2,"column":46,"index":54}}, "declarations": [ { "type": "VariableDeclarator", - "start":14,"end":23,"loc":{"start":{"line":2,"column":6,"index":14},"end":{"line":2,"column":15,"index":23}}, + "start":14,"end":28,"loc":{"start":{"line":2,"column":6,"index":14},"end":{"line":2,"column":20,"index":28}}, "id": { "type": "Identifier", "start":14,"end":23,"loc":{"start":{"line":2,"column":6,"index":14},"end":{"line":2,"column":15,"index":23},"identifierName":"x"}, @@ -40,25 +36,41 @@ } } }, - "init": null + "init": { + "type": "NumericLiteral", + "start":26,"end":28,"loc":{"start":{"line":2,"column":18,"index":26},"end":{"line":2,"column":20,"index":28}}, + "extra": { + "rawValue": 10, + "raw": "10" + }, + "value": 10 + } }, { "type": "VariableDeclarator", - "start":25,"end":34,"loc":{"start":{"line":2,"column":17,"index":25},"end":{"line":2,"column":26,"index":34}}, + "start":30,"end":53,"loc":{"start":{"line":2,"column":22,"index":30},"end":{"line":2,"column":45,"index":53}}, "id": { "type": "Identifier", - "start":25,"end":34,"loc":{"start":{"line":2,"column":17,"index":25},"end":{"line":2,"column":26,"index":34},"identifierName":"y"}, + "start":30,"end":39,"loc":{"start":{"line":2,"column":22,"index":30},"end":{"line":2,"column":31,"index":39},"identifierName":"y"}, "name": "y", "typeAnnotation": { "type": "TSTypeAnnotation", - "start":26,"end":34,"loc":{"start":{"line":2,"column":18,"index":26},"end":{"line":2,"column":26,"index":34}}, + "start":31,"end":39,"loc":{"start":{"line":2,"column":23,"index":31},"end":{"line":2,"column":31,"index":39}}, "typeAnnotation": { "type": "TSStringKeyword", - "start":28,"end":34,"loc":{"start":{"line":2,"column":20,"index":28},"end":{"line":2,"column":26,"index":34}} + "start":33,"end":39,"loc":{"start":{"line":2,"column":25,"index":33},"end":{"line":2,"column":31,"index":39}} } } }, - "init": null + "init": { + "type": "StringLiteral", + "start":42,"end":53,"loc":{"start":{"line":2,"column":34,"index":42},"end":{"line":2,"column":45,"index":53}}, + "extra": { + "rawValue": "something", + "raw": "\"something\"" + }, + "value": "something" + } } ], "kind": "const" From 28bbe47c783b5749e3d7d625b2baf5e572052c8a Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 2 Mar 2022 17:08:17 -0500 Subject: [PATCH 089/104] Update packages/babel-parser/src/parser/expression.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/expression.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index f698b255a8b2..96ff0ddf7e66 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -652,7 +652,7 @@ export default class ExpressionParser extends LValParser { ): N.Expression { if (update) { this.checkLVal(node.argument, { - in: (node = this.finishNode(node, "UpdateExpression")), + in: this.finishNode(node, "UpdateExpression"), }); return node; } From e0a361500d1ae8520ed6c95d3f1db899d1b677f0 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Wed, 2 Mar 2022 16:28:34 -0800 Subject: [PATCH 090/104] Change template string to normal string and remove default. Reviewed by @tolmasky. --- .../babel-parser/src/parse-error/pipeline-operator-errors.js | 2 +- packages/babel-parser/src/parse-error/to-node-description.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/src/parse-error/pipeline-operator-errors.js b/packages/babel-parser/src/parse-error/pipeline-operator-errors.js index ea6450f349b3..a5a66d6d5eb7 100644 --- a/packages/babel-parser/src/parse-error/pipeline-operator-errors.js +++ b/packages/babel-parser/src/parse-error/pipeline-operator-errors.js @@ -13,7 +13,7 @@ export const UnparenthesizedPipeBodyDescriptions = new Set([ export default (_: typeof toParseErrorCredentials) => ({ // This error is only used by the smart-mix proposal PipeBodyIsTighter: _( - `Unexpected yield after pipeline body; any yield expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence.`, + "Unexpected yield after pipeline body; any yield expression acting as Hack-style pipe body must be parenthesized due to its loose operator precedence.", ), PipeTopicRequiresHackPipes: _( 'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" or "smart" option.', diff --git a/packages/babel-parser/src/parse-error/to-node-description.js b/packages/babel-parser/src/parse-error/to-node-description.js index 9443c5ac32c0..c604e30454b1 100644 --- a/packages/babel-parser/src/parse-error/to-node-description.js +++ b/packages/babel-parser/src/parse-error/to-node-description.js @@ -28,7 +28,7 @@ type NodeWithDescription = | { type: NodeTypesWithDescriptions }; // eslint-disable-next-line no-confusing-arrow -const toNodeDescription = ({ type, prefix = false }: NodeWithDescription) => +const toNodeDescription = ({ type, prefix }: NodeWithDescription) => type === "UpdateExpression" ? NodeDescriptions.UpdateExpression[String(prefix)] : NodeDescriptions[type]; From b9206efec4922756c102b38a1da7d7d9414be566 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Thu, 3 Mar 2022 19:57:28 -0800 Subject: [PATCH 091/104] Get rid of ParseError class and use SyntaxErrors instead. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 249 ++++++++---------- .../src/parse-error/credentials.js | 47 +++- .../babel-parser/src/parser/expression.js | 2 +- packages/babel-parser/src/parser/util.js | 14 +- .../babel-parser/src/plugins/flow/index.js | 4 +- .../babel-parser/src/plugins/jsx/index.js | 4 +- .../babel-parser/src/plugins/placeholders.js | 4 +- .../src/plugins/typescript/index.js | 4 +- packages/babel-parser/src/tokenizer/index.js | 37 +-- packages/babel-parser/src/tokenizer/state.js | 6 +- .../babel-parser/src/util/expression-scope.js | 26 +- 11 files changed, 203 insertions(+), 194 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 848e3ebaa4cd..db5609f2ad0e 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -3,198 +3,155 @@ import { Position } from "./util/location"; import type { NodeBase } from "./types"; import { + instantiate, type ParseErrorCode, ParseErrorCodes, type ParseErrorCredentials, } from "./parse-error/credentials"; -const ArrayIsArray = Array.isArray; -const { - assign: ObjectAssign, - defineProperty: ObjectDefineProperty, - getPrototypeOf: ObjectGetPrototypeOf, - keys: ObjectKeys, -} = Object; - -type ToMessage = (self: ErrorDetails) => string; - -const StandardMessage = Symbol("StandardMessage"); - -// This should really be an abstract class, but that concept doesn't exist in -// Flow, outside of just creating an interface, but then you can't specify that -// it's a subclass of `SyntaxError`. You could do something like: -// -// interface IParseError { ... } -// type ParseError = SyntaxError & IParseError; -// -// But this is just a more complicated way of getting the same behavior, with -// the added clumsiness of not being able to extends ParseError directly. So, -// to keep things simple and prepare for a Typescript future, we just make it a -// "private" superclass that we exclusively subclass: - -export class ParseError extends SyntaxError { +// Babel uses "normal" SyntaxErrors for it's errors, but adds some extra +// functionality. This functionality is defined in the +// `ParseErrorSpecification` interface below. We may choose to change to someday +// give our errors their own full-blown class, but until then this allow us to +// keep all the desirable properties of SyntaxErrors (like their name in stack +// traces, etc.), and also allows us to punt on any publically facing +// class-hierarchy decisions until Babel 8. +interface ParseErrorSpecification { + // Look, these *could* be readonly, but then Flow complains when we initially + // set them. We could do a whole dance and make a special interface that's not + // readonly for when we create the error, then cast it to the readonly + // interface for public use, but the previous implementation didn't have them + // as readonly, so let's just not worry about it for now. code: ParseErrorCode; reasonCode: string; + syntaxPlugin?: string; + + missingPlugin?: string | string[]; loc: Position; details: ErrorDetails; - // There are no optional fields in classes in Flow, so we can't list these - // here: https://github.com/facebook/flow/issues/2859 - // syntaxPlugin?: SyntaxPlugin - // missingPlugin?: string[] - - constructor({ - loc, - details, - }: { - loc: Position, - details: ErrorDetails, - }): ParseError { - super(); - - this.loc = loc; - - ObjectDefineProperty(this, "details", { - value: details, - enumerable: false, - }); - - // $FlowIgnore - if (details.missingPlugin) { - ObjectDefineProperty(this, "missingPlugin", { - get() { - return this.details.missingPlugin; - }, - enumerable: true, - }); - } - - return this; - } - - clone({ - loc, - details, - }: { - loc?: Position, - details?: ErrorDetails, - } = {}) { - return new (ObjectGetPrototypeOf(this).constructor)({ - loc: loc || this.loc, - details: { ...this.details, ...details }, - }); - } - - get pos() { - return this.loc.index; - } + // We should consider removing this as it now just contains the same + // information as `loc.index`. + // pos: number; } -function toParseErrorClass( - toMessage: ToMessage, - credentials: ParseErrorCredentials, -): Class> { - return class extends ParseError { - #message: typeof StandardMessage | string = StandardMessage; - - constructor(...args): ParseError { - super(...args); - // $FlowIgnore - Only necessary because we can't make syntaxPlugin optional. - ObjectAssign(this, credentials); - return this; - } - - get message() { - return this.#message !== StandardMessage - ? String(this.#message) - : `${toMessage(this.details)} (${this.loc.line}:${this.loc.column})`; - } - - set message(message) { - this.#message = message; - } - }; +export type ParseError = SyntaxError & + ParseErrorSpecification; + +// By `ParseErrorConstructor`, we mean something like the new-less style +// `ErrorConstructor`[1], since `ParseError`'s are not themselves actually +// separate classes from `SyntaxError`'s. +// +// 1. https://github.com/microsoft/TypeScript/blob/v4.5.5/lib/lib.es5.d.ts#L1027 +export type ParseErrorConstructor = ({ + loc: Position, + details: ErrorDetails, +}) => ParseError; + +function toParseErrorConstructor({ + toMessage, + ...properties +}: ParseErrorCredentials): ParseErrorConstructor { + return ({ loc, details }: { loc: Position, details: ErrorDetails }) => + instantiate>( + SyntaxError, + { ...properties, loc }, + { + details: { value: details, enumerable: false }, + message: { + get: ({ details, loc }) => + `${toMessage(details)} (${loc.line}:${loc.column})`, + set: (self, value) => + Object.defineProperty(self, "message", { value }), + }, + pos: "loc.index", + missingPlugin: "missingPlugin" in details && "details.missingPlugin", + }, + ); } -// This part is tricky, and only necessary due to some bugs in Flow that won't -// be fixed for a year(?): https://github.com/facebook/flow/issues/8838 -// Flow has a very difficult time extracting the parameter types of functions, -// so we are forced to pretend the class exists earlier than it does. -// `toParseErrorCredentials` *does not* actuall return a -// `Class>`, but we simply mark it as such to "carry" -// the `ErrorDetails` type parameter around. This is not a problem in Typescript -// where this intermediate function actually won't be needed at all. +// This part is tricky. You'll probably notice from the name of this function +// that it is supposed to return `ParseErrorCredentials`, but instead these. +// declarations seem to instead imply that they return +// `ParseErrorConstructor` instead. This is because in Flow we +// can't easily extract parameter types (either from functions, like with +// Typescript's Parameters utility type, or from generic types either). As +// such, this function does double duty: packaging up the credentials during +// its actual runtime operation, but pretending to return the +// `ParseErrorConstructor` that we won't actually have until later +// to the type system, avoiding the need to do so with $ObjMap (which doesn't +// work) in `ParseErrorEnum`. This hack won't be necessary when we switch to +// Typescript. declare function toParseErrorCredentials( T, ?{ code?: ParseErrorCode, reasonCode?: string } | boolean, -): Class>; +): ParseErrorConstructor<{||}>; // ESLint seems to erroneously think that Flow's overloading syntax is an // accidental redeclaration of the function: // https://github.com/babel/eslint-plugin-babel/issues/162 // eslint-disable-next-line no-redeclare -declare function toParseErrorCredentials( - (T) => string, +declare function toParseErrorCredentials( + (ErrorDetails) => string, ?{ code?: ParseErrorCode, reasonCode?: string } | boolean, -): Class>; +): ParseErrorConstructor; // See comment about eslint and Flow overloading above. // eslint-disable-next-line no-redeclare export function toParseErrorCredentials(toMessageOrMessage, credentials) { - return [ - typeof toMessageOrMessage === "string" - ? () => toMessageOrMessage - : toMessageOrMessage, - credentials, - ]; + return { + toMessage: + typeof toMessageOrMessage === "string" + ? () => toMessageOrMessage + : toMessageOrMessage, + ...credentials, + }; } -declare function toParseErrorClasses(string[]): typeof toParseErrorClasses; +// This is the templated form. +declare function ParseErrorEnum(string[]): typeof ParseErrorEnum; // See comment about eslint and Flow overloading above. // eslint-disable-next-line no-redeclare -declare function toParseErrorClasses( - toClasses: (typeof toParseErrorCredentials) => T, +declare function ParseErrorEnum( + toParseErrorCredentials: (typeof toParseErrorCredentials) => T, syntaxPlugin?: string, ): T; -// toParseErrorClasses can optionally be template tagged to provide a -// syntaxPlugin: +// You call `ParseErrorEnum` with a mapping from `ReasonCode`'s to either error +// messages, or `toMessage` functions that define additional necessary `details` +// needed by the `ParseError`: // -// toParseErrorClasses`syntaxPlugin` (_ => ... ) +// ParseErrorEnum`optionalSyntaxPlugin` (_ => ({ +// ErrorWithStaticMessage: _("message"), +// ErrorWithDynamicMessage: _<{ type: string }>(({ type }) => `${type}`), +// }); // // See comment about eslint and Flow overloading above. // eslint-disable-next-line no-redeclare -export function toParseErrorClasses(argument, syntaxPlugin) { +export function ParseErrorEnum(argument, syntaxPlugin) { // If the first parameter is an array, that means we were called with a tagged // template literal. Extract the syntaxPlugin from this, and call again in // the "normalized" form. - if (ArrayIsArray(argument)) { - return toClasses => toParseErrorClasses(toClasses, argument[0]); + if (Array.isArray(argument)) { + return toParseErrorCredentialsMap => + ParseErrorEnum(toParseErrorCredentialsMap, argument[0]); } - const classes = argument(toParseErrorCredentials); + const partialCredentials = argument(toParseErrorCredentials); + const ParseErrorConstructors = {}; - for (const reasonCode of ObjectKeys(classes)) { - const [toMessage, credentials = {}] = classes[reasonCode]; - const ParseErrorClass = toParseErrorClass(toMessage, { - code: credentials.code || ParseErrorCodes.SyntaxError, - reasonCode: credentials.reasonCode || reasonCode, + for (const reasonCode of Object.keys(partialCredentials)) { + ParseErrorConstructors[reasonCode] = toParseErrorConstructor({ + code: ParseErrorCodes.SyntaxError, + reasonCode, ...(syntaxPlugin ? { syntaxPlugin } : {}), - }); - - classes[reasonCode] = ParseErrorClass; - - // We do this for backwards compatibility so that all errors just have the - // "SyntaxError" name in their messages instead of leaking the private - // subclass name. - ObjectDefineProperty(ParseErrorClass.prototype.constructor, "name", { - value: "SyntaxError", + ...partialCredentials[reasonCode], }); } - return classes; + return ParseErrorConstructors; } export type RaiseProperties = {| @@ -208,10 +165,10 @@ import StrictModeErrors from "./parse-error/strict-mode-errors"; import PipelineOperatorErrors from "./parse-error/pipeline-operator-errors"; export const Errors = { - ...toParseErrorClasses(ModuleErrors), - ...toParseErrorClasses(StandardErrors), - ...toParseErrorClasses(StrictModeErrors), - ...toParseErrorClasses`pipelineOperator`(PipelineOperatorErrors), + ...ParseErrorEnum(ModuleErrors), + ...ParseErrorEnum(StandardErrors), + ...ParseErrorEnum(StrictModeErrors), + ...ParseErrorEnum`pipelineOperator`(PipelineOperatorErrors), }; export type { LValAncestor } from "./parse-error/standard-errors"; diff --git a/packages/babel-parser/src/parse-error/credentials.js b/packages/babel-parser/src/parse-error/credentials.js index 437dc7a97742..477240c24b40 100644 --- a/packages/babel-parser/src/parse-error/credentials.js +++ b/packages/babel-parser/src/parse-error/credentials.js @@ -14,8 +14,53 @@ export type SyntaxPlugin = | "pipelineOperator" | "placeholders"; -export type ParseErrorCredentials = { +export type ToMessage = (self: ErrorDetails) => string; + +export type ParseErrorCredentials = { code: ParseErrorCode, reasonCode: string, syntaxPlugin?: SyntaxPlugin, + + toMessage: ToMessage, }; + +const thisify = (f: (self: any, ...args: any[]) => T): ((...any[]) => T) => + function (...args: any[]): T { + return ((f(this, ...args): any): T); + }; + +const reflect = (keys: string[], last = keys.length - 1) => ({ + get: self => keys.reduce((object, key) => object[key], self), + set: (self, value) => + keys.reduce( + (item, key, i) => (i === last ? (item[key] = value) : item[key]), + self, + ), +}); + +const instantiate = ( + constructor: () => any, + properties: Object, + descriptors: Object, +) => + Object.keys(descriptors) + .map(key => [key, descriptors[key]]) + .filter(([, descriptor]) => !!descriptor) + .map(([key, descriptor]) => [ + key, + typeof descriptor === "string" + ? reflect(descriptor.split(".")) + : descriptor, + ]) + .reduce( + (instance, [key, descriptor]) => + Object.defineProperty(instance, key, { + configurable: true, + ...descriptor, + ...(descriptor.get && { get: thisify(descriptor.get) }), + ...(descriptor.set && { set: thisify(descriptor.set) }), + }), + Object.assign((new constructor(): T), properties), + ); + +export { instantiate }; diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 96ff0ddf7e66..a6318ef65905 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -67,7 +67,7 @@ import { newAsyncArrowScope, newExpressionScope, } from "../util/expression-scope"; -import { Errors, ParseError } from "../parse-error"; +import { Errors, type ParseError } from "../parse-error"; import { UnparenthesizedPipeBodyDescriptions } from "../parse-error/pipeline-operator-errors"; import { setInnerComments } from "./comments"; import { cloneIdentifier } from "./node"; diff --git a/packages/babel-parser/src/parser/util.js b/packages/babel-parser/src/parser/util.js index 5830618d197c..fbe51edefa16 100644 --- a/packages/babel-parser/src/parser/util.js +++ b/packages/babel-parser/src/parser/util.js @@ -18,7 +18,11 @@ import ProductionParameterHandler, { PARAM_AWAIT, PARAM, } from "../util/production-parameter"; -import { Errors, ParseError } from "../parse-error"; +import { + Errors, + type ParseError, + type ParseErrorConstructor, +} from "../parse-error"; /*:: import type ScopeHandler from "../util/scope"; */ @@ -97,11 +101,11 @@ export default class UtilParser extends Tokenizer { expectContextual( token: TokenType, - ParseErrorClass?: Class>, + toParseError?: ParseErrorConstructor, ): void { if (!this.eatContextual(token)) { - if (ParseErrorClass != null) { - throw this.raise(ParseErrorClass, { at: this.state.startLoc }); + if (toParseError != null) { + throw this.raise(toParseError, { at: this.state.startLoc }); } throw this.unexpected(null, token); } @@ -190,7 +194,7 @@ export default class UtilParser extends Tokenizer { } catch (error) { const failState = this.state; this.state = oldState; - if (error instanceof ParseError) { + if (error instanceof SyntaxError) { return { node: null, error, thrown: true, aborted: false, failState }; } if (error === abortSignal) { diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 38fd875af437..bcb69382e7b0 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -29,7 +29,7 @@ import { SCOPE_OTHER, } from "../../util/scopeflags"; import type { ExpressionErrors } from "../../parser/util"; -import { Errors, toParseErrorClasses } from "../../parse-error"; +import { Errors, ParseErrorEnum } from "../../parse-error"; import { cloneIdentifier } from "../../parser/node"; const reservedTypes = new Set([ @@ -53,7 +53,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 = toParseErrorClasses`flow`(_ => ({ +const FlowErrors = ParseErrorEnum`flow`(_ => ({ AmbiguousConditionalArrow: _( "Ambiguous expression: wrap the arrow functions in parentheses to disambiguate.", ), diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index 739eb5e16e40..41e0a761b378 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -17,10 +17,10 @@ import * as N from "../../types"; import { isIdentifierChar, isIdentifierStart } from "../../util/identifier"; import type { Position } from "../../util/location"; import { isNewLine } from "../../util/whitespace"; -import { Errors, toParseErrorClasses } from "../../parse-error"; +import { Errors, ParseErrorEnum } from "../../parse-error"; /* eslint sort-keys: "error" */ -const JsxErrors = toParseErrorClasses`jsx`(_ => ({ +const JsxErrors = ParseErrorEnum`jsx`(_ => ({ AttributeIsEmpty: _( "JSX attributes must only be assigned a non-empty expression.", ), diff --git a/packages/babel-parser/src/plugins/placeholders.js b/packages/babel-parser/src/plugins/placeholders.js index 9859e4a96d37..3b28fc968b65 100644 --- a/packages/babel-parser/src/plugins/placeholders.js +++ b/packages/babel-parser/src/plugins/placeholders.js @@ -5,7 +5,7 @@ import * as charCodes from "charcodes"; import { tokenLabelName, tt } from "../tokenizer/types"; import type Parser from "../parser"; import * as N from "../types"; -import { toParseErrorClasses } from "../parse-error"; +import { ParseErrorEnum } from "../parse-error"; export type PlaceholderTypes = | "Identifier" @@ -47,7 +47,7 @@ type NodeOf = $Switch< type MaybePlaceholder = NodeOf; // | Placeholder /* eslint sort-keys: "error" */ -const PlaceholderErrors = toParseErrorClasses`placeholders`(_ => ({ +const PlaceholderErrors = ParseErrorEnum`placeholders`(_ => ({ ClassNameIsRequired: _("A class name is required."), UnexpectedSpace: _("Unexpected space in placeholder."), })); diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 3a3fc29921b1..f6d0110779ff 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -35,7 +35,7 @@ import TypeScriptScopeHandler from "./scope"; import * as charCodes from "charcodes"; import type { ExpressionErrors } from "../../parser/util"; import { PARAM } from "../../util/production-parameter"; -import { Errors, toParseErrorClasses } from "../../parse-error"; +import { Errors, ParseErrorEnum } from "../../parse-error"; import { cloneIdentifier } from "../../parser/node"; type TsModifier = @@ -68,7 +68,7 @@ type ParsingContext = | "TypeParametersOrArguments"; /* eslint sort-keys: "error" */ -const TSErrors = toParseErrorClasses`typescript`(_ => ({ +const TSErrors = ParseErrorEnum`typescript`(_ => ({ AbstractMethodHasImplementation: _<{| methodName: string |}>( ({ methodName }) => `Method '${methodName}' cannot have an implementation because it is marked abstract.`, diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 3e6c098a330b..779fabd7b1f5 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -20,7 +20,12 @@ import { type TokenType, } from "./types"; import { type TokContext } from "./context"; -import { Errors, ParseError, type RaiseProperties } from "../parse-error"; +import { + Errors, + type ParseError, + type ParseErrorConstructor, + type RaiseProperties, +} from "../parse-error"; import { lineBreakG, isNewLine, @@ -28,7 +33,7 @@ import { skipWhiteSpace, } from "../util/whitespace"; import State from "./state"; -import type { LookaheadState, DeferredStrictErrorClass } from "./state"; +import type { LookaheadState, DeferredStrictError } from "./state"; const VALID_REGEX_FLAGS = new Set([ charCodes.lowercaseG, @@ -282,8 +287,8 @@ export default class Tokenizer extends CommentsParser { // after a "use strict" directive. Strict mode will be set at parse // time for any literals that occur after the next node of the strict // directive. - this.state.strictErrors.forEach(([ParseErrorClass, at]) => - this.raise(ParseErrorClass, { at }), + this.state.strictErrors.forEach(([toParseError, at]) => + this.raise(toParseError, { at }), ); this.state.strictErrors.clear(); } @@ -1534,15 +1539,15 @@ export default class Tokenizer extends CommentsParser { } recordStrictModeErrors( - ParseErrorClass: DeferredStrictErrorClass, + toParseError: DeferredStrictError, { at }: { at: Position }, ) { const index = at.index; if (this.state.strict && !this.state.strictErrors.has(index)) { - this.raise(ParseErrorClass, { at }); + this.raise(toParseError, { at }); } else { - this.state.strictErrors.set(index, [ParseErrorClass, at]); + this.state.strictErrors.set(index, [toParseError, at]); } } @@ -1753,13 +1758,13 @@ export default class Tokenizer extends CommentsParser { * @returns {(ParseError | empty)} * @memberof Tokenizer */ - raise>>( - ParseErrorClass: T, + raise( + toParseError: ParseErrorConstructor, raiseProperties: RaiseProperties, ): ParseError { const { at, ...details } = raiseProperties; const loc = at instanceof Position ? at : at.loc.start; - const error = new ParseErrorClass({ loc, details }); + const error = toParseError({ loc, details }); if (!this.options.errorRecovery) throw error; if (!this.isLookahead) this.state.errors.push(error); @@ -1778,8 +1783,8 @@ export default class Tokenizer extends CommentsParser { * @returns {(ParseError | empty)} * @memberof Tokenizer */ - raiseOverwrite>>( - ParseErrorClass: T, + raiseOverwrite( + toParseError: ParseErrorConstructor, raiseProperties: RaiseProperties, ): ParseError | empty { const { at, ...details } = raiseProperties; @@ -1789,13 +1794,13 @@ export default class Tokenizer extends CommentsParser { for (let i = errors.length - 1; i >= 0; i--) { const error = errors[i]; - if (error.pos === pos) { - return (errors[i] = new ParseErrorClass({ loc, details })); + if (error.loc.index === pos) { + return (errors[i] = toParseError({ loc, details })); } - if (error.pos < pos) break; + if (error.loc.index < pos) break; } - return this.raise(ParseErrorClass, raiseProperties); + return this.raise(toParseError, raiseProperties); } // updateContext is used by the jsx plugin diff --git a/packages/babel-parser/src/tokenizer/state.js b/packages/babel-parser/src/tokenizer/state.js index 1e3fe4d300cd..692da2c1c591 100644 --- a/packages/babel-parser/src/tokenizer/state.js +++ b/packages/babel-parser/src/tokenizer/state.js @@ -7,9 +7,9 @@ import { Position } from "../util/location"; import { types as ct, type TokContext } from "./context"; import { tt, type TokenType } from "./types"; -import { Errors, ParseError } from "../parse-error"; +import { Errors, type ParseError } from "../parse-error"; -export type DeferredStrictErrorClass = +export type DeferredStrictError = | typeof Errors.StrictNumericEscape | typeof Errors.StrictOctalLiteral; @@ -144,7 +144,7 @@ export default class State { // todo(JLHwung): set strictErrors to null and avoid recording string errors // after a non-directive is parsed - strictErrors: Map = new Map(); + strictErrors: Map = new Map(); // Tokens length in token store tokensLength: number = 0; diff --git a/packages/babel-parser/src/util/expression-scope.js b/packages/babel-parser/src/util/expression-scope.js index 3886c613731a..c38394f0b6ac 100644 --- a/packages/babel-parser/src/util/expression-scope.js +++ b/packages/babel-parser/src/util/expression-scope.js @@ -76,24 +76,22 @@ class ExpressionScope { } } -type ArrowHeadParsingParameterInitializerErrorClass = +type ArrowHeadParsingParameterInitializerError = | typeof Errors.AwaitExpressionFormalParameter | typeof Errors.YieldInParameter; -type ArrowHeadParsingDeclarationErrorClass = - | ArrowHeadParsingParameterInitializerErrorClass +type ArrowHeadParsingDeclarationError = + | ArrowHeadParsingParameterInitializerError | typeof Errors.InvalidParenthesizedAssignment | typeof Errors.AwaitBindingIdentifier; class ArrowHeadParsingScope extends ExpressionScope { - declarationErrors: Map< - number, - [ArrowHeadParsingDeclarationErrorClass, Position], - > = new Map(); + declarationErrors: Map = + new Map(); constructor(type: 1 | 2) { super(type); } - recordDeclarationError( + recordDeclarationError( ParsingErrorClass: T, { at }: { at: Position }, ) { @@ -105,7 +103,7 @@ class ArrowHeadParsingScope extends ExpressionScope { this.declarationErrors.delete(index); } iterateErrors( - iterator: ([ArrowHeadParsingDeclarationErrorClass, Position]) => void, + iterator: ([ArrowHeadParsingDeclarationError, Position]) => void, ) { this.declarationErrors.forEach(iterator); } @@ -137,7 +135,7 @@ export default class ExpressionScopeHandler { * @memberof ExpressionScopeHandler */ recordParameterInitializerError( - ParseErrorClass: ArrowHeadParsingParameterInitializerErrorClass, + toParseError: ArrowHeadParsingParameterInitializerError, { at: node }: { at: Node }, ): void { const origin = { at: node.loc.start }; @@ -147,7 +145,7 @@ export default class ExpressionScopeHandler { while (!scope.isCertainlyParameterDeclaration()) { if (scope.canBeArrowParameterDeclaration()) { /*:: invariant(scope instanceof ArrowHeadParsingScope) */ - scope.recordDeclarationError(ParseErrorClass, origin); + scope.recordDeclarationError(toParseError, origin); } else { /*:: invariant(scope.type == kExpression) */ // Type-Expression is the boundary where initializer error can populate to @@ -155,7 +153,7 @@ export default class ExpressionScopeHandler { } scope = stack[--i]; } - this.parser.raise(ParseErrorClass, origin); + this.parser.raise(toParseError, origin); } /** @@ -223,8 +221,8 @@ export default class ExpressionScopeHandler { const currentScope = stack[stack.length - 1]; if (!currentScope.canBeArrowParameterDeclaration()) return; /*:: invariant(currentScope instanceof ArrowHeadParsingScope) */ - currentScope.iterateErrors(([ParseErrorClass, loc]) => { - this.parser.raise(ParseErrorClass, { at: loc }); + currentScope.iterateErrors(([toParseError, loc]) => { + this.parser.raise(toParseError, { at: loc }); // iterate from parent scope let i = stack.length - 2; let scope = stack[i]; From 4f12b28b09b3e6d95a94190705986b78a42ea5d6 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 4 Mar 2022 12:56:37 -0500 Subject: [PATCH 092/104] Update packages/babel-parser/src/parse-error.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Huáng Jùnliàng --- packages/babel-parser/src/parse-error.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index db5609f2ad0e..5b58b76b6cd3 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -14,7 +14,7 @@ import { // `ParseErrorSpecification` interface below. We may choose to change to someday // give our errors their own full-blown class, but until then this allow us to // keep all the desirable properties of SyntaxErrors (like their name in stack -// traces, etc.), and also allows us to punt on any publically facing +// traces, etc.), and also allows us to punt on any publicly facing // class-hierarchy decisions until Babel 8. interface ParseErrorSpecification { // Look, these *could* be readonly, but then Flow complains when we initially From aa6240fe1e0ba0d70e022205e434b732bd04da1f Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 5 Mar 2022 13:23:05 -0800 Subject: [PATCH 093/104] Remove this comment since we don't need this argument anymore. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/statement.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index f418464f80c4..1ca7c1a5ef04 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -2538,7 +2538,6 @@ export default class StatementParser extends ExpressionParser { if (node.key.name !== "type") { this.raise(Errors.ModuleAttributeDifferentFromType, { at: node.key, - // key: node.key.name, }); } From dfafab936016e687c2089261a28d7c8dd9bdff9c Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 5 Mar 2022 15:42:47 -0800 Subject: [PATCH 094/104] Use this in toMessage and restore clone method. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 29 +++++++++++++++---- .../src/parse-error/credentials.js | 22 +++++++------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index 5b58b76b6cd3..ef5b5e190a53 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -53,22 +53,39 @@ function toParseErrorConstructor({ toMessage, ...properties }: ParseErrorCredentials): ParseErrorConstructor { - return ({ loc, details }: { loc: Position, details: ErrorDetails }) => - instantiate>( + type ConstructorArgument = { loc: Position, details: ErrorDetails }; + return function constructor({ loc, details }: ConstructorArgument) { + return instantiate>( SyntaxError, { ...properties, loc }, { + clone(overrides: { loc?: Position, details?: ErrorDetails } = {}) { + const loc = overrides.loc || {}; + return constructor({ + loc: new Position( + "line" in loc ? loc.line : this.loc.line, + "column" in loc ? loc.column : this.loc.column, + "index" in loc ? loc.index : this.loc.index, + ), + details: { ...this.details, ...overrides.details }, + }); + }, details: { value: details, enumerable: false }, message: { - get: ({ details, loc }) => - `${toMessage(details)} (${loc.line}:${loc.column})`, - set: (self, value) => - Object.defineProperty(self, "message", { value }), + get() { + return `${toMessage(this.details)} (${this.loc.line}:${ + this.loc.column + })`; + }, + set(value: string) { + Object.defineProperty(this, "message", { value }); + }, }, pos: "loc.index", missingPlugin: "missingPlugin" in details && "details.missingPlugin", }, ); + }; } // This part is tricky. You'll probably notice from the name of this function diff --git a/packages/babel-parser/src/parse-error/credentials.js b/packages/babel-parser/src/parse-error/credentials.js index 477240c24b40..c2890303f917 100644 --- a/packages/babel-parser/src/parse-error/credentials.js +++ b/packages/babel-parser/src/parse-error/credentials.js @@ -24,18 +24,16 @@ export type ParseErrorCredentials = { toMessage: ToMessage, }; -const thisify = (f: (self: any, ...args: any[]) => T): ((...any[]) => T) => - function (...args: any[]): T { - return ((f(this, ...args): any): T); - }; - const reflect = (keys: string[], last = keys.length - 1) => ({ - get: self => keys.reduce((object, key) => object[key], self), - set: (self, value) => + get() { + return keys.reduce((object, key) => object[key], this); + }, + set(value) { keys.reduce( (item, key, i) => (i === last ? (item[key] = value) : item[key]), - self, - ), + this, + ); + }, }); const instantiate = ( @@ -48,7 +46,9 @@ const instantiate = ( .filter(([, descriptor]) => !!descriptor) .map(([key, descriptor]) => [ key, - typeof descriptor === "string" + typeof descriptor === "function" + ? { value: descriptor, enumerable: false } + : typeof descriptor === "string" ? reflect(descriptor.split(".")) : descriptor, ]) @@ -57,8 +57,6 @@ const instantiate = ( Object.defineProperty(instance, key, { configurable: true, ...descriptor, - ...(descriptor.get && { get: thisify(descriptor.get) }), - ...(descriptor.set && { set: thisify(descriptor.set) }), }), Object.assign((new constructor(): T), properties), ); From 3803c077e790c3a5582fbff3d1803e6bb7bb2c99 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 6 Mar 2022 07:16:22 -0800 Subject: [PATCH 095/104] Make sure pos and missingPlugin are enumerable. Reviewed by @tolmasky. --- packages/babel-parser/src/parse-error.js | 7 +++++-- packages/babel-parser/src/parse-error/credentials.js | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/babel-parser/src/parse-error.js b/packages/babel-parser/src/parse-error.js index ef5b5e190a53..0cd9cbfc12d8 100644 --- a/packages/babel-parser/src/parse-error.js +++ b/packages/babel-parser/src/parse-error.js @@ -81,8 +81,11 @@ function toParseErrorConstructor({ Object.defineProperty(this, "message", { value }); }, }, - pos: "loc.index", - missingPlugin: "missingPlugin" in details && "details.missingPlugin", + pos: { reflect: "loc.index", enumerable: true }, + missingPlugin: "missingPlugin" in details && { + reflect: "details.missingPlugin", + enumerable: true, + }, }, ); }; diff --git a/packages/babel-parser/src/parse-error/credentials.js b/packages/babel-parser/src/parse-error/credentials.js index c2890303f917..1e758abb8731 100644 --- a/packages/babel-parser/src/parse-error/credentials.js +++ b/packages/babel-parser/src/parse-error/credentials.js @@ -48,8 +48,8 @@ const instantiate = ( key, typeof descriptor === "function" ? { value: descriptor, enumerable: false } - : typeof descriptor === "string" - ? reflect(descriptor.split(".")) + : typeof descriptor.reflect === "string" + ? { ...descriptor, ...reflect(descriptor.reflect.split(".")) } : descriptor, ]) .reduce( From bcc473eeb329d2363a416fae79ccf3a35d51b351 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 6 Mar 2022 07:42:27 -0800 Subject: [PATCH 096/104] Make typescript tests faster by checking the bom manually. Reviewed by @tolmasky. --- scripts/parser-tests/typescript/index.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/scripts/parser-tests/typescript/index.js b/scripts/parser-tests/typescript/index.js index 0d74d06f4e1e..0faed5d29aea 100644 --- a/scripts/parser-tests/typescript/index.js +++ b/scripts/parser-tests/typescript/index.js @@ -4,19 +4,10 @@ import { fileURLToPath } from "url"; import TestRunner from "../utils/parser-test-runner.js"; import ErrorCodes from "./error-codes.js"; -import { spawnSync } from "child_process"; const getEncoding = path => - spawnSync("file", ["--brief", "--mime-encoding", path]) - .stdout.toString() - .trim(); -const toNodeEncoding = fileEncoding => - ({ - "us-ascii": "utf-8", - "utf-8": "utf-8", - "utf-16le": "utf-16le", - "utf-16be": "utf-16be", - "iso-8859-1": "latin1", - }[fileEncoding] || false); + ({ fffe: "utf-16le", feff: "utf-16be" }[ + fs.readFileSync(path).slice(0, 2).toString("hex") + ] || "utf-8"); const ErrorCodeRegExp = new RegExp(ErrorCodes.join("|")); @@ -30,7 +21,7 @@ function* loadTests(dir) { if (encoding === "utf-16be" || encoding === "binary") continue; yield { name, - contents: fs.readFileSync(filename, toNodeEncoding(encoding)), + contents: fs.readFileSync(filename, encoding), }; } } From b6ca63f7315da559fcc34a6a3b06fd410ffab4df Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 6 Mar 2022 11:10:28 -0500 Subject: [PATCH 097/104] Update packages/babel-parser/src/parser/lval.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/lval.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index f35bfdee4198..2b29c0bc3a66 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -534,6 +534,7 @@ export default class LValParser extends NodeUtils { ParenthesizedExpression: "expression", ArrayPattern: "elements", ObjectPattern: "properties", + __proto__: null, }[type] || false ); } From f6280996497eb6038a5c704e3d236c3eecd89f4a Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 6 Mar 2022 11:10:55 -0500 Subject: [PATCH 098/104] Update packages/babel-parser/src/plugins/estree.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/plugins/estree.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index 71f9b9d4bc49..c09187f99d82 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -344,7 +344,7 @@ export default (superClass: Class): Class => } isValidLVal(type: string, ...rest) { - return { Property: "value" }[type] || super.isValidLVal(type, ...rest); + return type === "Property" ? "value" : super.isValidLVal(type, ...rest); } isAssignable(node: N.Node, isBinding?: boolean): boolean { From 38340d487a1fece03aefafd5e59401de167b51eb Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 6 Mar 2022 11:11:16 -0500 Subject: [PATCH 099/104] Update packages/babel-parser/src/plugins/typescript/index.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/plugins/typescript/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index f6d0110779ff..5beafc6c67c2 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -1611,7 +1611,7 @@ export default (superClass: Class): Class => ): ?N.TsInterfaceDeclaration { if (this.hasFollowingLineBreak()) return null; this.expectContextual(tt._interface); - if (properties.declare) node.declare = properties.declare; + if (properties.declare) node.declare = true; if (tokenIsIdentifier(this.state.type)) { node.id = this.parseIdentifier(); this.checkIdentifier(node.id, BIND_TS_INTERFACE); From 192cc854e90702a7337a45a79882c3fa8cad19b0 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 6 Mar 2022 11:11:33 -0500 Subject: [PATCH 100/104] Update packages/babel-parser/src/plugins/typescript/index.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/plugins/typescript/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 5beafc6c67c2..c4d6face01c1 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -1716,8 +1716,8 @@ export default (superClass: Class): Class => node: N.TsEnumDeclaration, properties: { const?: true, declare?: true } = {}, ): N.TsEnumDeclaration { - if (properties.const) node.const = properties.const; - if (properties.declare) node.declare = properties.declare; + if (properties.const) node.const = true; + if (properties.declare) node.declare = true; this.expectContextual(tt._enum); node.id = this.parseIdentifier(); this.checkIdentifier( From a1da9a6035c0b20471b903b110c404188aa8088e Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 6 Mar 2022 08:21:44 -0800 Subject: [PATCH 101/104] Clarify const enum initialization comment. Reviewed by @tolmasky. --- packages/babel-parser/src/plugins/typescript/index.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index c4d6face01c1..8ee4dd71139b 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -2580,9 +2580,12 @@ export default (superClass: Class): Class => // var and let aren't ever allowed initializers. // - // If a const declaration has no type annotation and is initiailized to a - // string literal, numeric literal, or (FIXME) enum reference, then it is - // allowed. + // If a const declaration has no type annotation and is initiailized to + // a string literal, numeric literal, or enum reference, then it is + // allowed. In an ideal world, we'd check whether init was *actually* an + // enum reference, but we allow anything that "could be" a literal enum + // in `isPossiblyLiteralEnum` since we don't have all the information + // that the typescript compiler has. if (kind !== "const" || !!id.typeAnnotation) { this.raise(TSErrors.InitializerNotAllowedInAmbientContext, { at: init, From c5f96fd8b3b43d16f4f51b2a251495d06726060c Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 6 Mar 2022 08:26:54 -0800 Subject: [PATCH 102/104] Fix Flow being angry because __proto__: null makes it think that the return type can be null. Reviewed by @tolmasky. --- packages/babel-parser/src/parser/lval.js | 14 ++++--- .../babel-parser/src/plugins/flow/index.js | 4 +- .../src/plugins/typescript/index.js | 38 +++++++++++-------- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 2b29c0bc3a66..aeecc15515a1 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -34,7 +34,8 @@ import { import { ExpressionErrors } from "./util"; import { Errors, type LValAncestor } from "../parse-error"; -const { isArray: ArrayIsArray } = Array; +const getOwn = (object, key) => + Object.hasOwnProperty.call(object, key) && object[key]; const unwrapParenthesizedExpression = (node: Node): Node => { return node.type === "ParenthesizedExpression" @@ -526,7 +527,7 @@ export default class LValParser extends NodeUtils { */ // eslint-disable-next-line no-unused-vars isValidLVal(type: string, isParenthesized: boolean, binding: BindingTypes) { - return ( + return getOwn( { AssignmentPattern: "left", RestElement: "argument", @@ -534,8 +535,8 @@ export default class LValParser extends NodeUtils { ParenthesizedExpression: "expression", ArrayPattern: "elements", ObjectPattern: "properties", - __proto__: null, - }[type] || false + }, + type, ); } @@ -644,7 +645,7 @@ export default class LValParser extends NodeUtils { return; } - const [key, isParenthesizedExpression] = ArrayIsArray(validity) + const [key, isParenthesizedExpression] = Array.isArray(validity) ? validity : [validity, type === "ParenthesizedExpression"]; const nextAncestor = @@ -654,6 +655,9 @@ export default class LValParser extends NodeUtils { ? expression : ancestor; + // Flow has difficulty tracking `key` and `expression`, but only if we use + // null-proto objects. If we use normal objects, everything works fine. + // $FlowIgnore for (const child of [].concat(expression[key])) { if (child) { this.checkLVal(child, { diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index bcb69382e7b0..bd7a0ec63717 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -2434,9 +2434,7 @@ export default (superClass: Class): Class => } isValidLVal(type: string, ...rest) { - return ( - { TypeCastExpression: true }[type] || super.isValidLVal(type, ...rest) - ); + return type === "TypeCastExpression" || super.isValidLVal(type, ...rest); } // parse class property type annotations diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 8ee4dd71139b..b7b9cc7b72a7 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -38,6 +38,9 @@ import { PARAM } from "../../util/production-parameter"; import { Errors, ParseErrorEnum } from "../../parse-error"; import { cloneIdentifier } from "../../parser/node"; +const getOwn = (object, key) => + Object.hasOwnProperty.call(object, key) && object[key]; + type TsModifier = | "readonly" | "abstract" @@ -3308,22 +3311,25 @@ export default (superClass: Class): Class => isValidLVal(type: string, isParenthesized: boolean, binding: BindingTypes) { return ( - { - // Allow "typecasts" to appear on the left of assignment expressions, - // because it may be in an arrow function. - // e.g. `const f = (foo: number = 0) => foo;` - TSTypeCastExpression: true, - TSParameterProperty: "parameter", - TSNonNullExpression: "expression", - TSAsExpression: (binding !== BIND_NONE || isParenthesized) && [ - "expression", - true, - ], - TSTypeAssertion: (binding !== BIND_NONE || isParenthesized) && [ - "expression", - true, - ], - }[type] || super.isValidLVal(type, isParenthesized, binding) + getOwn( + { + // Allow "typecasts" to appear on the left of assignment expressions, + // because it may be in an arrow function. + // e.g. `const f = (foo: number = 0) => foo;` + TSTypeCastExpression: true, + TSParameterProperty: "parameter", + TSNonNullExpression: "expression", + TSAsExpression: (binding !== BIND_NONE || isParenthesized) && [ + "expression", + true, + ], + TSTypeAssertion: (binding !== BIND_NONE || isParenthesized) && [ + "expression", + true, + ], + }, + type, + ) || super.isValidLVal(type, isParenthesized, binding) ); } From 06a3459a2cd6a6a819711b41bbcbbf87a59f22f0 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 6 Mar 2022 12:10:21 -0800 Subject: [PATCH 103/104] Don't automatically set DTS on the typescript plugin based on filename. Reviewed by @tolmasky. --- .../babel-parser/src/plugins/typescript/index.js | 12 +----------- scripts/parser-tests/typescript/index.js | 10 ++++++---- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index b7b9cc7b72a7..f4306aff76ab 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3598,17 +3598,7 @@ export default (superClass: Class): Class => } shouldParseAsAmbientContext(): boolean { - // If the user took the time to provide a filename and it has the ".d.ts" - // file extension, then assume they want ambient context parsing unless - // they explicitly specify otherwise. - const DTS = this.getPluginOption("typescript", "dts"); - const DTSTruthy = !!DTS; - const DTSFalse = DTS === false; - - return ( - DTSTruthy || - (!DTSFalse && !!this.filename && /\.d\.ts$/.test(this.filename)) - ); + return !!this.getPluginOption("typescript", "dts"); } parse() { diff --git a/scripts/parser-tests/typescript/index.js b/scripts/parser-tests/typescript/index.js index 0faed5d29aea..c18bf4d185bb 100644 --- a/scripts/parser-tests/typescript/index.js +++ b/scripts/parser-tests/typescript/index.js @@ -26,9 +26,6 @@ function* loadTests(dir) { } } -const plugins = ["typescript", "decorators-legacy", "importAssertions"]; -const pluginsWithJSX = [...plugins, "jsx"]; - const TSTestsPath = path.join(dirname, "../../../build/typescript/tests"); // Check if the baseline errors contain the codes that should also be thrown from babel-parser @@ -94,7 +91,12 @@ function toFiles(strictMode, contents, name) { sourceFilename, sourceType: "unambiguous", strictMode, - plugins: /\.(t|j)sx$/.test(sourceFilename) ? pluginsWithJSX : plugins, + plugins: [ + ["typescript", { dts: sourceFilename.endsWith(".d.ts") }], + "decorators-legacy", + "importAssertions", + /\.(t|j)sx$/.test(sourceFilename) && "jsx", + ].filter(plugin => !!plugin), })); } From 12faef007851606c6f27b28a8bce900e601bd0d4 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 7 Mar 2022 08:18:26 -0800 Subject: [PATCH 104/104] Fix duplicated export error. Reviewed by @tolmasky. --- packages/babel-parser/src/plugins/typescript/index.js | 4 +++- .../export-type-only-keyword/output.json | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index f4306aff76ab..0cd8a0a15759 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3715,7 +3715,9 @@ export default (superClass: Class): Class => } else if (tokenIsKeywordOrIdentifier(this.state.type)) { // { type something ...? } hasTypeSpecifier = true; - leftOfAs = this.parseIdentifier(); + leftOfAs = isImport + ? this.parseIdentifier() + : this.parseModuleExportName(); } if (hasTypeSpecifier && isInTypeOnlyImportExport) { this.raise( diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json index 2f9439c2bf9e..73d3206463af 100644 --- a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json @@ -3,7 +3,6 @@ "start":0,"end":34,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":2,"column":19,"index":34}}, "errors": [ "SyntaxError: Unexpected keyword 'if'. (1:6)", - "SyntaxError: Unexpected keyword 'if'. (2:14)", "SyntaxError: Unexpected keyword 'if'. (2:14)" ], "program": {