Skip to content

Commit

Permalink
Simplify parser errors creation (#15884)
Browse files Browse the repository at this point in the history
* Simplify parser errors creation

* Better type for `code`
  • Loading branch information
nicolo-ribaudo committed Aug 30, 2023
1 parent f64b04e commit 4013c54
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 122 deletions.
114 changes: 64 additions & 50 deletions packages/babel-parser/src/parse-error.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { Position } from "./util/location";
import {
instantiate,
ParseErrorCode,
type ParseErrorCredentials,
type ToMessage,
type SyntaxPlugin,
} from "./parse-error/credentials";
import type { Undone } from "./parser/node";
import type { Node } from "./types";

type SyntaxPlugin =
| "flow"
| "typescript"
| "jsx"
| "pipelineOperator"
| "placeholders";

type ParseErrorCode =
| "BABEL_PARSER_SYNTAX_ERROR"
| "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED";

// 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
Expand Down Expand Up @@ -47,6 +51,23 @@ export type ParseErrorConstructor<ErrorDetails> = (a: {
details: ErrorDetails;
}) => ParseError<ErrorDetails>;

type ToMessage<ErrorDetails> = (self: ErrorDetails) => string;

type ParseErrorCredentials<ErrorDetails> = {
code: string;
reasonCode: string;
syntaxPlugin?: SyntaxPlugin;
toMessage: ToMessage<ErrorDetails>;
};

function defineHidden(obj: object, key: string, value: unknown) {
Object.defineProperty(obj, key, {
enumerable: false,
configurable: true,
value,
});
}

function toParseErrorConstructor<ErrorDetails extends object>({
toMessage,
...properties
Expand All @@ -57,53 +78,48 @@ function toParseErrorConstructor<ErrorDetails extends object>({
};

return function constructor({ loc, details }: ConstructorArgument) {
return instantiate(
SyntaxError,
{ ...properties, loc },
{
clone(
overrides: {
loc?: Position;
details?: ErrorDetails;
} = {},
) {
const loc = (overrides.loc || {}) as Partial<Position>;
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(this: ConstructorArgument): string {
return `${toMessage(this.details)} (${this.loc.line}:${
this.loc.column
})`;
},
set(value: string) {
Object.defineProperty(this, "message", { value });
},
},
pos: { reflect: "loc.index", enumerable: true },
missingPlugin: "missingPlugin" in details && {
reflect: "details.missingPlugin",
enumerable: true,
},
const error = new SyntaxError();
Object.assign(error, properties, { loc, pos: loc.index });
if ("missingPlugin" in details) {
Object.assign(error, { missingPlugin: details.missingPlugin });
}

type Overrides = {
loc?: Position;
details?: ErrorDetails;
};
defineHidden(error, "clone", function clone(overrides: Overrides = {}) {
const { line, column, index } = overrides.loc ?? loc;
return constructor({
loc: new Position(line, column, index),
details: { ...details, ...overrides.details },
});
});

defineHidden(error, "details", details);

Object.defineProperty(error, "message", {
configurable: true,
get(this: ParseError<ErrorDetails>): string {
const message = `${toMessage(details)} (${loc.line}:${loc.column})`;
this.message = message;
return message;
},
) as ParseError<ErrorDetails>;
set(value: string) {
Object.defineProperty(this, "message", { value, writable: true });
},
});

return error as ParseError<ErrorDetails>;
};
}

type ParseErrorTemplate =
| string
| ToMessage<any>
| { message: string | ToMessage<any> };
| { message: string | ToMessage<any>; code?: ParseErrorCode };

type ParseErrorTemplates = { [reasonCode: string]: ParseErrorTemplate };
export type ParseErrorTemplates = { [reasonCode: string]: ParseErrorTemplate };

// This is the templated form of `ParseErrorEnum`.
//
Expand Down Expand Up @@ -156,7 +172,7 @@ export function ParseErrorEnum<T extends ParseErrorTemplates>(
// ErrorWithDynamicMessage: ({ type } : { type: string }) => `${type}`),
// ErrorWithOverriddenCodeAndOrReasonCode: {
// message: ({ type }: { type: string }) => `${type}`),
// code: ParseErrorCode.SourceTypeModuleError,
// code: "AN_ERROR_CODE",
// ...(BABEL_8_BREAKING ? { } : { reasonCode: "CustomErrorReasonCode" })
// }
// });
Expand Down Expand Up @@ -189,7 +205,7 @@ export function ParseErrorEnum(
const toMessage = typeof message === "string" ? () => message : message;

ParseErrorConstructors[reasonCode] = toParseErrorConstructor({
code: ParseErrorCode.SyntaxError,
code: "BABEL_PARSER_SYNTAX_ERROR",
reasonCode,
toMessage,
...(syntaxPlugin ? { syntaxPlugin } : {}),
Expand Down Expand Up @@ -217,5 +233,3 @@ export const Errors = {
};

export type { LValAncestor } from "./parse-error/standard-errors";

export * from "./parse-error/credentials";
65 changes: 0 additions & 65 deletions packages/babel-parser/src/parse-error/credentials.ts

This file was deleted.

10 changes: 6 additions & 4 deletions packages/babel-parser/src/parse-error/module-errors.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { ParseErrorCode } from "../parse-error";
import type { ParseErrorTemplates } from "../parse-error";

const code = "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED";

export default {
ImportMetaOutsideModule: {
message: `import.meta may appear only with 'sourceType: "module"'`,
code: ParseErrorCode.SourceTypeModuleError,
code,
},
ImportOutsideModule: {
message: `'import' and 'export' may appear only with 'sourceType: "module"'`,
code: ParseErrorCode.SourceTypeModuleError,
code,
},
};
} satisfies ParseErrorTemplates;
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ParseErrorTemplates } from "../parse-error";
import toNodeDescription from "./to-node-description";

export const UnparenthesizedPipeBodyDescriptions = new Set([
Expand Down Expand Up @@ -48,4 +49,4 @@ export default {
"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.',
};
} satisfies ParseErrorTemplates;
3 changes: 2 additions & 1 deletion packages/babel-parser/src/parse-error/standard-errors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ParseErrorTemplates } from "../parse-error";
import toNodeDescription from "./to-node-description";

export type LValAncestor =
Expand Down Expand Up @@ -312,4 +313,4 @@ export default {
YieldInParameter: "Yield expression is not allowed in formal parameters.",
ZeroDigitNumericSeparator:
"Numeric separator can not be used after leading 0.",
};
} satisfies ParseErrorTemplates;
4 changes: 3 additions & 1 deletion packages/babel-parser/src/parse-error/strict-mode-errors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ParseErrorTemplates } from "../parse-error";

export default {
StrictDelete: "Deleting local variable in strict mode.",

Expand Down Expand Up @@ -25,4 +27,4 @@ export default {
StrictOctalLiteral: "Legacy octal literals are not allowed in strict mode.",

StrictWith: "'with' in strict mode.",
};
} satisfies ParseErrorTemplates;

0 comments on commit 4013c54

Please sign in to comment.