Introduce parser error codes (#13033)
This commit is contained in:
parent
d0fcbfccdd
commit
0ee98139a6
8
packages/babel-parser/src/parser/error-codes.js
Normal file
8
packages/babel-parser/src/parser/error-codes.js
Normal file
@ -0,0 +1,8 @@
|
||||
// @flow
|
||||
|
||||
export const ErrorCodes = Object.freeze({
|
||||
SyntaxError: "BABEL_PARSER_SYNTAX_ERROR",
|
||||
SourceTypeModuleError: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED",
|
||||
});
|
||||
|
||||
export type ErrorCode = $Values<typeof ErrorCodes>;
|
||||
@ -1,4 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import { makeErrorTemplates, ErrorCodes } from "./error";
|
||||
|
||||
/* eslint sort-keys: "error" */
|
||||
|
||||
/**
|
||||
@ -6,7 +9,8 @@
|
||||
*/
|
||||
|
||||
// The Errors key follows https://cs.chromium.org/chromium/src/v8/src/common/message-template.h unless it does not exist
|
||||
export const ErrorMessages = Object.freeze({
|
||||
export const ErrorMessages = makeErrorTemplates(
|
||||
{
|
||||
AccessorIsGenerator: "A %0ter cannot be a generator",
|
||||
ArgumentsInClass:
|
||||
"'arguments' is only allowed in functions and class methods",
|
||||
@ -71,8 +75,6 @@ export const ErrorMessages = Object.freeze({
|
||||
ImportCallArity: "import() requires exactly %0",
|
||||
ImportCallNotNewExpression: "Cannot use new with import(...)",
|
||||
ImportCallSpreadArgument: "... is not allowed in import()",
|
||||
ImportMetaOutsideModule: `import.meta may appear only with 'sourceType: "module"'`,
|
||||
ImportOutsideModule: `'import' and 'export' may appear only with 'sourceType: "module"'`,
|
||||
InvalidBigIntLiteral: "Invalid BigIntLiteral",
|
||||
InvalidCodePoint: "Code point out of bounds",
|
||||
InvalidDecimal: "Invalid decimal",
|
||||
@ -160,7 +162,8 @@ export const ErrorMessages = Object.freeze({
|
||||
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'",
|
||||
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:
|
||||
@ -217,4 +220,14 @@ export const ErrorMessages = Object.freeze({
|
||||
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,
|
||||
);
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
/* eslint sort-keys: "error" */
|
||||
import { getLineInfo, type Position } from "../util/location";
|
||||
import CommentsParser from "./comments";
|
||||
import { type ErrorCode, ErrorCodes } from "./error-codes";
|
||||
|
||||
// This function is used to raise exceptions on parse errors. It
|
||||
// takes an offset integer (into the current `input`) to indicate
|
||||
@ -14,11 +15,43 @@ type ErrorContext = {
|
||||
loc: Position,
|
||||
missingPlugin?: Array<string>,
|
||||
code?: string,
|
||||
reasonCode?: String,
|
||||
};
|
||||
|
||||
export type ParsingError = SyntaxError & ErrorContext;
|
||||
|
||||
export { ErrorMessages as Errors } from "./error-message";
|
||||
export type ErrorTemplate = {
|
||||
code: ErrorCode,
|
||||
template: string,
|
||||
reasonCode: string,
|
||||
};
|
||||
export type ErrorTemplates = {
|
||||
[key: string]: ErrorTemplate,
|
||||
};
|
||||
|
||||
export function makeErrorTemplates(
|
||||
messages: {
|
||||
[key: string]: string,
|
||||
},
|
||||
code: ErrorCode,
|
||||
): ErrorTemplates {
|
||||
const templates: ErrorTemplates = {};
|
||||
Object.keys(messages).forEach(reasonCode => {
|
||||
templates[reasonCode] = {
|
||||
code,
|
||||
reasonCode,
|
||||
template: messages[reasonCode],
|
||||
};
|
||||
});
|
||||
return Object.freeze(templates);
|
||||
}
|
||||
|
||||
export { ErrorCodes };
|
||||
export {
|
||||
ErrorMessages as Errors,
|
||||
SourceTypeModuleErrorMessages as SourceTypeModuleErrors,
|
||||
} from "./error-message";
|
||||
|
||||
export type raiseFunction = (number, ErrorTemplate, ...any) => void;
|
||||
|
||||
export default class ParserError extends CommentsParser {
|
||||
// Forward-declaration: defined in tokenizer/index.js
|
||||
@ -37,8 +70,12 @@ export default class ParserError extends CommentsParser {
|
||||
return loc;
|
||||
}
|
||||
|
||||
raise(pos: number, errorTemplate: string, ...params: any): Error | empty {
|
||||
return this.raiseWithData(pos, undefined, errorTemplate, ...params);
|
||||
raise(
|
||||
pos: number,
|
||||
{ code, reasonCode, template }: ErrorTemplate,
|
||||
...params: any
|
||||
): Error | empty {
|
||||
return this.raiseWithData(pos, { code, reasonCode }, template, ...params);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,12 +92,12 @@ export default class ParserError extends CommentsParser {
|
||||
*/
|
||||
raiseOverwrite(
|
||||
pos: number,
|
||||
errorTemplate: string,
|
||||
{ code, template }: ErrorTemplate,
|
||||
...params: any
|
||||
): Error | empty {
|
||||
const loc = this.getLocationForPosition(pos);
|
||||
const message =
|
||||
errorTemplate.replace(/%(\d+)/g, (_, i: number) => params[i]) +
|
||||
template.replace(/%(\d+)/g, (_, i: number) => params[i]) +
|
||||
` (${loc.line}:${loc.column})`;
|
||||
if (this.options.errorRecovery) {
|
||||
const errors = this.state.errors;
|
||||
@ -73,7 +110,7 @@ export default class ParserError extends CommentsParser {
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._raise({ loc, pos }, message);
|
||||
return this._raise({ code, loc, pos }, message);
|
||||
}
|
||||
|
||||
raiseWithData(
|
||||
|
||||
@ -54,7 +54,7 @@ import {
|
||||
newAsyncArrowScope,
|
||||
newExpressionScope,
|
||||
} from "../util/expression-scope";
|
||||
import { Errors } from "./error";
|
||||
import { Errors, SourceTypeModuleErrors } from "./error";
|
||||
|
||||
/*::
|
||||
import type { SourceType } from "../options";
|
||||
@ -1358,11 +1358,7 @@ export default class ExpressionParser extends LValParser {
|
||||
|
||||
if (this.isContextual("meta")) {
|
||||
if (!this.inModule) {
|
||||
this.raiseWithData(
|
||||
id.start,
|
||||
{ code: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED" },
|
||||
Errors.ImportMetaOutsideModule,
|
||||
);
|
||||
this.raise(id.start, SourceTypeModuleErrors.ImportMetaOutsideModule);
|
||||
}
|
||||
this.sawUnambiguousESM = true;
|
||||
}
|
||||
@ -1524,14 +1520,14 @@ export default class ExpressionParser extends LValParser {
|
||||
const metaProp = this.parseMetaProperty(node, meta, "target");
|
||||
|
||||
if (!this.scope.inNonArrowFunction && !this.scope.inClass) {
|
||||
let error = Errors.UnexpectedNewTarget;
|
||||
const errorTemplate = { ...Errors.UnexpectedNewTarget };
|
||||
|
||||
if (this.hasPlugin("classProperties")) {
|
||||
error += " or class properties";
|
||||
errorTemplate.template += " or class properties";
|
||||
}
|
||||
|
||||
/* eslint-disable @babel/development-internal/dry-error-messages */
|
||||
this.raise(metaProp.start, error);
|
||||
this.raise(metaProp.start, errorTemplate);
|
||||
/* eslint-enable @babel/development-internal/dry-error-messages */
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import * as N from "../types";
|
||||
import { types as tt, type TokenType } from "../tokenizer/types";
|
||||
import ExpressionParser from "./expression";
|
||||
import { Errors } from "./error";
|
||||
import { Errors, SourceTypeModuleErrors } from "./error";
|
||||
import {
|
||||
isIdentifierChar,
|
||||
isIdentifierStart,
|
||||
@ -324,13 +324,7 @@ export default class StatementParser extends ExpressionParser {
|
||||
|
||||
assertModuleNodeAllowed(node: N.Node): void {
|
||||
if (!this.options.allowImportExportEverywhere && !this.inModule) {
|
||||
this.raiseWithData(
|
||||
node.start,
|
||||
{
|
||||
code: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED",
|
||||
},
|
||||
Errors.ImportOutsideModule,
|
||||
);
|
||||
this.raise(node.start, SourceTypeModuleErrors.ImportOutsideModule);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { types as tt, type TokenType } from "../tokenizer/types";
|
||||
import { types as tt, TokenType } from "../tokenizer/types";
|
||||
import Tokenizer from "../tokenizer";
|
||||
import State from "../tokenizer/state";
|
||||
import type { Node } from "../types";
|
||||
@ -13,7 +13,7 @@ import ProductionParameterHandler, {
|
||||
PARAM_AWAIT,
|
||||
PARAM,
|
||||
} from "../util/production-parameter";
|
||||
import { Errors } from "./error";
|
||||
import { Errors, type ErrorTemplate, ErrorCodes } from "./error";
|
||||
/*::
|
||||
import type ScopeHandler from "../util/scope";
|
||||
*/
|
||||
@ -91,8 +91,8 @@ export default class UtilParser extends Tokenizer {
|
||||
|
||||
// Asserts that following token is given contextual keyword.
|
||||
|
||||
expectContextual(name: string, message?: string): void {
|
||||
if (!this.eatContextual(name)) this.unexpected(null, message);
|
||||
expectContextual(name: string, template?: ErrorTemplate): void {
|
||||
if (!this.eatContextual(name)) this.unexpected(null, template);
|
||||
}
|
||||
|
||||
// Test whether a semicolon can be inserted at the current position.
|
||||
@ -142,7 +142,11 @@ export default class UtilParser extends Tokenizer {
|
||||
assertNoSpace(message: string = "Unexpected space."): void {
|
||||
if (this.state.start > this.state.lastTokEnd) {
|
||||
/* eslint-disable @babel/development-internal/dry-error-messages */
|
||||
this.raise(this.state.lastTokEnd, message);
|
||||
this.raise(this.state.lastTokEnd, {
|
||||
code: ErrorCodes.SyntaxError,
|
||||
reasonCode: "UnexpectedSpace",
|
||||
template: message,
|
||||
});
|
||||
/* eslint-enable @babel/development-internal/dry-error-messages */
|
||||
}
|
||||
}
|
||||
@ -152,10 +156,18 @@ export default class UtilParser extends Tokenizer {
|
||||
|
||||
unexpected(
|
||||
pos: ?number,
|
||||
messageOrType: string | TokenType = "Unexpected token",
|
||||
messageOrType: ErrorTemplate | TokenType = {
|
||||
code: ErrorCodes.SyntaxError,
|
||||
reasonCode: "UnexpectedToken",
|
||||
template: "Unexpected token",
|
||||
},
|
||||
): empty {
|
||||
if (typeof messageOrType !== "string") {
|
||||
messageOrType = `Unexpected token, expected "${messageOrType.label}"`;
|
||||
if (messageOrType instanceof TokenType) {
|
||||
messageOrType = {
|
||||
code: ErrorCodes.SyntaxError,
|
||||
reasonCode: "UnexpectedToken",
|
||||
template: `Unexpected token, expected "${messageOrType.label}"`,
|
||||
};
|
||||
}
|
||||
/* eslint-disable @babel/development-internal/dry-error-messages */
|
||||
throw this.raise(pos != null ? pos : this.state.start, messageOrType);
|
||||
|
||||
@ -25,7 +25,7 @@ import {
|
||||
SCOPE_OTHER,
|
||||
} from "../../util/scopeflags";
|
||||
import type { ExpressionErrors } from "../../parser/util";
|
||||
import { Errors } from "../../parser/error";
|
||||
import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error";
|
||||
|
||||
const reservedTypes = new Set([
|
||||
"_",
|
||||
@ -48,7 +48,8 @@ const reservedTypes = new Set([
|
||||
|
||||
/* eslint sort-keys: "error" */
|
||||
// The Errors key follows https://github.com/facebook/flow/blob/master/src/parser/parse_error.ml unless it does not exist
|
||||
const FlowErrors = Object.freeze({
|
||||
const FlowErrors = makeErrorTemplates(
|
||||
{
|
||||
AmbiguousConditionalArrow:
|
||||
"Ambiguous expression: wrap the arrow functions in parentheses to disambiguate.",
|
||||
AmbiguousDeclareModuleKind:
|
||||
@ -58,7 +59,8 @@ const FlowErrors = Object.freeze({
|
||||
"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",
|
||||
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:
|
||||
@ -132,7 +134,9 @@ const FlowErrors = Object.freeze({
|
||||
UnsupportedStatementInDeclareModule:
|
||||
"Only declares and type imports are allowed inside declare module",
|
||||
UnterminatedFlowComment: "Unterminated flow-comment",
|
||||
});
|
||||
},
|
||||
/* code */ ErrorCodes.SyntaxError,
|
||||
);
|
||||
/* eslint-disable sort-keys */
|
||||
|
||||
function isEsModuleType(bodyElement: N.Node): boolean {
|
||||
|
||||
@ -14,13 +14,14 @@ import * as N from "../../types";
|
||||
import { isIdentifierChar, isIdentifierStart } from "../../util/identifier";
|
||||
import type { Position } from "../../util/location";
|
||||
import { isNewLine } from "../../util/whitespace";
|
||||
import { Errors } from "../../parser/error";
|
||||
import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error";
|
||||
|
||||
const HEX_NUMBER = /^[\da-fA-F]+$/;
|
||||
const DECIMAL_NUMBER = /^\d+$/;
|
||||
|
||||
/* eslint sort-keys: "error" */
|
||||
const JsxErrors = Object.freeze({
|
||||
const JsxErrors = makeErrorTemplates(
|
||||
{
|
||||
AttributeIsEmpty:
|
||||
"JSX attributes must only be assigned a non-empty expression",
|
||||
MissingClosingTagElement: "Expected corresponding JSX closing tag for <%0>",
|
||||
@ -32,7 +33,9 @@ const JsxErrors = Object.freeze({
|
||||
UnterminatedJsxContent: "Unterminated JSX contents",
|
||||
UnwrappedAdjacentJSXElements:
|
||||
"Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>?",
|
||||
});
|
||||
},
|
||||
/* code */ ErrorCodes.SyntaxError,
|
||||
);
|
||||
/* eslint-disable sort-keys */
|
||||
|
||||
// Be aware that this file is always executed and not only when the plugin is enabled.
|
||||
@ -133,10 +136,11 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
const htmlEntity =
|
||||
ch === charCodes.rightCurlyBrace ? "}" : ">";
|
||||
const char = this.input[this.state.pos];
|
||||
this.raise(
|
||||
this.state.pos,
|
||||
`Unexpected token \`${char}\`. Did you mean \`${htmlEntity}\` or \`{'${char}'}\`?`,
|
||||
);
|
||||
this.raise(this.state.pos, {
|
||||
code: ErrorCodes.SyntaxError,
|
||||
reasonCode: "UnexpectedToken",
|
||||
template: `Unexpected token \`${char}\`. Did you mean \`${htmlEntity}\` or \`{'${char}'}\`?`,
|
||||
});
|
||||
}
|
||||
/* falls through */
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import * as charCodes from "charcodes";
|
||||
import { types as tt, TokenType } from "../tokenizer/types";
|
||||
import type Parser from "../parser";
|
||||
import * as N from "../types";
|
||||
import { makeErrorTemplates, ErrorCodes } from "../parser/error";
|
||||
|
||||
tt.placeholder = new TokenType("%%", { startsExpr: true });
|
||||
|
||||
@ -47,6 +48,13 @@ type NodeOf<T: PlaceholderTypes> = $Switch<
|
||||
// the substituted nodes.
|
||||
type MaybePlaceholder<T: PlaceholderTypes> = NodeOf<T>; // | Placeholder<T>
|
||||
|
||||
const PlaceHolderErrors = makeErrorTemplates(
|
||||
{
|
||||
ClassNameIsRequired: "A class name is required",
|
||||
},
|
||||
/* code */ ErrorCodes.SyntaxError,
|
||||
);
|
||||
|
||||
export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
class extends superClass {
|
||||
parsePlaceholder<T: PlaceholderTypes>(
|
||||
@ -240,7 +248,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
node.body = this.finishPlaceholder(placeholder, "ClassBody");
|
||||
return this.finishNode(node, type);
|
||||
} else {
|
||||
this.unexpected(null, "A class name is required");
|
||||
this.unexpected(null, PlaceHolderErrors.ClassNameIsRequired);
|
||||
}
|
||||
} else {
|
||||
this.parseClassId(node, isStatement, optionalId);
|
||||
|
||||
@ -29,7 +29,12 @@ import TypeScriptScopeHandler from "./scope";
|
||||
import * as charCodes from "charcodes";
|
||||
import type { ExpressionErrors } from "../../parser/util";
|
||||
import { PARAM } from "../../util/production-parameter";
|
||||
import { Errors } from "../../parser/error";
|
||||
import {
|
||||
Errors,
|
||||
makeErrorTemplates,
|
||||
type ErrorTemplate,
|
||||
ErrorCodes,
|
||||
} from "../../parser/error";
|
||||
|
||||
type TsModifier =
|
||||
| "readonly"
|
||||
@ -60,7 +65,8 @@ type ParsingContext =
|
||||
| "TypeParametersOrArguments";
|
||||
|
||||
/* eslint sort-keys: "error" */
|
||||
const TSErrors = Object.freeze({
|
||||
const TSErrors = makeErrorTemplates(
|
||||
{
|
||||
AbstractMethodHasImplementation:
|
||||
"Method '%0' cannot have an implementation because it is marked abstract.",
|
||||
ClassMethodHasDeclare: "Class methods cannot have the 'declare' modifier",
|
||||
@ -86,8 +92,10 @@ const TSErrors = Object.freeze({
|
||||
"Index signatures cannot have an accessibility modifier ('%0')",
|
||||
IndexSignatureHasDeclare:
|
||||
"Index signatures cannot have the 'declare' modifier",
|
||||
IndexSignatureHasStatic: "Index signatures cannot have the 'static' modifier",
|
||||
InvalidModifierOnTypeMember: "'%0' modifier cannot appear on a type member.",
|
||||
IndexSignatureHasStatic:
|
||||
"Index signatures cannot have the 'static' modifier",
|
||||
InvalidModifierOnTypeMember:
|
||||
"'%0' modifier cannot appear on a type member.",
|
||||
InvalidTupleMemberLabel:
|
||||
"Tuple members must be labeled with a simple identifier.",
|
||||
MixedLabeledAndUnlabeledElements:
|
||||
@ -108,19 +116,24 @@ const TSErrors = Object.freeze({
|
||||
"'readonly' modifier can only appear on a property declaration or index signature.",
|
||||
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.",
|
||||
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.",
|
||||
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,
|
||||
);
|
||||
/* eslint-disable sort-keys */
|
||||
|
||||
// Doesn't handle "void" or "null" because those are keywords, not identifiers.
|
||||
@ -217,7 +230,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
},
|
||||
allowedModifiers: TsModifier[],
|
||||
disallowedModifiers?: TsModifier[],
|
||||
errorTemplate?: string,
|
||||
errorTemplate?: ErrorTemplate,
|
||||
): void {
|
||||
for (;;) {
|
||||
const startPos = this.state.start;
|
||||
@ -2098,7 +2111,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
) {
|
||||
this.raise(
|
||||
importNode.start,
|
||||
"A type-only import can specify a default import or named bindings, but not both.",
|
||||
TSErrors.TypeImportCannotSpecifyDefaultAndNamed,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import * as charCodes from "charcodes";
|
||||
import { isIdentifierStart, isIdentifierChar } from "../util/identifier";
|
||||
import { types as tt, keywords as keywordTypes, type TokenType } from "./types";
|
||||
import { type TokContext, types as ct } from "./context";
|
||||
import ParserErrors, { Errors } from "../parser/error";
|
||||
import ParserErrors, { Errors, type ErrorTemplate } from "../parser/error";
|
||||
import { SourceLocation } from "../util/location";
|
||||
import {
|
||||
lineBreak,
|
||||
@ -115,7 +115,7 @@ export default class Tokenizer extends ParserErrors {
|
||||
// parser/util.js
|
||||
/*::
|
||||
+hasPrecedingLineBreak: () => boolean;
|
||||
+unexpected: (pos?: ?number, messageOrType?: string | TokenType) => empty;
|
||||
+unexpected: (pos?: ?number, messageOrType?: ErrorTemplate | TokenType) => empty;
|
||||
+expectPlugin: (name: string, pos?: ?number) => true;
|
||||
*/
|
||||
|
||||
@ -1321,7 +1321,7 @@ export default class Tokenizer extends ParserErrors {
|
||||
}
|
||||
}
|
||||
|
||||
recordStrictModeErrors(pos: number, message: string) {
|
||||
recordStrictModeErrors(pos: number, message: ErrorTemplate) {
|
||||
if (this.state.strict && !this.state.strictErrors.has(pos)) {
|
||||
this.raise(pos, message);
|
||||
} else {
|
||||
|
||||
@ -6,7 +6,7 @@ import { Position } from "../util/location";
|
||||
|
||||
import { types as ct, type TokContext } from "./context";
|
||||
import { types as tt, type TokenType } from "./types";
|
||||
import type { ParsingError } from "../parser/error";
|
||||
import type { ParsingError, ErrorTemplate } from "../parser/error";
|
||||
|
||||
type TopicContextState = {
|
||||
// When a topic binding has been currently established,
|
||||
@ -147,7 +147,7 @@ export default class State {
|
||||
|
||||
// todo(JLHwung): set strictErrors to null and avoid recording string errors
|
||||
// after a non-directive is parsed
|
||||
strictErrors: Map<number, string> = new Map();
|
||||
strictErrors: Map<number, ErrorTemplate> = new Map();
|
||||
|
||||
// Names of exports store. `default` is stored as a name for both
|
||||
// `export default foo;` and `export { foo as default };`.
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
CLASS_ELEMENT_FLAG_STATIC,
|
||||
type ClassElementTypes,
|
||||
} from "./scopeflags";
|
||||
import { Errors } from "../parser/error";
|
||||
import { Errors, type raiseFunction } from "../parser/error";
|
||||
|
||||
export class ClassScope {
|
||||
// A list of private named declared in the current class
|
||||
@ -19,8 +19,6 @@ export class ClassScope {
|
||||
undefinedPrivateNames: Map<string, number> = new Map();
|
||||
}
|
||||
|
||||
type raiseFunction = (number, string, ...any) => void;
|
||||
|
||||
export default class ClassScopeHandler {
|
||||
stack: Array<ClassScope> = [];
|
||||
declare raise: raiseFunction;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import type { ErrorTemplate, raiseFunction } from "../parser/error";
|
||||
|
||||
/*:: declare var invariant; */
|
||||
/**
|
||||
* @module util/expression-scope
|
||||
@ -52,8 +54,6 @@ const kExpression = 0,
|
||||
|
||||
type ExpressionScopeType = 0 | 1 | 2 | 3;
|
||||
|
||||
type raiseFunction = (number, string, ...any) => void;
|
||||
|
||||
class ExpressionScope {
|
||||
type: ExpressionScopeType;
|
||||
|
||||
@ -74,17 +74,17 @@ class ExpressionScope {
|
||||
}
|
||||
|
||||
class ArrowHeadParsingScope extends ExpressionScope {
|
||||
errors: Map</* pos */ number, /* message */ string> = new Map();
|
||||
errors: Map</* pos */ number, /* message */ ErrorTemplate> = new Map();
|
||||
constructor(type: 1 | 2) {
|
||||
super(type);
|
||||
}
|
||||
recordDeclarationError(pos: number, message: string) {
|
||||
this.errors.set(pos, message);
|
||||
recordDeclarationError(pos: number, template: ErrorTemplate) {
|
||||
this.errors.set(pos, template);
|
||||
}
|
||||
clearDeclarationError(pos: number) {
|
||||
this.errors.delete(pos);
|
||||
}
|
||||
iterateErrors(iterator: (message: string, pos: number) => void) {
|
||||
iterateErrors(iterator: (template: ErrorTemplate, pos: number) => void) {
|
||||
this.errors.forEach(iterator);
|
||||
}
|
||||
}
|
||||
@ -110,17 +110,17 @@ export default class ExpressionScopeHandler {
|
||||
* otherwise it will be recorded to any ancestry MaybeArrowParameterDeclaration and
|
||||
* MaybeAsyncArrowParameterDeclaration scope until an Expression scope is seen.
|
||||
* @param {number} pos Error position
|
||||
* @param {string} message Error message
|
||||
* @param {ErrorTemplate} template Error template
|
||||
* @memberof ExpressionScopeHandler
|
||||
*/
|
||||
recordParameterInitializerError(pos: number, message: string): void {
|
||||
recordParameterInitializerError(pos: number, template: ErrorTemplate): void {
|
||||
const { stack } = this;
|
||||
let i = stack.length - 1;
|
||||
let scope: ExpressionScope = stack[i];
|
||||
while (!scope.isCertainlyParameterDeclaration()) {
|
||||
if (scope.canBeArrowParameterDeclaration()) {
|
||||
/*:: invariant(scope instanceof ArrowHeadParsingScope) */
|
||||
scope.recordDeclarationError(pos, message);
|
||||
scope.recordDeclarationError(pos, template);
|
||||
} else {
|
||||
/*:: invariant(scope.type == kExpression) */
|
||||
// Type-Expression is the boundary where initializer error can populate to
|
||||
@ -129,7 +129,7 @@ export default class ExpressionScopeHandler {
|
||||
scope = stack[--i];
|
||||
}
|
||||
/* eslint-disable @babel/development-internal/dry-error-messages */
|
||||
this.raise(pos, message);
|
||||
this.raise(pos, template);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,18 +149,21 @@ export default class ExpressionScopeHandler {
|
||||
* arrow scope because when we finish parsing `( [(a) = []] = [] )`, it is an unambiguous assignment
|
||||
* expression and can not be cast to pattern
|
||||
* @param {number} pos
|
||||
* @param {string} message
|
||||
* @param {ErrorTemplate} template
|
||||
* @returns {void}
|
||||
* @memberof ExpressionScopeHandler
|
||||
*/
|
||||
recordParenthesizedIdentifierError(pos: number, message: string): void {
|
||||
recordParenthesizedIdentifierError(
|
||||
pos: number,
|
||||
template: ErrorTemplate,
|
||||
): void {
|
||||
const { stack } = this;
|
||||
const scope: ExpressionScope = stack[stack.length - 1];
|
||||
if (scope.isCertainlyParameterDeclaration()) {
|
||||
this.raise(pos, message);
|
||||
this.raise(pos, template);
|
||||
} else if (scope.canBeArrowParameterDeclaration()) {
|
||||
/*:: invariant(scope instanceof ArrowHeadParsingScope) */
|
||||
scope.recordDeclarationError(pos, message);
|
||||
scope.recordDeclarationError(pos, template);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -172,17 +175,17 @@ export default class ExpressionScopeHandler {
|
||||
* Errors will be recorded to any ancestry MaybeAsyncArrowParameterDeclaration
|
||||
* scope until an Expression scope is seen.
|
||||
* @param {number} pos
|
||||
* @param {string} message
|
||||
* @param {ErrorTemplate} template
|
||||
* @memberof ExpressionScopeHandler
|
||||
*/
|
||||
recordAsyncArrowParametersError(pos: number, message: string): void {
|
||||
recordAsyncArrowParametersError(pos: number, template: ErrorTemplate): void {
|
||||
const { stack } = this;
|
||||
let i = stack.length - 1;
|
||||
let scope: ExpressionScope = stack[i];
|
||||
while (scope.canBeArrowParameterDeclaration()) {
|
||||
if (scope.type === kMaybeAsyncArrowParameterDeclaration) {
|
||||
/*:: invariant(scope instanceof ArrowHeadParsingScope) */
|
||||
scope.recordDeclarationError(pos, message);
|
||||
scope.recordDeclarationError(pos, template);
|
||||
}
|
||||
scope = stack[--i];
|
||||
}
|
||||
@ -193,9 +196,9 @@ export default class ExpressionScopeHandler {
|
||||
const currentScope = stack[stack.length - 1];
|
||||
if (!currentScope.canBeArrowParameterDeclaration()) return;
|
||||
/*:: invariant(currentScope instanceof ArrowHeadParsingScope) */
|
||||
currentScope.iterateErrors((message, pos) => {
|
||||
currentScope.iterateErrors((template, pos) => {
|
||||
/* eslint-disable @babel/development-internal/dry-error-messages */
|
||||
this.raise(pos, message);
|
||||
this.raise(pos, template);
|
||||
// iterate from parent scope
|
||||
let i = stack.length - 2;
|
||||
let scope = stack[i];
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
type BindingTypes,
|
||||
} from "./scopeflags";
|
||||
import * as N from "../types";
|
||||
import { Errors } from "../parser/error";
|
||||
import { Errors, type raiseFunction } from "../parser/error";
|
||||
|
||||
// Start an AST node, attaching a start offset.
|
||||
export class Scope {
|
||||
@ -34,8 +34,6 @@ export class Scope {
|
||||
}
|
||||
}
|
||||
|
||||
type raiseFunction = (number, string, ...any) => void;
|
||||
|
||||
// The functions in this module keep track of declared variables in the
|
||||
// current scope in order to detect duplicate variable names.
|
||||
export default class ScopeHandler<IScope: Scope = Scope> {
|
||||
|
||||
21
packages/babel-parser/test/error-codes.js
Normal file
21
packages/babel-parser/test/error-codes.js
Normal file
@ -0,0 +1,21 @@
|
||||
import { parse } from "../lib";
|
||||
|
||||
describe("error codes", function () {
|
||||
it("raises an error with BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED and reasonCode", function () {
|
||||
const code = `import "foo"`;
|
||||
const { errors } = parse(code, {
|
||||
errorRecovery: true,
|
||||
sourceType: "script",
|
||||
});
|
||||
const error = errors[0];
|
||||
expect(error.code).toBe("BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED");
|
||||
expect(error.reasonCode).toBe("ImportOutsideModule");
|
||||
});
|
||||
it("raises an error with BABEL_PARSER_SYNTAX_ERROR and reasonCode", function () {
|
||||
const code = `a b`;
|
||||
const { errors } = parse(code, { errorRecovery: true });
|
||||
const error = errors[0];
|
||||
expect(error.code).toBe("BABEL_PARSER_SYNTAX_ERROR");
|
||||
expect(error.reasonCode).toBe("MissingSemicolon");
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user