mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Refactor CompilerErrorDetails
- Made most static methods on CompilerError take a single options object as an argument. With the exception of invariant which takes a condition and an options object. - Added a new `suggestions` field on CompilerErrorDetail, which we'll use to provide eslint suggestions - Updated eslint-plugin-react-forget to handle suggestions
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
import type { SourceLocation } from "./HIR";
|
||||
import type { ExtractClassProperties } from "./Utils/types";
|
||||
import { assertExhaustive } from "./Utils/utils";
|
||||
|
||||
export enum ErrorSeverity {
|
||||
@@ -33,29 +32,61 @@ export enum ErrorSeverity {
|
||||
Invariant = "Invariant",
|
||||
}
|
||||
|
||||
export type CompilerErrorOptions = {
|
||||
export enum CompilerSuggestionOperation {
|
||||
InsertBefore,
|
||||
InsertAfter,
|
||||
Remove,
|
||||
Replace,
|
||||
}
|
||||
export type CompilerSuggestion =
|
||||
| {
|
||||
op:
|
||||
| CompilerSuggestionOperation.InsertAfter
|
||||
| CompilerSuggestionOperation.InsertBefore
|
||||
| CompilerSuggestionOperation.Replace;
|
||||
range: [number, number];
|
||||
description: string;
|
||||
text: string;
|
||||
}
|
||||
| {
|
||||
op: CompilerSuggestionOperation.Remove;
|
||||
range: [number, number];
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type CompilerErrorDetailOptions = {
|
||||
reason: string;
|
||||
description?: string | null | undefined;
|
||||
severity: ErrorSeverity;
|
||||
loc: SourceLocation | null;
|
||||
suggestions: Array<CompilerSuggestion> | null;
|
||||
};
|
||||
type CompilerErrorDetailOptions = ExtractClassProperties<CompilerErrorDetail>;
|
||||
|
||||
/**
|
||||
* Each bailout or invariant in HIR lowering creates an {@link CompilerErrorDetail}, which is then
|
||||
* aggregated into a single {@link CompilerError} later.
|
||||
*/
|
||||
export class CompilerErrorDetail {
|
||||
reason: string;
|
||||
description: string | null;
|
||||
severity: ErrorSeverity;
|
||||
loc: SourceLocation | null;
|
||||
options: CompilerErrorDetailOptions;
|
||||
|
||||
constructor(options: CompilerErrorDetailOptions) {
|
||||
this.reason = options.reason;
|
||||
this.description = options.description;
|
||||
this.severity = options.severity;
|
||||
this.loc = options.loc;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
get reason(): CompilerErrorDetailOptions["reason"] {
|
||||
return this.options.reason;
|
||||
}
|
||||
get description(): CompilerErrorDetailOptions["description"] {
|
||||
return this.options.description;
|
||||
}
|
||||
get severity(): CompilerErrorDetailOptions["severity"] {
|
||||
return this.options.severity;
|
||||
}
|
||||
get loc(): CompilerErrorDetailOptions["loc"] {
|
||||
return this.options.loc;
|
||||
}
|
||||
get suggestions(): CompilerErrorDetailOptions["suggestions"] {
|
||||
return this.options.suggestions;
|
||||
}
|
||||
|
||||
printErrorMessage(): string {
|
||||
@@ -79,17 +110,13 @@ export class CompilerError extends Error {
|
||||
|
||||
static invariant(
|
||||
condition: unknown,
|
||||
reason: string,
|
||||
loc: SourceLocation | null,
|
||||
description: string | null = null
|
||||
options: Omit<CompilerErrorDetailOptions, "severity">
|
||||
): asserts condition {
|
||||
if (!condition) {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
description,
|
||||
loc,
|
||||
reason,
|
||||
...options,
|
||||
severity: ErrorSeverity.Invariant,
|
||||
})
|
||||
);
|
||||
@@ -97,34 +124,21 @@ export class CompilerError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
static todo(
|
||||
reason: string,
|
||||
loc: SourceLocation | null,
|
||||
description: string | null = null
|
||||
): never {
|
||||
static todo(options: Omit<CompilerErrorDetailOptions, "severity">): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
description,
|
||||
loc,
|
||||
reason,
|
||||
severity: ErrorSeverity.Todo,
|
||||
})
|
||||
new CompilerErrorDetail({ ...options, severity: ErrorSeverity.Todo })
|
||||
);
|
||||
throw errors;
|
||||
}
|
||||
|
||||
static invalidInput(
|
||||
reason: string,
|
||||
loc: SourceLocation | null,
|
||||
description: string | null = null
|
||||
options: Omit<CompilerErrorDetailOptions, "severity">
|
||||
): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
description,
|
||||
loc,
|
||||
reason,
|
||||
...options,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
})
|
||||
);
|
||||
@@ -132,16 +146,12 @@ export class CompilerError extends Error {
|
||||
}
|
||||
|
||||
static invalidReact(
|
||||
reason: string,
|
||||
loc: SourceLocation | null,
|
||||
description: string | null = null
|
||||
options: Omit<CompilerErrorDetailOptions, "severity">
|
||||
): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
description,
|
||||
loc,
|
||||
reason,
|
||||
...options,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
})
|
||||
);
|
||||
@@ -149,16 +159,12 @@ export class CompilerError extends Error {
|
||||
}
|
||||
|
||||
static invalidConfig(
|
||||
reason: string,
|
||||
loc: SourceLocation | null,
|
||||
description: string | null = null
|
||||
options: Omit<CompilerErrorDetailOptions, "severity">
|
||||
): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
description,
|
||||
loc,
|
||||
reason,
|
||||
...options,
|
||||
severity: ErrorSeverity.InvalidConfig,
|
||||
})
|
||||
);
|
||||
@@ -180,11 +186,12 @@ export class CompilerError extends Error {
|
||||
return this.details.map((detail) => detail.toString()).join("\n\n");
|
||||
}
|
||||
|
||||
push(options: CompilerErrorOptions): CompilerErrorDetail {
|
||||
push(options: CompilerErrorDetailOptions): CompilerErrorDetail {
|
||||
const detail = new CompilerErrorDetail({
|
||||
reason: options.reason,
|
||||
description: options.description ?? null,
|
||||
severity: options.severity,
|
||||
suggestions: options.suggestions,
|
||||
loc: typeof options.loc === "symbol" ? null : options.loc,
|
||||
});
|
||||
return this.pushErrorDetail(detail);
|
||||
|
||||
@@ -53,11 +53,12 @@ export function compileProgram(
|
||||
return;
|
||||
}
|
||||
|
||||
CompilerError.invariant(
|
||||
fn.node.id != null,
|
||||
"FunctionDeclaration must have a name",
|
||||
fn.node.loc ?? GeneratedSource
|
||||
);
|
||||
CompilerError.invariant(fn.node.id != null, {
|
||||
reason: "FunctionDeclaration must have a name",
|
||||
description: null,
|
||||
loc: fn.node.loc ?? GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
const originalIdent = fn.node.id;
|
||||
|
||||
if (pass.opts.gating != null) {
|
||||
@@ -65,11 +66,12 @@ export function compileProgram(
|
||||
fn.node.id = addSuffix(fn.node.id, "_uncompiled");
|
||||
|
||||
// Rename and append compiled function
|
||||
CompilerError.invariant(
|
||||
compiled.id != null,
|
||||
"FunctionDeclaration must produce a name",
|
||||
fn.node.loc ?? GeneratedSource
|
||||
);
|
||||
CompilerError.invariant(compiled.id != null, {
|
||||
reason: "FunctionDeclaration must produce a name",
|
||||
description: null,
|
||||
loc: fn.node.loc ?? GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
compiled.id = addSuffix(compiled.id, "_forget");
|
||||
const compiledFn = fn.insertAfter(compiled)[0];
|
||||
compiledFn.skip();
|
||||
@@ -212,6 +214,7 @@ export function compileProgram(
|
||||
description: violation.value.trim(),
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
loc: violation.loc ?? null,
|
||||
suggestions: null, // TODO(@poteto) add autofix for eslint
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -357,6 +360,7 @@ function buildFunctionDeclaration(
|
||||
severity: ErrorSeverity.Todo,
|
||||
description: `Handle ${fn.parentPath.type}`,
|
||||
loc: fn.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
const variableDeclarator = fn.parentPath;
|
||||
@@ -367,6 +371,7 @@ function buildFunctionDeclaration(
|
||||
severity: ErrorSeverity.Todo,
|
||||
description: `Handle ${variableDeclarator.parentPath.type}`,
|
||||
loc: fn.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
const variableDeclaration = variableDeclarator.parentPath;
|
||||
@@ -378,6 +383,7 @@ function buildFunctionDeclaration(
|
||||
severity: ErrorSeverity.Todo,
|
||||
description: `Handle ${id.type}`,
|
||||
loc: fn.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -407,11 +413,12 @@ function buildBlockStatement(
|
||||
return wrappedBody.node;
|
||||
}
|
||||
|
||||
CompilerError.invariant(
|
||||
body.isBlockStatement(),
|
||||
"Body must be a BlockStatement",
|
||||
body.node.loc ?? GeneratedSource
|
||||
);
|
||||
CompilerError.invariant(body.isBlockStatement(), {
|
||||
reason: "Body must be a BlockStatement",
|
||||
description: null,
|
||||
loc: body.node.loc ?? GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
|
||||
return body.node;
|
||||
}
|
||||
@@ -426,16 +433,20 @@ function addImportsToProgram(
|
||||
// Codegen currently does not rename import specifiers, so we do additional
|
||||
// validation here
|
||||
if (identifiers.has(importSpecifierName)) {
|
||||
CompilerError.invalidConfig(
|
||||
`Encountered conflicting import specifier for ${importSpecifierName} in Forget config.`,
|
||||
GeneratedSource
|
||||
);
|
||||
CompilerError.invalidConfig({
|
||||
reason: `Encountered conflicting import specifier for ${importSpecifierName} in Forget config.`,
|
||||
description: null,
|
||||
loc: GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
if (path.scope.hasBinding(importSpecifierName)) {
|
||||
CompilerError.invalidConfig(
|
||||
`Encountered conflicting import specifiers for ${importSpecifierName} in generated program.`,
|
||||
GeneratedSource
|
||||
);
|
||||
CompilerError.invalidConfig({
|
||||
reason: `Encountered conflicting import specifiers for ${importSpecifierName} in generated program.`,
|
||||
description: null,
|
||||
loc: GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
identifiers.add(importSpecifierName);
|
||||
|
||||
|
||||
+20
-18
@@ -35,18 +35,20 @@ export function assertConsistentIdentifiers(fn: HIRFunction): void {
|
||||
}
|
||||
}
|
||||
for (const instr of block.instructions) {
|
||||
CompilerError.invariant(
|
||||
instr.lvalue.identifier.name === null,
|
||||
`Expected all lvalues to be temporaries`,
|
||||
instr.lvalue.loc,
|
||||
`Found named lvalue '${instr.lvalue.identifier.name}'`
|
||||
);
|
||||
CompilerError.invariant(
|
||||
!assignments.has(instr.lvalue.identifier.id),
|
||||
`Expected lvalues to be assigned exactly once`,
|
||||
instr.lvalue.loc,
|
||||
`Found duplicate assignment of '${printPlace(instr.lvalue)}'`
|
||||
);
|
||||
CompilerError.invariant(instr.lvalue.identifier.name === null, {
|
||||
reason: `Expected all lvalues to be temporaries`,
|
||||
description: `Found named lvalue '${instr.lvalue.identifier.name}'`,
|
||||
loc: instr.lvalue.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
CompilerError.invariant(!assignments.has(instr.lvalue.identifier.id), {
|
||||
reason: `Expected lvalues to be assigned exactly once`,
|
||||
description: `Found duplicate assignment of '${printPlace(
|
||||
instr.lvalue
|
||||
)}'`,
|
||||
loc: instr.lvalue.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
assignments.add(instr.lvalue.identifier.id);
|
||||
for (const operand of eachInstructionLValue(instr)) {
|
||||
validate(identifiers, operand.identifier, operand.loc);
|
||||
@@ -72,11 +74,11 @@ function validate(
|
||||
if (previous === undefined) {
|
||||
identifiers.set(identifier.id, identifier);
|
||||
} else {
|
||||
CompilerError.invariant(
|
||||
identifier === previous,
|
||||
`Duplicate identifier object`,
|
||||
loc ?? GeneratedSource,
|
||||
`Found duplicate identifier object for id ${identifier.id}`
|
||||
);
|
||||
CompilerError.invariant(identifier === previous, {
|
||||
reason: `Duplicate identifier object`,
|
||||
description: `Found duplicate identifier object for id ${identifier.id}`,
|
||||
loc: loc ?? GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -13,14 +13,14 @@ import { mapTerminalSuccessors } from "./visitors";
|
||||
export function assertTerminalSuccessorsExist(fn: HIRFunction): void {
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
mapTerminalSuccessors(block.terminal, (successor) => {
|
||||
CompilerError.invariant(
|
||||
fn.body.blocks.has(successor),
|
||||
`Terminal successor references unknown block`,
|
||||
(block.terminal as any).loc ?? GeneratedSource,
|
||||
`Block bb${successor} does not exist for terminal '${printTerminal(
|
||||
CompilerError.invariant(fn.body.blocks.has(successor), {
|
||||
reason: `Terminal successor references unknown block`,
|
||||
description: `Block bb${successor} does not exist for terminal '${printTerminal(
|
||||
block.terminal
|
||||
)}'`
|
||||
);
|
||||
)}'`,
|
||||
loc: (block.terminal as any).loc ?? GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
return successor;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ export function lower(
|
||||
reason: `(BuildHIR::lower) Could not find binding for param '${param.node.name}'`,
|
||||
severity: ErrorSeverity.Invariant,
|
||||
loc: param.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -122,6 +123,7 @@ export function lower(
|
||||
reason: `(BuildHIR::lower) Handle ${param.node.type} params`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: param.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -143,6 +145,7 @@ export function lower(
|
||||
reason: `(BuildHIR::lower) Unexpected function body kind: ${body.type}}`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
loc: body.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -320,6 +323,7 @@ function lowerStatement(
|
||||
"(BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement",
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: stmt.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return {
|
||||
kind: "unsupported",
|
||||
@@ -391,6 +395,7 @@ function lowerStatement(
|
||||
reason: `(BuildHIR::lowerStatement) Handle empty test in ForStatement`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: stmt.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
} else {
|
||||
builder.terminateWithContinuation(
|
||||
@@ -540,6 +545,7 @@ function lowerStatement(
|
||||
"(BuildHIR::lowerStatement) Expected at most one `default` branch in SwitchStatement, this code should have failed to parse",
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
loc: case_.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -614,6 +620,7 @@ function lowerStatement(
|
||||
reason: `(BuildHIR::lowerStatement) Handle ${nodeKind} kinds in VariableDeclaration`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: stmt.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -641,6 +648,7 @@ function lowerStatement(
|
||||
reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
|
||||
severity: ErrorSeverity.Invariant,
|
||||
loc: id.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
} else {
|
||||
const place: Place = {
|
||||
@@ -655,6 +663,7 @@ function lowerStatement(
|
||||
reason: `(BuildHIR::lowerAssignment) Invalid declaration kind (const) for variable later reassigned.`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
loc: id.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
lowerValueToTemporary(builder, {
|
||||
@@ -681,6 +690,7 @@ function lowerStatement(
|
||||
reason: `(BuildHIR::lowerStatement) Expected variable declaration to be an identifier if no initializer was provided.`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
loc: stmt.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -768,11 +778,12 @@ function lowerStatement(
|
||||
case "FunctionDeclaration": {
|
||||
const stmt = stmtPath as NodePath<t.FunctionDeclaration>;
|
||||
stmt.skip();
|
||||
CompilerError.invariant(
|
||||
stmt.get("id").type === "Identifier",
|
||||
"function declarations must have a name",
|
||||
stmt.node.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(stmt.get("id").type === "Identifier", {
|
||||
reason: "function declarations must have a name",
|
||||
description: null,
|
||||
loc: stmt.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
const id = stmt.get("id") as NodePath<t.Identifier>;
|
||||
|
||||
// Desugar FunctionDeclaration to FunctionExpression.
|
||||
@@ -795,11 +806,13 @@ function lowerStatement(
|
||||
),
|
||||
])
|
||||
);
|
||||
CompilerError.invariant(
|
||||
desugared.length === 1,
|
||||
"only one declaration is created from desugaring function declaration",
|
||||
stmt.node.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(desugared.length === 1, {
|
||||
reason:
|
||||
"only one declaration is created from desugaring function declaration",
|
||||
description: null,
|
||||
loc: stmt.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
lowerStatement(builder, desugared.at(0)!);
|
||||
return;
|
||||
}
|
||||
@@ -844,11 +857,12 @@ function lowerStatement(
|
||||
let test: Place;
|
||||
if (left.isVariableDeclaration()) {
|
||||
const declarations = left.get("declarations");
|
||||
CompilerError.invariant(
|
||||
declarations.length === 1,
|
||||
`Expected only one declaration in the init of a ForOfStatement, got ${declarations.length}`,
|
||||
left.node.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(declarations.length === 1, {
|
||||
reason: `Expected only one declaration in the init of a ForOfStatement, got ${declarations.length}`,
|
||||
description: null,
|
||||
loc: left.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
const id = declarations[0].get("id");
|
||||
const nextIterableOf = lowerValueToTemporary(builder, {
|
||||
kind: "NextIterableOf",
|
||||
@@ -868,6 +882,7 @@ function lowerStatement(
|
||||
reason: `(BuildHIR::lowerStatement) Handle ${left.type} inits in ForOfStatement`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: left.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -935,6 +950,7 @@ function lowerStatement(
|
||||
reason: `(BuildHIR::lowerStatement) Handle ${stmtPath.type} statements`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: stmtPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
lowerValueToTemporary(builder, {
|
||||
kind: "UnsupportedNode",
|
||||
@@ -1007,6 +1023,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: propertyPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -1016,6 +1033,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${valuePath.type} values in ObjectExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: valuePath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -1039,6 +1057,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.type} properties in ObjectExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: propertyPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -1058,6 +1077,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${element.type} elements in ArrayExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
} else if (element.isExpression()) {
|
||||
@@ -1073,6 +1093,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${element.type} elements in ArrayExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: element.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -1091,6 +1112,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Expected Expression, got ${calleePath.type} in NewExpression (v8 intrinsics not supported): ${calleePath.type}`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: calleePath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
@@ -1116,6 +1138,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Expected Expression, got ${calleePath.type} in CallExpression (v8 intrinsics not supported)`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: calleePath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
@@ -1149,6 +1172,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Expected Expression, got ${leftPath.type} lval in BinaryExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: leftPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
@@ -1180,6 +1204,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Expected SequenceExpression to have at least one expression`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
loc: expr.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
} else {
|
||||
lowerValueToTemporary(builder, {
|
||||
@@ -1391,6 +1416,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${operator} operators in AssignmentExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: expr.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
@@ -1463,6 +1489,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Expected Identifier or MemberExpression, got ${expr.type} lval in AssignmentExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: expr.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
@@ -1504,6 +1531,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${attribute.type} attributes in JSXElement`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: attribute.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -1516,14 +1544,16 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Unexpected colon in attribute name '${name}'`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: namePath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
CompilerError.invariant(
|
||||
namePath.isJSXNamespacedName(),
|
||||
"Refinement",
|
||||
namePath.node.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(namePath.isJSXNamespacedName(), {
|
||||
reason: "Refinement",
|
||||
description: null,
|
||||
loc: namePath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
const namespace = namePath.node.namespace.name;
|
||||
const name = namePath.node.name.name;
|
||||
propName = `${namespace}:${name}`;
|
||||
@@ -1538,6 +1568,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${valueExpr.type} attribute values in JSXElement`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: valueExpr.node?.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -1547,6 +1578,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${expression.type} expressions in JSXExpressionContainer within JSXElement`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: valueExpr.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -1589,14 +1621,17 @@ function lowerExpression(
|
||||
"(BuildHIR::lowerExpression) Handle tagged template with interpolations",
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
CompilerError.invariant(
|
||||
expr.get("quasi").get("quasis").length == 1,
|
||||
"there should be only one quasi as we don't support interpolations yet",
|
||||
expr.node.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(expr.get("quasi").get("quasis").length == 1, {
|
||||
reason:
|
||||
"there should be only one quasi as we don't support interpolations yet",
|
||||
description: null,
|
||||
loc: expr.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
const value = expr.get("quasi").get("quasis").at(0)!.node.value;
|
||||
if (value.raw !== value.cooked) {
|
||||
builder.errors.push({
|
||||
@@ -1604,6 +1639,7 @@ function lowerExpression(
|
||||
"(BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value",
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
@@ -1625,6 +1661,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Unexpected quasi and subexpression lengths in TemplateLiteral.`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
@@ -1634,6 +1671,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerAssignment) Handle TSType in TemplateLiteral.`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
@@ -1675,6 +1713,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) delete on a non-member expression has no semantic meaning`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
loc: expr.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: expr.node, loc: exprLoc };
|
||||
}
|
||||
@@ -1712,6 +1751,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Handle UpdateExpression with ${argument.type} argument`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
@@ -1720,6 +1760,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Handle prefix UpdateExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
@@ -1774,6 +1815,7 @@ function lowerExpression(
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
|
||||
}
|
||||
@@ -1843,7 +1885,12 @@ function lowerOptionalMemberExpression(
|
||||
loc,
|
||||
};
|
||||
});
|
||||
CompilerError.invariant(object !== null, "Satisfy type checker", null);
|
||||
CompilerError.invariant(object !== null, {
|
||||
reason: "Satisfy type checker",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
|
||||
// block to evaluate if the callee is non-null/undefined. arguments are lowered in this block to preserve
|
||||
// the semantic of conditional evaluation depending on the callee
|
||||
@@ -2047,6 +2094,7 @@ function lowerReorderableExpression(
|
||||
reason: `(BuildHIR::node.lowerReorderableExpression) Expression type '${expr.type}' cannot be safely reordered`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: expr.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
return lowerExpressionToTemporary(builder, expr);
|
||||
@@ -2138,6 +2186,7 @@ function lowerArguments(
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in CallExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: argPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2167,6 +2216,7 @@ function lowerMemberExpression(
|
||||
reason: `(BuildHIR::lowerMemberExpression) Handle ${propertyNode.type} property`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: propertyNode.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return {
|
||||
object,
|
||||
@@ -2187,6 +2237,7 @@ function lowerMemberExpression(
|
||||
reason: `(BuildHIR::lowerMemberExpression) Expected Expression, got ${propertyNode.type} property`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: propertyNode.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return {
|
||||
object,
|
||||
@@ -2243,6 +2294,7 @@ function lowerJsxElementName(
|
||||
reason: `(BuildHIR::lowerJsxElementName) Expected JSXNamespacedName to have no colons in the namespace or name, got '${namespace}' : '${name}'`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
const place = lowerValueToTemporary(builder, {
|
||||
@@ -2256,6 +2308,7 @@ function lowerJsxElementName(
|
||||
reason: `(BuildHIR::lowerJsxElementName) Handle ${exprPath.type} tags`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return lowerValueToTemporary(builder, {
|
||||
kind: "UnsupportedNode",
|
||||
@@ -2275,11 +2328,12 @@ function lowerJsxMemberExpression(
|
||||
if (object.isJSXMemberExpression()) {
|
||||
objectPlace = lowerJsxMemberExpression(builder, object);
|
||||
} else {
|
||||
CompilerError.invariant(
|
||||
object.isJSXIdentifier(),
|
||||
`TypeScript refinement fail: expected 'JsxIdentifier', got '${object.node.type}'`,
|
||||
object.node.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(object.isJSXIdentifier(), {
|
||||
reason: `TypeScript refinement fail: expected 'JsxIdentifier', got '${object.node.type}'`,
|
||||
description: null,
|
||||
loc: object.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
objectPlace = lowerIdentifier(builder, object);
|
||||
}
|
||||
const property = exprPath.get("property").node.name;
|
||||
@@ -2310,11 +2364,12 @@ function lowerJsxElement(
|
||||
if (expression.isJSXEmptyExpression()) {
|
||||
return null;
|
||||
} else {
|
||||
CompilerError.invariant(
|
||||
expression.isExpression(),
|
||||
`(BuildHIR::lowerJsxElement) Expected Expression but found ${expression.type}!`,
|
||||
expression.node.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(expression.isExpression(), {
|
||||
reason: `(BuildHIR::lowerJsxElement) Expected Expression but found ${expression.type}!`,
|
||||
description: null,
|
||||
loc: expression.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return lowerExpressionToTemporary(builder, expression);
|
||||
}
|
||||
} else if (exprPath.isJSXText()) {
|
||||
@@ -2329,6 +2384,7 @@ function lowerJsxElement(
|
||||
reason: `(BuildHIR::lowerJsxElement) Unhandled JsxElement, got: ${exprPath.type}`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
const place = lowerValueToTemporary(builder, {
|
||||
kind: "UnsupportedNode",
|
||||
@@ -2481,6 +2537,7 @@ function lowerIdentifierForAssignment(
|
||||
reason: `(BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.`,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
loc: path.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
} else {
|
||||
// Else its an internal error bc we couldn't find the binding
|
||||
@@ -2488,6 +2545,7 @@ function lowerIdentifierForAssignment(
|
||||
reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
|
||||
severity: ErrorSeverity.Invariant,
|
||||
loc: path.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
@@ -2530,6 +2588,7 @@ function lowerAssignment(
|
||||
reason: `(BuildHIR::lowerAssignment) Invalid declaration kind (const) for variable later reassigned.`,
|
||||
severity: ErrorSeverity.InvalidInput,
|
||||
loc: lvalue.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
lowerValueToTemporary(builder, {
|
||||
@@ -2560,11 +2619,12 @@ function lowerAssignment(
|
||||
}
|
||||
case "MemberExpression": {
|
||||
// This can only occur because of a coding error, parsers enforce this condition
|
||||
CompilerError.invariant(
|
||||
kind === InstructionKind.Reassign,
|
||||
"MemberExpression may only appear in an assignment expression",
|
||||
lvaluePath.node.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(kind === InstructionKind.Reassign, {
|
||||
reason: "MemberExpression may only appear in an assignment expression",
|
||||
description: null,
|
||||
loc: lvaluePath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
const lvalue = lvaluePath as NodePath<t.MemberExpression>;
|
||||
const property = lvalue.get("property");
|
||||
const object = lowerExpressionToTemporary(builder, lvalue.get("object"));
|
||||
@@ -2574,6 +2634,7 @@ function lowerAssignment(
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in MemberExpression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: property.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: lvalueNode, loc };
|
||||
}
|
||||
@@ -2592,6 +2653,7 @@ function lowerAssignment(
|
||||
"(BuildHIR::lowerAssignment) Expected private name to appear as a non-computed property",
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: property.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: lvalueNode, loc };
|
||||
}
|
||||
@@ -2624,6 +2686,7 @@ function lowerAssignment(
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${argument.node.type} rest element in ArrayPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: element.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -2691,6 +2754,7 @@ function lowerAssignment(
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${argument.node.type} rest element in ArrayPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: argument.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -2714,6 +2778,7 @@ function lowerAssignment(
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in ObjectPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: property.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -2722,6 +2787,7 @@ function lowerAssignment(
|
||||
reason: `(BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: property.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -2731,6 +2797,7 @@ function lowerAssignment(
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${key.type} keys in ObjectPattern`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: key.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -2740,6 +2807,7 @@ function lowerAssignment(
|
||||
reason: `(BuildHIR::lowerAssignment) Expected object property value to be an LVal, got: ${element.type}`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: element.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -2875,6 +2943,7 @@ function lowerAssignment(
|
||||
reason: `(BuildHIR::lowerAssignment) Handle ${lvaluePath.type} assignments`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: lvaluePath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return { kind: "UnsupportedNode", node: lvalueNode, loc };
|
||||
}
|
||||
|
||||
@@ -88,7 +88,12 @@ export class Dominator<T> {
|
||||
*/
|
||||
get(id: T): T | null {
|
||||
const dominator = this.#nodes.get(id);
|
||||
CompilerError.invariant(dominator !== undefined, "Unknown node", null);
|
||||
CompilerError.invariant(dominator !== undefined, {
|
||||
reason: "Unknown node",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return dominator === id ? null : dominator;
|
||||
}
|
||||
|
||||
@@ -119,7 +124,12 @@ export class PostDominator<T> {
|
||||
*/
|
||||
get(id: T): T | null {
|
||||
const dominator = this.#nodes.get(id);
|
||||
CompilerError.invariant(dominator !== undefined, "Unknown node", null);
|
||||
CompilerError.invariant(dominator !== undefined, {
|
||||
reason: "Unknown node",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return dominator === id ? null : dominator;
|
||||
}
|
||||
|
||||
@@ -159,11 +169,12 @@ function computeImmediateDominators<T>(graph: Graph<T>): Map<T, T> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
CompilerError.invariant(
|
||||
newIdom !== null,
|
||||
`At least one predecessor must have been visited for block ${id}`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(newIdom !== null, {
|
||||
reason: `At least one predecessor must have been visited for block ${id}`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
|
||||
for (const pred of node.preds) {
|
||||
// For all other predecessors
|
||||
|
||||
@@ -195,11 +195,12 @@ export class Environment {
|
||||
if (config?.customHooks) {
|
||||
this.#globals = new Map(DEFAULT_GLOBALS);
|
||||
for (const [hookName, hook] of config.customHooks) {
|
||||
CompilerError.invariant(
|
||||
!this.#globals.has(hookName),
|
||||
`[Globals] Found existing definition in global registry for custom hook ${hookName}`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(!this.#globals.has(hookName), {
|
||||
reason: `[Globals] Found existing definition in global registry for custom hook ${hookName}`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
this.#globals.set(
|
||||
hookName,
|
||||
addHook(this.#shapes, [], {
|
||||
@@ -271,11 +272,12 @@ export class Environment {
|
||||
// If an object or function has a shapeId, it must have been assigned
|
||||
// by Forget (and be present in a builtin or user-defined registry)
|
||||
const shape = this.#shapes.get(shapeId);
|
||||
CompilerError.invariant(
|
||||
shape !== undefined,
|
||||
`[HIR] Forget internal error: cannot resolve shape ${shapeId}`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(shape !== undefined, {
|
||||
reason: `[HIR] Forget internal error: cannot resolve shape ${shapeId}`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return shape.properties.get(property) ?? null;
|
||||
} else {
|
||||
return null;
|
||||
@@ -286,11 +288,12 @@ export class Environment {
|
||||
const { shapeId } = type;
|
||||
if (shapeId !== null) {
|
||||
const shape = this.#shapes.get(shapeId);
|
||||
CompilerError.invariant(
|
||||
shape !== undefined,
|
||||
`[HIR] Forget internal error: cannot resolve shape ${shapeId}`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(shape !== undefined, {
|
||||
reason: `[HIR] Forget internal error: cannot resolve shape ${shapeId}`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return shape.functionType;
|
||||
}
|
||||
return null;
|
||||
|
||||
+18
-14
@@ -137,18 +137,20 @@ function handleAssignment(
|
||||
for (const property of path.get("properties")) {
|
||||
if (property.isObjectProperty()) {
|
||||
const valuePath = property.get("value");
|
||||
CompilerError.invariant(
|
||||
valuePath.isLVal(),
|
||||
`[FindContextIdentifiers] Expected object property value to be an LVal, got: ${valuePath.type}`,
|
||||
valuePath.node.loc ?? GeneratedSource
|
||||
);
|
||||
CompilerError.invariant(valuePath.isLVal(), {
|
||||
reason: `[FindContextIdentifiers] Expected object property value to be an LVal, got: ${valuePath.type}`,
|
||||
description: null,
|
||||
loc: valuePath.node.loc ?? GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
handleAssignment(reassigned, valuePath);
|
||||
} else {
|
||||
CompilerError.invariant(
|
||||
property.isRestElement(),
|
||||
`[FindContextIdentifiers] Invalid assumptions for babel types.`,
|
||||
property.node.loc ?? GeneratedSource
|
||||
);
|
||||
CompilerError.invariant(property.isRestElement(), {
|
||||
reason: `[FindContextIdentifiers] Invalid assumptions for babel types.`,
|
||||
description: null,
|
||||
loc: property.node.loc ?? GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
handleAssignment(reassigned, property);
|
||||
}
|
||||
}
|
||||
@@ -170,10 +172,12 @@ function handleAssignment(
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
CompilerError.todo(
|
||||
`[FindContextIdentifiers] Cannot handle Object destructuring assignment target ${lvalNode.type}`,
|
||||
lvalNode.loc ?? GeneratedSource
|
||||
);
|
||||
CompilerError.todo({
|
||||
reason: `[FindContextIdentifiers] Cannot handle Object destructuring assignment target ${lvalNode.type}`,
|
||||
description: null,
|
||||
loc: lvalNode.loc ?? GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,7 +883,12 @@ export function isMutableEffect(
|
||||
}
|
||||
|
||||
case Effect.Unknown: {
|
||||
CompilerError.invariant(false, "Unexpected unknown effect", location);
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Unexpected unknown effect",
|
||||
description: null,
|
||||
loc: location,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
case Effect.Read:
|
||||
case Effect.Freeze: {
|
||||
@@ -921,11 +926,12 @@ const opaqueBlockId = Symbol();
|
||||
export type BlockId = number & { [opaqueBlockId]: "BlockId" };
|
||||
|
||||
export function makeBlockId(id: number): BlockId {
|
||||
CompilerError.invariant(
|
||||
id >= 0 && Number.isInteger(id),
|
||||
"Expected block id to be a non-negative integer",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(id >= 0 && Number.isInteger(id), {
|
||||
reason: "Expected block id to be a non-negative integer",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return id as BlockId;
|
||||
}
|
||||
|
||||
@@ -937,11 +943,12 @@ const opaqueScopeId = Symbol();
|
||||
export type ScopeId = number & { [opaqueScopeId]: "ScopeId" };
|
||||
|
||||
export function makeScopeId(id: number): ScopeId {
|
||||
CompilerError.invariant(
|
||||
id >= 0 && Number.isInteger(id),
|
||||
"Expected block id to be a non-negative integer",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(id >= 0 && Number.isInteger(id), {
|
||||
reason: "Expected block id to be a non-negative integer",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return id as ScopeId;
|
||||
}
|
||||
|
||||
@@ -953,11 +960,12 @@ const opaqueIdentifierId = Symbol();
|
||||
export type IdentifierId = number & { [opaqueIdentifierId]: "IdentifierId" };
|
||||
|
||||
export function makeIdentifierId(id: number): IdentifierId {
|
||||
CompilerError.invariant(
|
||||
id >= 0 && Number.isInteger(id),
|
||||
"Expected identifier id to be a non-negative integer",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(id >= 0 && Number.isInteger(id), {
|
||||
reason: "Expected identifier id to be a non-negative integer",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return id as IdentifierId;
|
||||
}
|
||||
|
||||
@@ -969,11 +977,12 @@ const opaqueInstructionId = Symbol();
|
||||
export type InstructionId = number & { [opaqueInstructionId]: "IdentifierId" };
|
||||
|
||||
export function makeInstructionId(id: number): InstructionId {
|
||||
CompilerError.invariant(
|
||||
id >= 0 && Number.isInteger(id),
|
||||
"Expected instruction id to be a non-negative integer",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(id >= 0 && Number.isInteger(id), {
|
||||
reason: "Expected instruction id to be a non-negative integer",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return id as InstructionId;
|
||||
}
|
||||
|
||||
|
||||
@@ -392,8 +392,12 @@ export default class HIRBuilder {
|
||||
last.kind === "label" &&
|
||||
last.label === label &&
|
||||
last.breakBlock === breakBlock,
|
||||
"Mismatched label",
|
||||
null
|
||||
{
|
||||
reason: "Mismatched label",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
return value;
|
||||
}
|
||||
@@ -411,8 +415,12 @@ export default class HIRBuilder {
|
||||
last.kind === "switch" &&
|
||||
last.label === label &&
|
||||
last.breakBlock === breakBlock,
|
||||
"Mismatched label",
|
||||
null
|
||||
{
|
||||
reason: "Mismatched label",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
return value;
|
||||
}
|
||||
@@ -447,8 +455,12 @@ export default class HIRBuilder {
|
||||
last.label === label &&
|
||||
last.continueBlock === continueBlock &&
|
||||
last.breakBlock === breakBlock,
|
||||
"Mismatched loops",
|
||||
null
|
||||
{
|
||||
reason: "Mismatched loops",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
return value;
|
||||
}
|
||||
@@ -464,11 +476,12 @@ export default class HIRBuilder {
|
||||
return scope.breakBlock;
|
||||
}
|
||||
}
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
"Expected a loop or switch to be in scope",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Expected a loop or switch to be in scope",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -484,14 +497,20 @@ export default class HIRBuilder {
|
||||
return scope.continueBlock;
|
||||
}
|
||||
} else if (label !== null && scope.label === label) {
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
"Continue may only refer to a labeled loop",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Continue may only refer to a labeled loop",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
CompilerError.invariant(false, "Expected a loop to be in scope", null);
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Expected a loop to be in scope",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,11 +530,12 @@ function _shrink(func: HIR): void {
|
||||
return target;
|
||||
}
|
||||
const block = func.blocks.get(blockId);
|
||||
CompilerError.invariant(
|
||||
block != null,
|
||||
`expected block ${blockId} to exist`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(block != null, {
|
||||
reason: `expected block ${blockId} to exist`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
target = getTargetIfIndirection(block);
|
||||
if (target !== null) {
|
||||
// the target might also be a simple goto, recurse
|
||||
@@ -731,11 +751,12 @@ export function markInstructionIds(func: HIR): void {
|
||||
const visited = new Set<Instruction>();
|
||||
for (const [_, block] of func.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
CompilerError.invariant(
|
||||
!visited.has(instr),
|
||||
`${printInstruction(instr)} already visited!`,
|
||||
instr.loc
|
||||
);
|
||||
CompilerError.invariant(!visited.has(instr), {
|
||||
reason: `${printInstruction(instr)} already visited!`,
|
||||
description: null,
|
||||
loc: instr.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
visited.add(instr);
|
||||
instr.id = makeInstructionId(++id);
|
||||
}
|
||||
|
||||
+12
-10
@@ -47,11 +47,12 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
|
||||
const originalPredecessorId = Array.from(block.preds)[0]!;
|
||||
const predecessorId = merged.get(originalPredecessorId);
|
||||
const predecessor = fn.body.blocks.get(predecessorId);
|
||||
CompilerError.invariant(
|
||||
predecessor !== undefined,
|
||||
`Expected predecessor ${predecessorId} to exist`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(predecessor !== undefined, {
|
||||
reason: `Expected predecessor ${predecessorId} to exist`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
if (predecessor.terminal.kind !== "goto" || predecessor.kind !== "block") {
|
||||
// The predecessor is not guaranteed to transfer control to this block,
|
||||
// they aren't consecutive.
|
||||
@@ -60,11 +61,12 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
|
||||
|
||||
// Replace phis in the merged block with canonical assignments to the single operand value
|
||||
for (const phi of block.phis) {
|
||||
CompilerError.invariant(
|
||||
phi.operands.size === 1,
|
||||
`Found a block with a single predecessor but where a phi has multiple (${phi.operands.size}) operands`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(phi.operands.size === 1, {
|
||||
reason: `Found a block with a single predecessor but where a phi has multiple (${phi.operands.size}) operands`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
const operand = Array.from(phi.operands.values())[0]!;
|
||||
const instr: Instruction = {
|
||||
id: predecessor.terminal.id,
|
||||
|
||||
@@ -103,11 +103,12 @@ function addShape(
|
||||
functionType,
|
||||
};
|
||||
|
||||
CompilerError.invariant(
|
||||
!registry.has(id),
|
||||
`[ObjectShape] Could not add shape to registry: name ${id} already exists.`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(!registry.has(id), {
|
||||
reason: `[ObjectShape] Could not add shape to registry: name ${id} already exists.`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
registry.set(id, shape);
|
||||
return shape;
|
||||
}
|
||||
|
||||
@@ -469,8 +469,12 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
|
||||
value = "`";
|
||||
CompilerError.invariant(
|
||||
instrValue.subexprs.length === instrValue.quasis.length - 1,
|
||||
"Bad assumption about quasi length.",
|
||||
instrValue.loc
|
||||
{
|
||||
reason: "Bad assumption about quasi length.",
|
||||
description: null,
|
||||
loc: instrValue.loc,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
for (let i = 0; i < instrValue.subexprs.length; i++) {
|
||||
value += instrValue.quasis[i].raw;
|
||||
|
||||
@@ -62,11 +62,12 @@ const opaqueTypeId = Symbol();
|
||||
export type TypeId = number & { [opaqueTypeId]: "IdentifierId" };
|
||||
|
||||
export function makeTypeId(id: number): TypeId {
|
||||
CompilerError.invariant(
|
||||
id >= 0 && Number.isInteger(id),
|
||||
"Expected instruction id to be a non-negative integer",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(id >= 0 && Number.isInteger(id), {
|
||||
reason: "Expected instruction id to be a non-negative integer",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return id as TypeId;
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -169,11 +169,12 @@ function infer(
|
||||
// In practice this never really matters because the Component function has no
|
||||
// context refs, so it will never have duplicate deps.
|
||||
for (const place of context) {
|
||||
CompilerError.invariant(
|
||||
place.identifier.name !== null,
|
||||
"context refs should always have a name",
|
||||
place.loc
|
||||
);
|
||||
CompilerError.invariant(place.identifier.name !== null, {
|
||||
reason: "context refs should always have a name",
|
||||
description: null,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
|
||||
const effect = mutations.get(place.identifier.name);
|
||||
if (effect !== undefined) {
|
||||
|
||||
+6
-5
@@ -108,11 +108,12 @@ export function inferMutableLifetimes(
|
||||
start = Math.min(start, operand.mutableRange.start);
|
||||
end = Math.max(end, operand.mutableRange.end);
|
||||
}
|
||||
CompilerError.invariant(
|
||||
start !== Number.MAX_SAFE_INTEGER,
|
||||
"Expected phi to have a start range value",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(start !== Number.MAX_SAFE_INTEGER, {
|
||||
reason: "Expected phi to have a start range value",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
phi.id.mutableRange = {
|
||||
start: makeInstructionId(start),
|
||||
end: makeInstructionId(end),
|
||||
|
||||
+88
-67
@@ -191,11 +191,13 @@ class InferenceState {
|
||||
* (Re)initializes a @param value with its default @param kind.
|
||||
*/
|
||||
initialize(value: InstructionValue, kind: ValueKind): void {
|
||||
CompilerError.invariant(
|
||||
value.kind !== "LoadLocal",
|
||||
"Expected all top-level identifiers to be defined as variables, not values",
|
||||
value.loc
|
||||
);
|
||||
CompilerError.invariant(value.kind !== "LoadLocal", {
|
||||
reason:
|
||||
"Expected all top-level identifiers to be defined as variables, not values",
|
||||
description: null,
|
||||
loc: value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
this.#values.set(value, kind);
|
||||
}
|
||||
|
||||
@@ -204,24 +206,25 @@ class InferenceState {
|
||||
*/
|
||||
kind(place: Place): ValueKind {
|
||||
const values = this.#variables.get(place.identifier.id);
|
||||
CompilerError.invariant(
|
||||
values != null,
|
||||
`Expected value kind to be initialized at '${printSourceLocation(
|
||||
CompilerError.invariant(values != null, {
|
||||
reason: `Expected value kind to be initialized at '${printSourceLocation(
|
||||
place.loc
|
||||
)}'`,
|
||||
place.loc
|
||||
);
|
||||
description: null,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
let mergedKind: ValueKind | null = null;
|
||||
for (const value of values) {
|
||||
const kind = this.#values.get(value)!;
|
||||
mergedKind = mergedKind !== null ? mergeValues(mergedKind, kind) : kind;
|
||||
}
|
||||
CompilerError.invariant(
|
||||
mergedKind !== null,
|
||||
`InferReferenceEffects::kind: Expected at least one value`,
|
||||
place.loc,
|
||||
`No value found at '${printPlace(place)}'`
|
||||
);
|
||||
CompilerError.invariant(mergedKind !== null, {
|
||||
reason: `InferReferenceEffects::kind: Expected at least one value`,
|
||||
description: `No value found at '${printPlace(place)}'`,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
return mergedKind;
|
||||
}
|
||||
|
||||
@@ -230,11 +233,12 @@ class InferenceState {
|
||||
*/
|
||||
alias(place: Place, value: Place): void {
|
||||
const values = this.#variables.get(value.identifier.id);
|
||||
CompilerError.invariant(
|
||||
values != null,
|
||||
`Expected value for identifier \`${value.identifier.id}\` to be initialized.`,
|
||||
value.loc
|
||||
);
|
||||
CompilerError.invariant(values != null, {
|
||||
reason: `Expected value for identifier \`${value.identifier.id}\` to be initialized.`,
|
||||
description: null,
|
||||
loc: value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
this.#variables.set(place.identifier.id, new Set(values));
|
||||
}
|
||||
|
||||
@@ -242,11 +246,14 @@ class InferenceState {
|
||||
* Defines (initializing or updating) a variable with a specific kind of value.
|
||||
*/
|
||||
define(place: Place, value: InstructionValue): void {
|
||||
CompilerError.invariant(
|
||||
this.#values.has(value),
|
||||
`Expected value to be initialized at '${printSourceLocation(value.loc)}'`,
|
||||
value.loc
|
||||
);
|
||||
CompilerError.invariant(this.#values.has(value), {
|
||||
reason: `Expected value to be initialized at '${printSourceLocation(
|
||||
value.loc
|
||||
)}'`,
|
||||
description: null,
|
||||
loc: value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
this.#variables.set(place.identifier.id, new Set([value]));
|
||||
}
|
||||
|
||||
@@ -269,11 +276,12 @@ class InferenceState {
|
||||
reference(place: Place, effectKind: Effect): void {
|
||||
const values = this.#variables.get(place.identifier.id);
|
||||
if (values === undefined) {
|
||||
CompilerError.invariant(
|
||||
effectKind !== Effect.Store,
|
||||
"[InferReferenceEffects] Unhandled store reference effect",
|
||||
place.loc
|
||||
);
|
||||
CompilerError.invariant(effectKind !== Effect.Store, {
|
||||
reason: "[InferReferenceEffects] Unhandled store reference effect",
|
||||
description: null,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
place.effect =
|
||||
effectKind === Effect.ConditionallyMutate
|
||||
? Effect.ConditionallyMutate
|
||||
@@ -315,13 +323,14 @@ class InferenceState {
|
||||
) {
|
||||
effect = Effect.Mutate;
|
||||
} else {
|
||||
CompilerError.invalidReact(
|
||||
`InferReferenceEffects: inferred mutation of known immutable value`,
|
||||
place.loc,
|
||||
`Found mutation of ${printIdentifier(place.identifier)}${printType(
|
||||
place.identifier.type
|
||||
)} (${valueKind})`
|
||||
);
|
||||
CompilerError.invalidReact({
|
||||
reason: `InferReferenceEffects: inferred mutation of known immutable value`,
|
||||
description: `Found mutation of ${printIdentifier(
|
||||
place.identifier
|
||||
)}${printType(place.identifier.type)} (${valueKind})`,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -330,13 +339,14 @@ class InferenceState {
|
||||
valueKind !== ValueKind.Mutable &&
|
||||
valueKind !== ValueKind.Context
|
||||
) {
|
||||
CompilerError.invalidReact(
|
||||
`InferReferenceEffects: inferred mutation of known immutable value`,
|
||||
place.loc,
|
||||
`Found mutation of ${printIdentifier(place.identifier)}${printType(
|
||||
place.identifier.type
|
||||
)} (${valueKind})`
|
||||
);
|
||||
CompilerError.invalidReact({
|
||||
reason: `InferReferenceEffects: inferred mutation of known immutable value`,
|
||||
description: `Found mutation of ${printIdentifier(
|
||||
place.identifier
|
||||
)}${printType(place.identifier.type)} (${valueKind})`,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO(gsn): This should be bailout once we add bailout infra.
|
||||
@@ -365,11 +375,13 @@ class InferenceState {
|
||||
break;
|
||||
}
|
||||
case Effect.Unknown: {
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
"Unexpected unknown effect, expected to infer a precise effect kind",
|
||||
place.loc
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason:
|
||||
"Unexpected unknown effect, expected to infer a precise effect kind",
|
||||
description: null,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
@@ -378,11 +390,12 @@ class InferenceState {
|
||||
);
|
||||
}
|
||||
}
|
||||
CompilerError.invariant(
|
||||
effect !== null,
|
||||
"Expected effect to be set",
|
||||
place.loc
|
||||
);
|
||||
CompilerError.invariant(effect !== null, {
|
||||
reason: "Expected effect to be set",
|
||||
description: null,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
place.effect = effect;
|
||||
}
|
||||
|
||||
@@ -753,11 +766,13 @@ function inferBlock(
|
||||
continue;
|
||||
}
|
||||
case "MethodCall": {
|
||||
CompilerError.invariant(
|
||||
state.isDefined(instrValue.receiver),
|
||||
"[InferReferenceEffects] Internal error: receiver of PropertyCall should have been defined by corresponding PropertyLoad",
|
||||
instrValue.loc
|
||||
);
|
||||
CompilerError.invariant(state.isDefined(instrValue.receiver), {
|
||||
reason:
|
||||
"[InferReferenceEffects] Internal error: receiver of PropertyCall should have been defined by corresponding PropertyLoad",
|
||||
description: null,
|
||||
loc: instrValue.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
state.reference(instrValue.property, Effect.Read);
|
||||
|
||||
const signature = getFunctionCallSignature(
|
||||
@@ -892,8 +907,13 @@ function inferBlock(
|
||||
const valueKind = state.kind(instrValue.place);
|
||||
CompilerError.invariant(
|
||||
valueKind === ValueKind.Mutable || valueKind === ValueKind.Context,
|
||||
"[InferReferenceEffects] Context variables are always mutable.",
|
||||
instrValue.loc
|
||||
{
|
||||
reason:
|
||||
"[InferReferenceEffects] Context variables are always mutable.",
|
||||
description: null,
|
||||
loc: instrValue.loc,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
state.initialize(instrValue, valueKind);
|
||||
state.define(lvalue, instrValue);
|
||||
@@ -980,11 +1000,12 @@ function inferBlock(
|
||||
}
|
||||
|
||||
for (const operand of eachInstructionOperand(instr)) {
|
||||
CompilerError.invariant(
|
||||
effectKind != null,
|
||||
`effectKind must be set for instruction value \`${instrValue.kind}\``,
|
||||
instrValue.loc
|
||||
);
|
||||
CompilerError.invariant(effectKind != null, {
|
||||
reason: `effectKind must be set for instruction value \`${instrValue.kind}\``,
|
||||
description: null,
|
||||
loc: instrValue.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
state.reference(operand, effectKind);
|
||||
}
|
||||
|
||||
|
||||
@@ -100,17 +100,22 @@ export function inlineUseMemo(fn: HIRFunction): void {
|
||||
}
|
||||
|
||||
if (body.loweredFunc.params.length > 0) {
|
||||
CompilerError.invalidReact(
|
||||
"useMemo callbacks may not accept any arguments",
|
||||
body.loc
|
||||
);
|
||||
CompilerError.invalidReact({
|
||||
reason: "useMemo callbacks may not accept any arguments",
|
||||
description: null,
|
||||
loc: body.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
|
||||
if (body.loweredFunc.async || body.loweredFunc.generator) {
|
||||
CompilerError.invalidReact(
|
||||
"useMemo callbacks may not be async or generator functions",
|
||||
body.loc
|
||||
);
|
||||
CompilerError.invalidReact({
|
||||
reason:
|
||||
"useMemo callbacks may not be async or generator functions",
|
||||
description: null,
|
||||
loc: body.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
|
||||
// We know this function is used for useMemo and can prune it later
|
||||
|
||||
+31
-25
@@ -70,11 +70,12 @@ class Context {
|
||||
this.#builders.push(builder);
|
||||
fn();
|
||||
const popped = this.#builders.pop();
|
||||
CompilerError.invariant(
|
||||
popped === builder,
|
||||
"Expected push/pop to be called 1:1",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(popped === builder, {
|
||||
reason: "Expected push/pop to be called 1:1",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return builder.complete();
|
||||
}
|
||||
}
|
||||
@@ -94,11 +95,12 @@ class Builder {
|
||||
|
||||
append(item: ReactiveStatement, label: BlockId | null): void {
|
||||
if (label !== null) {
|
||||
CompilerError.invariant(
|
||||
item.kind === "terminal",
|
||||
"Only terminals may have a label",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(item.kind === "terminal", {
|
||||
reason: "Only terminals may have a label",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
item.label = label;
|
||||
}
|
||||
this.#instructions.push(item);
|
||||
@@ -138,11 +140,12 @@ class Builder {
|
||||
// "Expected all scopes to be closed when exiting a block"
|
||||
// );
|
||||
const first = this.#stack[0]!;
|
||||
CompilerError.invariant(
|
||||
first.kind === "block",
|
||||
"Expected first stack item to be a basic block",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(first.kind === "block", {
|
||||
reason: "Expected first stack item to be a basic block",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return first.block;
|
||||
}
|
||||
}
|
||||
@@ -173,11 +176,12 @@ function visitBlock(context: Context, block: ReactiveBlock): void {
|
||||
break;
|
||||
}
|
||||
case "scope": {
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
"Expected the function to not have scopes already assigned",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Expected the function to not have scopes already assigned",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
@@ -192,12 +196,14 @@ function visitBlock(context: Context, block: ReactiveBlock): void {
|
||||
export function getInstructionScope(
|
||||
instr: ReactiveInstruction
|
||||
): ReactiveScope | null {
|
||||
CompilerError.invariant(
|
||||
instr.lvalue !== null,
|
||||
"Expected lvalues to not be null when assigning scopes. " +
|
||||
CompilerError.invariant(instr.lvalue !== null, {
|
||||
reason:
|
||||
"Expected lvalues to not be null when assigning scopes. " +
|
||||
"Pruning lvalues too early can result in missing scope information.",
|
||||
instr.loc
|
||||
);
|
||||
description: null,
|
||||
loc: instr.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
for (const operand of eachInstructionLValue(instr)) {
|
||||
const operandScope = getPlaceScope(instr.id, operand);
|
||||
if (operandScope !== null) {
|
||||
|
||||
+110
-83
@@ -65,11 +65,12 @@ class Driver {
|
||||
}
|
||||
|
||||
visitBlock(block: BasicBlock, blockValue: ReactiveBlock): void {
|
||||
CompilerError.invariant(
|
||||
!this.cx.emitted.has(block.id),
|
||||
`Cannot emit the same block twice: bb${block.id}`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(!this.cx.emitted.has(block.id), {
|
||||
reason: `Cannot emit the same block twice: bb${block.id}`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
this.cx.emitted.add(block.id);
|
||||
for (const instruction of block.instructions) {
|
||||
blockValue.push({
|
||||
@@ -252,11 +253,12 @@ class Driver {
|
||||
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
|
||||
} else {
|
||||
const break_ = this.visitBreak(terminal.loop, null);
|
||||
CompilerError.invariant(
|
||||
break_ !== null,
|
||||
"If loop body is already scheduled it must be a break",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(break_ !== null, {
|
||||
reason: "If loop body is already scheduled it must be a break",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
loopBody = [break_];
|
||||
}
|
||||
|
||||
@@ -309,11 +311,12 @@ class Driver {
|
||||
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
|
||||
} else {
|
||||
const break_ = this.visitBreak(terminal.loop, null);
|
||||
CompilerError.invariant(
|
||||
break_ !== null,
|
||||
"If loop body is already scheduled it must be a break",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(break_ !== null, {
|
||||
reason: "If loop body is already scheduled it must be a break",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
loopBody = [break_];
|
||||
}
|
||||
|
||||
@@ -394,11 +397,12 @@ class Driver {
|
||||
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
|
||||
} else {
|
||||
const break_ = this.visitBreak(terminal.loop, null);
|
||||
CompilerError.invariant(
|
||||
break_ !== null,
|
||||
"If loop body is already scheduled it must be a break",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(break_ !== null, {
|
||||
reason: "If loop body is already scheduled it must be a break",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
loopBody = [break_];
|
||||
}
|
||||
|
||||
@@ -470,11 +474,12 @@ class Driver {
|
||||
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
|
||||
} else {
|
||||
const break_ = this.visitBreak(terminal.loop, null);
|
||||
CompilerError.invariant(
|
||||
break_ !== null,
|
||||
"If loop body is already scheduled it must be a break",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(break_ !== null, {
|
||||
reason: "If loop body is already scheduled it must be a break",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
loopBody = [break_];
|
||||
}
|
||||
|
||||
@@ -547,11 +552,13 @@ class Driver {
|
||||
let block: ReactiveBlock;
|
||||
if (this.cx.isScheduled(terminal.block)) {
|
||||
const break_ = this.visitBreak(terminal.block, null);
|
||||
CompilerError.invariant(
|
||||
break_ !== null,
|
||||
"Expected a break target for a label whose body is already scheduled",
|
||||
terminal.loc
|
||||
);
|
||||
CompilerError.invariant(break_ !== null, {
|
||||
reason:
|
||||
"Expected a break target for a label whose body is already scheduled",
|
||||
description: null,
|
||||
loc: terminal.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
block = [break_];
|
||||
} else {
|
||||
block = this.traverseBlock(this.cx.ir.blocks.get(terminal.block)!);
|
||||
@@ -630,11 +637,12 @@ class Driver {
|
||||
break;
|
||||
}
|
||||
case "unsupported": {
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
"Unexpected unsupported terminal",
|
||||
terminal.loc
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Unexpected unsupported terminal",
|
||||
description: null,
|
||||
loc: terminal.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(terminal, "Unexpected terminal");
|
||||
@@ -665,8 +673,13 @@ class Driver {
|
||||
CompilerError.invariant(
|
||||
instr.lvalue.identifier.id ===
|
||||
defaultBlock.terminal.test.identifier.id,
|
||||
"Expected branch block to end in an instruction that sets the test value",
|
||||
instr.lvalue.loc
|
||||
{
|
||||
reason:
|
||||
"Expected branch block to end in an instruction that sets the test value",
|
||||
description: null,
|
||||
loc: instr.lvalue.loc,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
return {
|
||||
block: defaultBlock.id,
|
||||
@@ -693,11 +706,12 @@ class Driver {
|
||||
} else if (defaultBlock.terminal.kind === "goto") {
|
||||
const instructions = defaultBlock.instructions;
|
||||
if (instructions.length === 0) {
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
"Expected goto value block to have at least one instruction",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Expected goto value block to have at least one instruction",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
} else if (defaultBlock.instructions.length === 1) {
|
||||
const instr = defaultBlock.instructions[0]!;
|
||||
let place: Place = instr.lvalue!;
|
||||
@@ -792,11 +806,12 @@ class Driver {
|
||||
case "optional": {
|
||||
const test = this.visitValueBlock(terminal.test, terminal.loc);
|
||||
const testBlock = this.cx.ir.blocks.get(test.block)!;
|
||||
CompilerError.invariant(
|
||||
testBlock.terminal.kind === "branch",
|
||||
`Unexpected terminal kind '${testBlock.terminal.kind}' for optional call test block`,
|
||||
testBlock.terminal.loc
|
||||
);
|
||||
CompilerError.invariant(testBlock.terminal.kind === "branch", {
|
||||
reason: `Unexpected terminal kind '${testBlock.terminal.kind}' for optional call test block`,
|
||||
description: null,
|
||||
loc: testBlock.terminal.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
const consequent = this.visitValueBlock(
|
||||
testBlock.terminal.consequent,
|
||||
terminal.loc
|
||||
@@ -831,11 +846,12 @@ class Driver {
|
||||
case "logical": {
|
||||
const test = this.visitValueBlock(terminal.test, terminal.loc);
|
||||
const testBlock = this.cx.ir.blocks.get(test.block)!;
|
||||
CompilerError.invariant(
|
||||
testBlock.terminal.kind === "branch",
|
||||
`Unexpected terminal kind '${testBlock.terminal.kind}' for logical test block`,
|
||||
testBlock.terminal.loc
|
||||
);
|
||||
CompilerError.invariant(testBlock.terminal.kind === "branch", {
|
||||
reason: `Unexpected terminal kind '${testBlock.terminal.kind}' for logical test block`,
|
||||
description: null,
|
||||
loc: testBlock.terminal.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
|
||||
const leftFinal = this.visitValueBlock(
|
||||
testBlock.terminal.consequent,
|
||||
@@ -876,11 +892,12 @@ class Driver {
|
||||
case "ternary": {
|
||||
const test = this.visitValueBlock(terminal.test, terminal.loc);
|
||||
const testBlock = this.cx.ir.blocks.get(test.block)!;
|
||||
CompilerError.invariant(
|
||||
testBlock.terminal.kind === "branch",
|
||||
`Unexpected terminal kind '${testBlock.terminal.kind}' for ternary test block`,
|
||||
testBlock.terminal.loc
|
||||
);
|
||||
CompilerError.invariant(testBlock.terminal.kind === "branch", {
|
||||
reason: `Unexpected terminal kind '${testBlock.terminal.kind}' for ternary test block`,
|
||||
description: null,
|
||||
loc: testBlock.terminal.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
const consequent = this.visitValueBlock(
|
||||
testBlock.terminal.consequent,
|
||||
terminal.loc
|
||||
@@ -905,11 +922,12 @@ class Driver {
|
||||
};
|
||||
}
|
||||
default: {
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
`Unexpected value block terminal kind '${terminal.kind}'`,
|
||||
terminal.loc
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected value block terminal kind '${terminal.kind}'`,
|
||||
description: null,
|
||||
loc: terminal.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -924,7 +942,12 @@ class Driver {
|
||||
): ReactiveTerminalStatement<ReactiveBreakTerminal> {
|
||||
const target = this.cx.getBreakTarget(block);
|
||||
if (target === null) {
|
||||
CompilerError.invariant(false, "Expected a break target", null);
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Expected a break target",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
switch (target.type) {
|
||||
case "implicit": {
|
||||
@@ -962,11 +985,12 @@ class Driver {
|
||||
id: InstructionId
|
||||
): ReactiveTerminalStatement<ReactiveContinueTerminal> {
|
||||
const target = this.cx.getContinueTarget(block);
|
||||
CompilerError.invariant(
|
||||
target !== null,
|
||||
`Expected continue target to be scheduled for bb${block}`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(target !== null, {
|
||||
reason: `Expected continue target to be scheduled for bb${block}`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
switch (target.type) {
|
||||
case "implicit": {
|
||||
return {
|
||||
@@ -1046,11 +1070,12 @@ class Context {
|
||||
*/
|
||||
schedule(block: BlockId, type: "if" | "switch" | "case"): number {
|
||||
const id = this.#nextScheduleId++;
|
||||
CompilerError.invariant(
|
||||
!this.#scheduled.has(block),
|
||||
`Break block is already scheduled: bb${block}`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(!this.#scheduled.has(block), {
|
||||
reason: `Break block is already scheduled: bb${block}`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
this.#scheduled.add(block);
|
||||
this.#controlFlowStack.push({ block, id, type });
|
||||
return id;
|
||||
@@ -1064,11 +1089,12 @@ class Context {
|
||||
const id = this.#nextScheduleId++;
|
||||
const ownsBlock = !this.#scheduled.has(fallthroughBlock);
|
||||
this.#scheduled.add(fallthroughBlock);
|
||||
CompilerError.invariant(
|
||||
!this.#scheduled.has(continueBlock),
|
||||
`Continue block is already scheduled: bb${continueBlock}`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(!this.#scheduled.has(continueBlock), {
|
||||
reason: `Continue block is already scheduled: bb${continueBlock}`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
this.#scheduled.add(continueBlock);
|
||||
let ownsLoop = false;
|
||||
if (loopBlock !== null) {
|
||||
@@ -1093,11 +1119,12 @@ class Context {
|
||||
*/
|
||||
unschedule(scheduleId: number): void {
|
||||
const last = this.#controlFlowStack.pop();
|
||||
CompilerError.invariant(
|
||||
last !== undefined && last.id === scheduleId,
|
||||
"Can only unschedule the last target",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(last !== undefined && last.id === scheduleId, {
|
||||
reason: "Can only unschedule the last target",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
if (last.type !== "loop" || last.ownsBlock !== null) {
|
||||
this.#scheduled.delete(last.block);
|
||||
}
|
||||
|
||||
+138
-104
@@ -215,11 +215,12 @@ function codegenReactiveScope(
|
||||
firstOutputIndex = index;
|
||||
}
|
||||
|
||||
CompilerError.invariant(
|
||||
identifier.name != null,
|
||||
`Expected identifier '@${identifier.id}' to be named`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(identifier.name != null, {
|
||||
reason: `Expected identifier '@${identifier.id}' to be named`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
|
||||
const name = convertIdentifier(identifier);
|
||||
if (!cx.hasDeclared(identifier)) {
|
||||
@@ -283,11 +284,12 @@ function codegenReactiveScope(
|
||||
null as t.Expression | null
|
||||
);
|
||||
if (testCondition === null) {
|
||||
CompilerError.invariant(
|
||||
firstOutputIndex !== null,
|
||||
`Expected scope '@${scope.id}' to have at least one declaration`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(firstOutputIndex !== null, {
|
||||
reason: `Expected scope '@${scope.id}' to have at least one declaration`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
testCondition = t.binaryExpression(
|
||||
"===",
|
||||
t.memberExpression(
|
||||
@@ -344,17 +346,19 @@ function codegenTerminal(
|
||||
);
|
||||
}
|
||||
case "for-of": {
|
||||
CompilerError.invariant(
|
||||
terminal.init.kind === "SequenceExpression",
|
||||
`Expected a sequence expression init for ForOf`,
|
||||
terminal.init.loc,
|
||||
`Got '${terminal.init.kind}' expression instead`
|
||||
);
|
||||
CompilerError.invariant(terminal.init.kind === "SequenceExpression", {
|
||||
reason: `Expected a sequence expression init for ForOf`,
|
||||
description: `Got '${terminal.init.kind}' expression instead`,
|
||||
loc: terminal.init.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
if (terminal.init.instructions.length !== 2) {
|
||||
CompilerError.todo(
|
||||
"Support non-trivial ForOf inits",
|
||||
terminal.init.loc
|
||||
);
|
||||
CompilerError.todo({
|
||||
reason: "Support non-trivial ForOf inits",
|
||||
description: null,
|
||||
loc: terminal.init.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
const iterableCollection = terminal.init.instructions[0];
|
||||
const iterableItem = terminal.init.instructions[1];
|
||||
@@ -369,12 +373,12 @@ function codegenTerminal(
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
`Expected a StoreLocal or Destructure to be assigned to the collection`,
|
||||
iterableItem.value.loc,
|
||||
`Found ${iterableItem.value.kind}`
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Expected a StoreLocal or Destructure to be assigned to the collection`,
|
||||
description: `Found ${iterableItem.value.kind}`,
|
||||
loc: iterableItem.value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
let varDeclKind: "const" | "let";
|
||||
switch (iterableItem.value.lvalue.kind) {
|
||||
@@ -385,11 +389,13 @@ function codegenTerminal(
|
||||
varDeclKind = "let" as const;
|
||||
break;
|
||||
case InstructionKind.Reassign:
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
"Destructure should never be Reassign as it would be an Object/ArrayPattern",
|
||||
iterableItem.loc
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason:
|
||||
"Destructure should never be Reassign as it would be an Object/ArrayPattern",
|
||||
description: null,
|
||||
loc: iterableItem.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
default:
|
||||
assertExhaustive(
|
||||
iterableItem.value.lvalue.kind,
|
||||
@@ -509,11 +515,13 @@ function codegenInstructionNullable(
|
||||
hasDeclaration ||= !isDeclared;
|
||||
}
|
||||
if (hasReasign && hasDeclaration) {
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
"Encountered a destructuring operation where some identifiers are already declared (reassignments) but others are not (declarations)",
|
||||
instr.loc
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason:
|
||||
"Encountered a destructuring operation where some identifiers are already declared (reassignments) but others are not (declarations)",
|
||||
description: null,
|
||||
loc: instr.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
} else if (hasReasign) {
|
||||
kind = InstructionKind.Reassign;
|
||||
}
|
||||
@@ -521,31 +529,34 @@ function codegenInstructionNullable(
|
||||
}
|
||||
switch (kind) {
|
||||
case InstructionKind.Const: {
|
||||
CompilerError.invariant(
|
||||
instr.lvalue === null,
|
||||
`Const declaration cannot be referenced as an expression`,
|
||||
instr.value.loc
|
||||
);
|
||||
CompilerError.invariant(instr.lvalue === null, {
|
||||
reason: `Const declaration cannot be referenced as an expression`,
|
||||
description: null,
|
||||
loc: instr.value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
return createVariableDeclaration(instr.loc, "const", [
|
||||
t.variableDeclarator(codegenLValue(lvalue), value),
|
||||
]);
|
||||
}
|
||||
case InstructionKind.Let: {
|
||||
CompilerError.invariant(
|
||||
instr.lvalue === null,
|
||||
`Const declaration cannot be referenced as an expression`,
|
||||
instr.value.loc
|
||||
);
|
||||
CompilerError.invariant(instr.lvalue === null, {
|
||||
reason: `Const declaration cannot be referenced as an expression`,
|
||||
description: null,
|
||||
loc: instr.value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
return createVariableDeclaration(instr.loc, "let", [
|
||||
t.variableDeclarator(codegenLValue(lvalue), value),
|
||||
]);
|
||||
}
|
||||
case InstructionKind.Reassign: {
|
||||
CompilerError.invariant(
|
||||
value !== null,
|
||||
"Expected a value for reassignment",
|
||||
instr.value.loc
|
||||
);
|
||||
CompilerError.invariant(value !== null, {
|
||||
reason: "Expected a value for reassignment",
|
||||
description: null,
|
||||
loc: instr.value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
const expr = t.assignmentExpression("=", codegenLValue(lvalue), value);
|
||||
if (instr.lvalue !== null) {
|
||||
if (instr.value.kind !== "StoreContext") {
|
||||
@@ -592,11 +603,12 @@ function codegenForInit(
|
||||
}))
|
||||
).body;
|
||||
const declaration = body[0]!;
|
||||
CompilerError.invariant(
|
||||
declaration.type === "VariableDeclaration",
|
||||
"Expected a variable declaration",
|
||||
declaration.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(declaration.type === "VariableDeclaration", {
|
||||
reason: "Expected a variable declaration",
|
||||
description: null,
|
||||
loc: declaration.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return declaration;
|
||||
} else {
|
||||
return codegenInstructionValue(cx, init);
|
||||
@@ -744,11 +756,12 @@ function codegenInstructionValue(
|
||||
switch (optionalValue.type) {
|
||||
case "OptionalCallExpression":
|
||||
case "CallExpression": {
|
||||
CompilerError.invariant(
|
||||
t.isExpression(optionalValue.callee),
|
||||
"v8 intrinsics are validated during lowering",
|
||||
optionalValue.callee.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(t.isExpression(optionalValue.callee), {
|
||||
reason: "v8 intrinsics are validated during lowering",
|
||||
description: null,
|
||||
loc: optionalValue.callee.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
value = t.optionalCallExpression(
|
||||
optionalValue.callee,
|
||||
optionalValue.arguments,
|
||||
@@ -759,11 +772,12 @@ function codegenInstructionValue(
|
||||
case "OptionalMemberExpression":
|
||||
case "MemberExpression": {
|
||||
const property = optionalValue.property;
|
||||
CompilerError.invariant(
|
||||
t.isExpression(property),
|
||||
"Private names are validated during lowering",
|
||||
property.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(t.isExpression(property), {
|
||||
reason: "Private names are validated during lowering",
|
||||
description: null,
|
||||
loc: property.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
value = t.optionalMemberExpression(
|
||||
optionalValue.object,
|
||||
property,
|
||||
@@ -773,12 +787,13 @@ function codegenInstructionValue(
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
"Expected an optional value to resolve to a call expression or member expression",
|
||||
instrValue.loc,
|
||||
`Got a '${optionalValue.type}'`
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason:
|
||||
"Expected an optional value to resolve to a call expression or member expression",
|
||||
description: `Got a '${optionalValue.type}'`,
|
||||
loc: instrValue.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -788,18 +803,28 @@ function codegenInstructionValue(
|
||||
CompilerError.invariant(
|
||||
t.isMemberExpression(memberExpr) ||
|
||||
t.isOptionalMemberExpression(memberExpr),
|
||||
"[Codegen] Internal error: MethodCall::property must be an unpromoted + unmemoized MemberExpression. " +
|
||||
`Got a '${memberExpr.type}'`,
|
||||
memberExpr.loc ?? null
|
||||
{
|
||||
reason:
|
||||
"[Codegen] Internal error: MethodCall::property must be an unpromoted + unmemoized MemberExpression. " +
|
||||
`Got a '${memberExpr.type}'`,
|
||||
description: null,
|
||||
loc: memberExpr.loc ?? null,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
CompilerError.invariant(
|
||||
t.isNodesEquivalent(
|
||||
memberExpr.object,
|
||||
codegenPlace(cx, instrValue.receiver)
|
||||
),
|
||||
"[Codegen] Internal error: Forget should always generate MethodCall::property " +
|
||||
"as a MemberExpression of MethodCall::receiver",
|
||||
memberExpr.loc ?? null
|
||||
{
|
||||
reason:
|
||||
"[Codegen] Internal error: Forget should always generate MethodCall::property " +
|
||||
"as a MemberExpression of MethodCall::receiver",
|
||||
description: null,
|
||||
loc: memberExpr.loc ?? null,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
const args = instrValue.args.map((arg) => codegenArgument(cx, arg));
|
||||
value = createCallExpression(instrValue.loc, memberExpr, args);
|
||||
@@ -851,11 +876,12 @@ function codegenInstructionValue(
|
||||
} else if (tagValue.type === "MemberExpression") {
|
||||
tag = convertMemberExpressionToJsx(tagValue);
|
||||
} else {
|
||||
CompilerError.invariant(
|
||||
tagValue.type === "StringLiteral",
|
||||
`Expected JSX tag to be an identifier or string, got '${tagValue.type}'`,
|
||||
tagValue.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(tagValue.type === "StringLiteral", {
|
||||
reason: `Expected JSX tag to be an identifier or string, got '${tagValue.type}'`,
|
||||
description: null,
|
||||
loc: tagValue.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
if (tagValue.value.indexOf(":") >= 0) {
|
||||
const [namespace, name] = tagValue.value.split(":", 2);
|
||||
tag = createJsxNamespacedName(
|
||||
@@ -1049,6 +1075,7 @@ function codegenInstructionValue(
|
||||
}'`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: declarator.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return t.stringLiteral(`TODO handle ${declarator.id}`);
|
||||
} else {
|
||||
@@ -1056,6 +1083,7 @@ function codegenInstructionValue(
|
||||
reason: `(CodegenReactiveFunction::codegenInstructionValue) Handle conversion of ${stmt.type} to expression`,
|
||||
severity: ErrorSeverity.Todo,
|
||||
loc: stmt.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return t.stringLiteral(`TODO handle ${stmt.type}`);
|
||||
}
|
||||
@@ -1105,11 +1133,12 @@ function codegenInstructionValue(
|
||||
case "Destructure":
|
||||
case "StoreLocal":
|
||||
case "StoreContext": {
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
`Unexpected ${instrValue.kind} in codegenInstructionValue`,
|
||||
instrValue.loc
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected ${instrValue.kind} in codegenInstructionValue`,
|
||||
description: null,
|
||||
loc: instrValue.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
@@ -1193,20 +1222,23 @@ function codegenJsxElement(
|
||||
function convertMemberExpressionToJsx(
|
||||
expr: t.MemberExpression
|
||||
): t.JSXMemberExpression {
|
||||
CompilerError.invariant(
|
||||
expr.property.type === "Identifier",
|
||||
"Expected JSX member expression property to be a string",
|
||||
expr.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(expr.property.type === "Identifier", {
|
||||
reason: "Expected JSX member expression property to be a string",
|
||||
description: null,
|
||||
loc: expr.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
const property = t.jsxIdentifier(expr.property.name);
|
||||
if (expr.object.type === "Identifier") {
|
||||
return t.jsxMemberExpression(t.jsxIdentifier(expr.object.name), property);
|
||||
} else {
|
||||
CompilerError.invariant(
|
||||
expr.object.type === "MemberExpression",
|
||||
"Expected JSX member expression to be an identifier or nested member expression",
|
||||
expr.object.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(expr.object.type === "MemberExpression", {
|
||||
reason:
|
||||
"Expected JSX member expression to be an identifier or nested member expression",
|
||||
description: null,
|
||||
loc: expr.object.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
const object = convertMemberExpressionToJsx(expr.object);
|
||||
return t.jsxMemberExpression(object, property);
|
||||
}
|
||||
@@ -1288,12 +1320,14 @@ function codegenPlace(cx: Context, place: Place): t.Expression {
|
||||
if (tmp != null) {
|
||||
return tmp;
|
||||
}
|
||||
CompilerError.invariant(
|
||||
place.identifier.name !== null || tmp !== undefined,
|
||||
`[Codegen] No value found for temporary`,
|
||||
place.loc,
|
||||
`Value for '${printPlace(place)}' was not set in the codegen context`
|
||||
);
|
||||
CompilerError.invariant(place.identifier.name !== null || tmp !== undefined, {
|
||||
reason: `[Codegen] No value found for temporary`,
|
||||
description: `Value for '${printPlace(
|
||||
place
|
||||
)}' was not set in the codegen context`,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
const identifier = convertIdentifier(place.identifier);
|
||||
identifier.loc = place.loc as any;
|
||||
return identifier;
|
||||
|
||||
+27
-17
@@ -126,8 +126,13 @@ export class ReactiveScopeDependencyTree {
|
||||
deps.every(
|
||||
(dep) => dep.accessType === PropertyAccessType.UnconditionalDependency
|
||||
),
|
||||
"[PropagateScopeDependencies] All dependencies must be reduced to unconditional dependencies.",
|
||||
null
|
||||
{
|
||||
reason:
|
||||
"[PropagateScopeDependencies] All dependencies must be reduced to unconditional dependencies.",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
|
||||
for (const dep of deps) {
|
||||
@@ -163,11 +168,12 @@ export class ReactiveScopeDependencyTree {
|
||||
promoteDepsFromExhaustiveConditionals(
|
||||
trees: Array<ReactiveScopeDependencyTree>
|
||||
): void {
|
||||
CompilerError.invariant(
|
||||
trees.length > 1,
|
||||
"Expected trees to be at least 2 elements long.",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(trees.length > 1, {
|
||||
reason: "Expected trees to be at least 2 elements long.",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
|
||||
for (const [id, root] of this.#roots) {
|
||||
const nodesForRootId = mapNonNull(trees, (tree) => tree.#roots.get(id));
|
||||
@@ -458,19 +464,23 @@ function addSubtreeIntersection(
|
||||
otherProperties: Array<Map<string, DependencyNode>>,
|
||||
currProperties: Map<string, DependencyNode>
|
||||
): void {
|
||||
CompilerError.invariant(
|
||||
otherProperties.length > 1,
|
||||
"[DeriveMinimalDependencies] Expected otherProperties to be at least 2 elements long.",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(otherProperties.length > 1, {
|
||||
reason:
|
||||
"[DeriveMinimalDependencies] Expected otherProperties to be at least 2 elements long.",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
|
||||
otherProperties.forEach((properties) =>
|
||||
properties.forEach((node, _) =>
|
||||
CompilerError.invariant(
|
||||
!isUnconditional(node.accessType),
|
||||
"[DeriveMinimalDependencies] Expected otherProperties to only contain unconditional nodes!",
|
||||
null
|
||||
)
|
||||
CompilerError.invariant(!isUnconditional(node.accessType), {
|
||||
reason:
|
||||
"[DeriveMinimalDependencies] Expected otherProperties to only contain unconditional nodes!",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
+6
-5
@@ -105,11 +105,12 @@ class Visitor extends ReactiveFunctionVisitor<State> {
|
||||
break;
|
||||
}
|
||||
case Effect.Unknown: {
|
||||
CompilerError.invariant(
|
||||
false,
|
||||
"Unexpected unknown effect",
|
||||
operand.loc
|
||||
);
|
||||
CompilerError.invariant(false, {
|
||||
reason: "Unexpected unknown effect",
|
||||
description: null,
|
||||
loc: operand.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
|
||||
+6
-5
@@ -226,11 +226,12 @@ function printTerminal(writer: Writer, terminal: ReactiveTerminal): void {
|
||||
writer.writeLine(`${prefix}: {`);
|
||||
writer.indented(() => {
|
||||
const block = case_.block;
|
||||
CompilerError.invariant(
|
||||
block != null,
|
||||
"Expected case to have a block",
|
||||
case_.test?.loc ?? null
|
||||
);
|
||||
CompilerError.invariant(block != null, {
|
||||
reason: "Expected case to have a block",
|
||||
description: null,
|
||||
loc: case_.test?.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
printReactiveInstructions(writer, block);
|
||||
});
|
||||
writer.writeLine("}");
|
||||
|
||||
+7
-5
@@ -75,11 +75,13 @@ export function promoteUsedTemporaries(fn: ReactiveFunction): void {
|
||||
}
|
||||
|
||||
function promoteTemporary(identifier: Identifier, state: VisitorState): void {
|
||||
CompilerError.invariant(
|
||||
identifier.name === null,
|
||||
"promoteTemporary: Expected to be called only for temporary variables",
|
||||
GeneratedSource
|
||||
);
|
||||
CompilerError.invariant(identifier.name === null, {
|
||||
reason:
|
||||
"promoteTemporary: Expected to be called only for temporary variables",
|
||||
description: null,
|
||||
loc: GeneratedSource,
|
||||
suggestions: null,
|
||||
});
|
||||
if (state.tags.has(identifier.id)) {
|
||||
identifier.name = `T${state.nextId++}`;
|
||||
} else {
|
||||
|
||||
+7
-6
@@ -459,12 +459,13 @@ class PropagationVisitor extends ReactiveFunctionVisitor<Context> {
|
||||
// OptionalExpression value is a SequenceExpression where the instructions
|
||||
// represent the code prior to the `?` and the final value represents the
|
||||
// conditional code that follows.
|
||||
CompilerError.invariant(
|
||||
inner.kind === "SequenceExpression",
|
||||
"Expected OptionalExpression value to be a SequenceExpression",
|
||||
value.loc,
|
||||
`Found a '${value.kind}'`
|
||||
);
|
||||
CompilerError.invariant(inner.kind === "SequenceExpression", {
|
||||
reason:
|
||||
"Expected OptionalExpression value to be a SequenceExpression",
|
||||
description: `Found a '${value.kind}'`,
|
||||
loc: value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
// Instructions are the unconditionally executed portion before the `?`
|
||||
for (const instr of inner.instructions) {
|
||||
this.visitInstruction(instr, context);
|
||||
|
||||
+24
-16
@@ -243,11 +243,12 @@ class State {
|
||||
this.scopes.set(scope.id, node);
|
||||
}
|
||||
const identifierNode = this.identifiers.get(identifier);
|
||||
CompilerError.invariant(
|
||||
identifierNode !== undefined,
|
||||
"Expected identifier to be initialized",
|
||||
place.loc
|
||||
);
|
||||
CompilerError.invariant(identifierNode !== undefined, {
|
||||
reason: "Expected identifier to be initialized",
|
||||
description: null,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
identifierNode.scopes.add(scope.id);
|
||||
}
|
||||
}
|
||||
@@ -264,11 +265,12 @@ function computeMemoizedIdentifiers(state: State): Set<IdentifierId> {
|
||||
// Visit an identifier, optionally forcing it to be memoized
|
||||
function visit(id: IdentifierId, forceMemoize: boolean = false): boolean {
|
||||
const node = state.identifiers.get(id);
|
||||
CompilerError.invariant(
|
||||
node !== undefined,
|
||||
`Expected a node for all identifiers, none found for '${id}'`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(node !== undefined, {
|
||||
reason: `Expected a node for all identifiers, none found for '${id}'`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
if (node.seen) {
|
||||
return node.memoized;
|
||||
}
|
||||
@@ -303,11 +305,12 @@ function computeMemoizedIdentifiers(state: State): Set<IdentifierId> {
|
||||
// Force all the scope's optionally-memoizeable dependencies (not "Never") to be memoized
|
||||
function forceMemoizeScopeDependencies(id: ScopeId): void {
|
||||
const node = state.scopes.get(id);
|
||||
CompilerError.invariant(
|
||||
node !== undefined,
|
||||
"Expected a node for all scopes",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(node !== undefined, {
|
||||
reason: "Expected a node for all scopes",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
if (node.seen) {
|
||||
return;
|
||||
}
|
||||
@@ -606,7 +609,12 @@ function computeMemoizationInputs(
|
||||
};
|
||||
}
|
||||
case "UnsupportedNode": {
|
||||
CompilerError.invariant(false, `Unexpected unsupported node`, value.loc);
|
||||
CompilerError.invariant(false, {
|
||||
reason: `Unexpected unsupported node`,
|
||||
description: null,
|
||||
loc: value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(value, `Unexpected value kind '${(value as any).kind}'`);
|
||||
|
||||
+6
-1
@@ -103,6 +103,11 @@ class Scopes {
|
||||
this.#stack.push(next);
|
||||
fn();
|
||||
const last = this.#stack.pop();
|
||||
CompilerError.invariant(last === next, "Mismatch push/pop calls", null);
|
||||
CompilerError.invariant(last === next, {
|
||||
reason: "Mismatch push/pop calls",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,11 +83,12 @@ export function eliminateRedundantPhi(fn: HIRFunction): void {
|
||||
same = operand;
|
||||
}
|
||||
}
|
||||
CompilerError.invariant(
|
||||
same !== null,
|
||||
"Expected phis to be non-empty",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(same !== null, {
|
||||
reason: "Expected phis to be non-empty",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
rewrites.set(phi.id, same);
|
||||
block.phis.delete(phi);
|
||||
}
|
||||
|
||||
@@ -67,11 +67,12 @@ class SSABuilder {
|
||||
}
|
||||
|
||||
state(): State {
|
||||
CompilerError.invariant(
|
||||
this.#current !== null,
|
||||
"we need to be in a block to access state!",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(this.#current !== null, {
|
||||
reason: "we need to be in a block to access state!",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
return this.#states.get(this.#current)!;
|
||||
}
|
||||
|
||||
@@ -96,12 +97,12 @@ class SSABuilder {
|
||||
|
||||
definePlace(oldPlace: Place): Place {
|
||||
const oldId = oldPlace.identifier;
|
||||
CompilerError.invariant(
|
||||
!this.#unknown.has(oldId),
|
||||
`EnterSSA: Expected identifier to be defined before being used`,
|
||||
oldPlace.loc,
|
||||
`Identifier ${printIdentifier(oldId)} is undefined`
|
||||
);
|
||||
CompilerError.invariant(!this.#unknown.has(oldId), {
|
||||
reason: `EnterSSA: Expected identifier to be defined before being used`,
|
||||
description: `Identifier ${printIdentifier(oldId)} is undefined`,
|
||||
loc: oldPlace.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
|
||||
// Do not redefine context references.
|
||||
if (this.#context.has(oldId)) {
|
||||
@@ -237,11 +238,12 @@ function enterSSAImpl(
|
||||
): void {
|
||||
const visitedBlocks: Set<BasicBlock> = new Set();
|
||||
for (const [blockId, block] of func.body.blocks) {
|
||||
CompilerError.invariant(
|
||||
!visitedBlocks.has(block),
|
||||
`found a cycle! visiting bb${block.id} again`,
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(!visitedBlocks.has(block), {
|
||||
reason: `found a cycle! visiting bb${block.id} again`,
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
visitedBlocks.add(block);
|
||||
|
||||
builder.startBlock(block);
|
||||
@@ -249,11 +251,12 @@ function enterSSAImpl(
|
||||
if (blockId === rootEntry) {
|
||||
// NOTE: func.context should be empty for the root function
|
||||
if (func.env.enableOptimizeFunctionExpressions) {
|
||||
CompilerError.invariant(
|
||||
func.context.length === 0,
|
||||
`Expected function context to be empty for outer function declarations`,
|
||||
func.loc
|
||||
);
|
||||
CompilerError.invariant(func.context.length === 0, {
|
||||
reason: `Expected function context to be empty for outer function declarations`,
|
||||
description: null,
|
||||
loc: func.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
} else {
|
||||
func.context = func.context.map((p) => builder.defineContext(p));
|
||||
}
|
||||
@@ -270,11 +273,13 @@ function enterSSAImpl(
|
||||
) {
|
||||
const loweredFunc = instr.value.loweredFunc;
|
||||
const entry = loweredFunc.body.blocks.get(loweredFunc.body.entry)!;
|
||||
CompilerError.invariant(
|
||||
entry.preds.size === 0,
|
||||
"Expected function expression entry block to have zero predecessors",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(entry.preds.size === 0, {
|
||||
reason:
|
||||
"Expected function expression entry block to have zero predecessors",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
entry.preds.add(blockId);
|
||||
builder.defineFunction(loweredFunc);
|
||||
builder.enter(() => {
|
||||
|
||||
@@ -131,12 +131,12 @@ export function leaveSSA(fn: HIRFunction): void {
|
||||
if (value.kind === "DeclareLocal") {
|
||||
const name = value.lvalue.place.identifier.name;
|
||||
if (name !== null) {
|
||||
CompilerError.invariant(
|
||||
!declarations.has(name),
|
||||
`Unexpected duplicate declaration`,
|
||||
value.lvalue.place.loc,
|
||||
`Found duplicate declaration for '${name}'`
|
||||
);
|
||||
CompilerError.invariant(!declarations.has(name), {
|
||||
reason: `Unexpected duplicate declaration`,
|
||||
description: `Found duplicate declaration for '${name}'`,
|
||||
loc: value.lvalue.place.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
declarations.set(name, {
|
||||
lvalue: value.lvalue,
|
||||
place: value.lvalue.place,
|
||||
@@ -153,8 +153,12 @@ export function leaveSSA(fn: HIRFunction): void {
|
||||
) {
|
||||
CompilerError.invariant(
|
||||
originalLVal !== undefined || block.kind === "block",
|
||||
`TODO: Handle reassignment in a value block where the original declaration was removed by dead code elimination (DCE)`,
|
||||
value.lvalue.place.loc
|
||||
{
|
||||
reason: `TODO: Handle reassignment in a value block where the original declaration was removed by dead code elimination (DCE)`,
|
||||
description: null,
|
||||
loc: value.lvalue.place.loc,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
declarations.set(value.lvalue.place.identifier.name, {
|
||||
lvalue: value.lvalue,
|
||||
@@ -176,9 +180,14 @@ export function leaveSSA(fn: HIRFunction): void {
|
||||
if (place.identifier.name == null) {
|
||||
CompilerError.invariant(
|
||||
kind === null || kind === InstructionKind.Const,
|
||||
`Expected consistent kind for destructuring`,
|
||||
place.loc,
|
||||
`other places were '${kind}' but '${printPlace(place)}' is const`
|
||||
{
|
||||
reason: `Expected consistent kind for destructuring`,
|
||||
description: `other places were '${kind}' but '${printPlace(
|
||||
place
|
||||
)}' is const`,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
kind = InstructionKind.Const;
|
||||
} else {
|
||||
@@ -189,8 +198,12 @@ export function leaveSSA(fn: HIRFunction): void {
|
||||
) {
|
||||
CompilerError.invariant(
|
||||
originalLVal !== undefined || block.kind !== "value",
|
||||
`TODO: Handle reassignment in a value block where the original declaration was removed by dead code elimination (DCE)`,
|
||||
place.loc
|
||||
{
|
||||
reason: `TODO: Handle reassignment in a value block where the original declaration was removed by dead code elimination (DCE)`,
|
||||
description: null,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
declarations.set(place.identifier.name, {
|
||||
lvalue: value.lvalue,
|
||||
@@ -198,32 +211,39 @@ export function leaveSSA(fn: HIRFunction): void {
|
||||
});
|
||||
CompilerError.invariant(
|
||||
kind === null || kind === InstructionKind.Const,
|
||||
`Expected consistent kind for destructuring`,
|
||||
place.loc,
|
||||
`Other places were '${kind}' but '${printPlace(
|
||||
place
|
||||
)}' is const`
|
||||
{
|
||||
reason: `Expected consistent kind for destructuring`,
|
||||
description: `Other places were '${kind}' but '${printPlace(
|
||||
place
|
||||
)}' is const`,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
kind = InstructionKind.Const;
|
||||
} else {
|
||||
CompilerError.invariant(
|
||||
kind === null || kind === InstructionKind.Reassign,
|
||||
`Expected consistent kind for destructuring`,
|
||||
place.loc,
|
||||
`Other places were '${kind}' but '${printPlace(
|
||||
place
|
||||
)}' is reassigned`
|
||||
{
|
||||
reason: `Expected consistent kind for destructuring`,
|
||||
description: `Other places were '${kind}' but '${printPlace(
|
||||
place
|
||||
)}' is reassigned`,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
}
|
||||
);
|
||||
kind = InstructionKind.Reassign;
|
||||
originalLVal.lvalue.kind = InstructionKind.Let;
|
||||
}
|
||||
}
|
||||
}
|
||||
CompilerError.invariant(
|
||||
kind !== null,
|
||||
"Expected at least one operand",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(kind !== null, {
|
||||
reason: "Expected at least one operand",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
value.lvalue.kind = kind;
|
||||
}
|
||||
rewritePlace(lvalue, rewrites, declarations);
|
||||
@@ -356,11 +376,12 @@ export function leaveSSA(fn: HIRFunction): void {
|
||||
|
||||
// If we never saw a declaration for this phi, it may have been pruned by DCE, so synthesize
|
||||
// a new Let binding
|
||||
CompilerError.invariant(
|
||||
phi.id.name != null,
|
||||
"Expected reassignment phis to have a name",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(phi.id.name != null, {
|
||||
reason: "Expected reassignment phis to have a name",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
const declaration = declarations.get(phi.id.name);
|
||||
if (declaration === undefined) {
|
||||
let initValue: Place;
|
||||
|
||||
@@ -342,11 +342,12 @@ class Unifier {
|
||||
if (type.kind === "Phi") {
|
||||
const operands = new Set(type.operands.map((i) => this.get(i).kind));
|
||||
|
||||
CompilerError.invariant(
|
||||
operands.size > 0,
|
||||
"there should be at least one operand",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(operands.size > 0, {
|
||||
reason: "there should be at least one operand",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
const kind = operands.values().next().value;
|
||||
|
||||
// there's only one unique type and it's not a type var
|
||||
|
||||
@@ -20,11 +20,12 @@ export default class DisjointSet<T> {
|
||||
*/
|
||||
union(items: Array<T>): void {
|
||||
const first = items.shift();
|
||||
CompilerError.invariant(
|
||||
first != null,
|
||||
"Expected set to be non-empty",
|
||||
null
|
||||
);
|
||||
CompilerError.invariant(first != null, {
|
||||
reason: "Expected set to be non-empty",
|
||||
description: null,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
});
|
||||
// determine an arbitrary "root" for this set: if the first
|
||||
// item already has a root then use that, otherwise the first item
|
||||
// will be the new root.
|
||||
|
||||
+1
@@ -138,6 +138,7 @@ function validateOperand(
|
||||
reason:
|
||||
"Cannot use a mutable function where an immutable value is expected",
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ export function validateHooksUsage(fn: HIRFunction): void {
|
||||
"Hooks may not be referenced as normal values, they must be called. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)",
|
||||
loc: typeof place.loc !== "symbol" ? place.loc : null,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
+2
@@ -109,6 +109,7 @@ function validateNonRefValue(error: CompilerError, operand: Place): void {
|
||||
reason:
|
||||
"Ref values (the `current` property) may not be accessed during render",
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -124,6 +125,7 @@ function validateNonRefObject(error: CompilerError, operand: Place): void {
|
||||
reason:
|
||||
"Ref values may not be passed to functions because they could read the ref value (`current` property) during render",
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
+1
@@ -98,6 +98,7 @@ export function validateUnconditionalHooks(
|
||||
"Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)",
|
||||
loc,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export { default as BabelPlugin } from "./Babel/BabelPlugin";
|
||||
export {
|
||||
CompilerError,
|
||||
CompilerErrorDetail,
|
||||
CompilerSuggestionOperation,
|
||||
ErrorSeverity,
|
||||
} from "./CompilerError";
|
||||
export {
|
||||
|
||||
+66
-5
@@ -8,6 +8,7 @@
|
||||
import { transformFromAstSync } from "@babel/core";
|
||||
import type { SourceLocation as BabelSourceLocation } from "@babel/types";
|
||||
import ReactForgetBabelPlugin, {
|
||||
CompilerSuggestionOperation,
|
||||
ErrorSeverity,
|
||||
type CompilerError,
|
||||
type CompilerErrorDetail,
|
||||
@@ -20,6 +21,10 @@ type CompilerErrorDetailWithLoc = Omit<CompilerErrorDetail, "loc"> & {
|
||||
loc: BabelSourceLocation;
|
||||
};
|
||||
|
||||
function assertExhaustive(_: never, errorMsg: string): never {
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
function isReactForgetCompilerError(err: Error): err is CompilerError {
|
||||
return err.name === "ReactForgetCompilerError";
|
||||
}
|
||||
@@ -63,6 +68,7 @@ const rule: Rule.RuleModule = {
|
||||
description: "Surfaces diagnostics from React Forget",
|
||||
recommended: true,
|
||||
},
|
||||
fixable: "code",
|
||||
},
|
||||
create(context: Rule.RuleContext) {
|
||||
// Compat with older versions of eslint
|
||||
@@ -90,12 +96,67 @@ const rule: Rule.RuleModule = {
|
||||
} catch (err) {
|
||||
if (isReactForgetCompilerError(err) && Array.isArray(err.details)) {
|
||||
for (const detail of err.details) {
|
||||
if (isReportableDiagnostic(detail)) {
|
||||
context.report({
|
||||
message: detail.toString(),
|
||||
loc: detail.loc,
|
||||
});
|
||||
if (!isReportableDiagnostic(detail)) {
|
||||
continue;
|
||||
}
|
||||
let suggest: Array<Rule.SuggestionReportDescriptor> = [];
|
||||
if (Array.isArray(detail.suggestions)) {
|
||||
for (const suggestion of detail.suggestions) {
|
||||
switch (suggestion.op) {
|
||||
case CompilerSuggestionOperation.InsertBefore:
|
||||
suggest.push({
|
||||
desc: suggestion.description,
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBeforeRange(
|
||||
suggestion.range,
|
||||
suggestion.text
|
||||
);
|
||||
},
|
||||
});
|
||||
break;
|
||||
case CompilerSuggestionOperation.InsertAfter:
|
||||
suggest.push({
|
||||
desc: suggestion.description,
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfterRange(
|
||||
suggestion.range,
|
||||
suggestion.text
|
||||
);
|
||||
},
|
||||
});
|
||||
break;
|
||||
case CompilerSuggestionOperation.Replace:
|
||||
suggest.push({
|
||||
desc: suggestion.description,
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange(
|
||||
suggestion.range,
|
||||
suggestion.text
|
||||
);
|
||||
},
|
||||
});
|
||||
break;
|
||||
case CompilerSuggestionOperation.Remove:
|
||||
suggest.push({
|
||||
desc: suggestion.description,
|
||||
fix(fixer) {
|
||||
return fixer.removeRange(suggestion.range);
|
||||
},
|
||||
});
|
||||
break;
|
||||
default:
|
||||
assertExhaustive(
|
||||
suggestion,
|
||||
"Unhandled suggestion operation"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
context.report({
|
||||
message: detail.toString(),
|
||||
loc: detail.loc,
|
||||
suggest,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw err;
|
||||
|
||||
Reference in New Issue
Block a user