Skip to content

Commit

Permalink
Introduce parser error codes (#13033)
Browse files Browse the repository at this point in the history
  • Loading branch information
sosukesuzuki authored and JLHwung committed Apr 16, 2021
1 parent 0cfe3dc commit f01020d
Show file tree
Hide file tree
Showing 16 changed files with 551 additions and 442 deletions.
8 changes: 8 additions & 0 deletions packages/babel-parser/src/parser/error-codes.js
@@ -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>;
437 changes: 225 additions & 212 deletions packages/babel-parser/src/parser/error-message.js

Large diffs are not rendered by default.

51 changes: 44 additions & 7 deletions packages/babel-parser/src/parser/error.js
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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);
}

/**
Expand All @@ -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;
Expand All @@ -73,7 +110,7 @@ export default class ParserError extends CommentsParser {
}
}
}
return this._raise({ loc, pos }, message);
return this._raise({ code, loc, pos }, message);
}

raiseWithData(
Expand Down
14 changes: 5 additions & 9 deletions packages/babel-parser/src/parser/expression.js
Expand Up @@ -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";
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 */
}

Expand Down
10 changes: 2 additions & 8 deletions packages/babel-parser/src/parser/statement.js
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
}

Expand Down
28 changes: 20 additions & 8 deletions packages/babel-parser/src/parser/util.js
@@ -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";
Expand All @@ -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";
*/
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 */
}
}
Expand All @@ -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);
Expand Down

0 comments on commit f01020d

Please sign in to comment.