@samitouri / QOS-React-2 / commits / 9c1f8a962c

Make CompilerError.reason a static string

While running the latest Forget build on www I noticed that a lot of the bailouts were special-cases where we used interpolation in the error `reason` string to provide more context for debugging. This is a pretty cool result, because it means that we actually support nearly all the common syntax (at least based on a sample of the codebase). But it makes our tools for aggregating errors break down a bit. This PR adds a new, nullable `description` property to CompilerErrorDetail, and manually updates to ensure that we always pass a static `reason` and only use interpolation in the `description`. This will allow our aggregation tools to group by the reason.

Joe Savona committed Apr 6, 2023 at 08:51 UTC 9c1f8a962c1f15935749e604cac2d095c952af04
11 files changed +74 -44
compiler/forget/src/CompilerError.ts
+27 -4
@@ -19,6 +19,7 @@ export enum ErrorSeverity {
19
20 export type CompilerErrorOptions = {
21 reason: string;
22 + description?: string | null | undefined;
23 severity: ErrorSeverity;
24 nodePath: AnyNodePath | null;
25 };
@@ -64,7 +65,8 @@ export function tryPrintCodeFrame(
65 try {
66 return options.nodePath
67 .buildCodeFrameError(
67 - options.reason,
68 + options.reason +
69 + (options.description != null ? `. ${options.description}` : ""),
70 mapSeverityToErrorCtor(options.severity)
71 )
72 .toString();
@@ -79,12 +81,14 @@ export function tryPrintCodeFrame(
81 */
82 export class CompilerErrorDetail {
83 reason: string;
84 + description: string | null;
85 severity: ErrorSeverity;
86 codeframe: string | null;
87 loc: BabelSourceLocation | null;
88
89 constructor(options: CompilerErrorDetailOptions) {
90 this.reason = options.reason;
91 + this.description = options.description;
92 this.severity = options.severity;
93 this.codeframe = options.codeframe;
94 this.loc = options.loc;
@@ -95,6 +99,9 @@ export class CompilerErrorDetail {
99 return this.codeframe;
100 }
101 const buffer = [`${this.severity}: ${this.reason}`];
102 + if (this.description !== null) {
103 + buffer.push(`. ${this.description}`);
104 + }
105 if (this.loc != null) {
106 buffer.push(` (${this.loc.start.line}:${this.loc.end.line})`);
107 }
@@ -109,11 +116,16 @@ export class CompilerErrorDetail {
116 export class CompilerError extends Error {
117 details: CompilerErrorDetail[] = [];
118
112 - static invariant(reason: string, loc: SourceLocation): never {
119 + static invariant(
120 + reason: string,
121 + loc: SourceLocation,
122 + description: string | null = null
123 + ): never {
124 const errors = new CompilerError();
125 errors.pushErrorDetail(
126 new CompilerErrorDetail({
127 codeframe: null,
128 + description,
129 loc: typeof loc === "symbol" ? null : loc,
130 reason,
131 severity: ErrorSeverity.Invariant,
@@ -122,11 +134,16 @@ export class CompilerError extends Error {
134 throw errors;
135 }
136
125 - static todo(reason: string, loc: SourceLocation): never {
137 + static todo(
138 + reason: string,
139 + loc: SourceLocation,
140 + description: string | null = null
141 + ): never {
142 const errors = new CompilerError();
143 errors.pushErrorDetail(
144 new CompilerErrorDetail({
145 codeframe: null,
146 + description,
147 loc: typeof loc === "symbol" ? null : loc,
148 reason,
149 severity: ErrorSeverity.Todo,
@@ -135,11 +152,16 @@ export class CompilerError extends Error {
152 throw errors;
153 }
154
138 - static invalidInput(reason: string, loc: SourceLocation): never {
155 + static invalidInput(
156 + reason: string,
157 + loc: SourceLocation,
158 + description: string | null = null
159 + ): never {
160 const errors = new CompilerError();
161 errors.pushErrorDetail(
162 new CompilerErrorDetail({
163 codeframe: null,
164 + description,
165 loc: typeof loc === "symbol" ? null : loc,
166 reason,
167 severity: ErrorSeverity.InvalidInput,
@@ -165,6 +187,7 @@ export class CompilerError extends Error {
187 push(options: CompilerErrorOptions): CompilerErrorDetail {
188 const detail = new CompilerErrorDetail({
189 reason: options.reason,
190 + description: options.description ?? null,
191 severity: options.severity,
192 codeframe: tryPrintCodeFrame(options),
193 loc: options.nodePath?.node?.loc ?? null,
compiler/forget/src/HIR/ValidateConsistentIdentifiers.ts
+9 -8
@@ -37,16 +37,16 @@ export function validateConsistentIdentifiers(fn: HIRFunction): void {
37 for (const instr of block.instructions) {
38 if (instr.lvalue.identifier.name !== null) {
39 CompilerError.invariant(
40 - `Expected all lvalues to be temporaries, found '${instr.lvalue.identifier.name}'`,
41 - instr.lvalue.loc
40 + `Expected all lvalues to be temporaries`,
41 + instr.lvalue.loc,
42 + `Found named lvalue '${instr.lvalue.identifier.name}'`
43 );
44 }
45 if (assignments.has(instr.lvalue.identifier.id)) {
46 CompilerError.invariant(
46 - `Expected lvalues to be assigned exactly once, found duplicate assignment of '${printPlace(
47 - instr.lvalue
48 - )}'`,
49 - instr.lvalue.loc
47 + `Expected lvalues to be assigned exactly once`,
48 + instr.lvalue.loc,
49 + `Found duplicate assignment of '${printPlace(instr.lvalue)}'`
50 );
51 }
52 assignments.add(instr.lvalue.identifier.id);
@@ -75,8 +75,9 @@ function validate(
75 identifiers.set(identifier.id, identifier);
76 } else if (identifier !== previous) {
77 CompilerError.invariant(
78 - `Duplicate identifier for id ${identifier.id}`,
79 - loc ?? GeneratedSource
78 + `Duplicate identifier object`,
79 + loc ?? GeneratedSource,
80 + `Found duplicate identifier object for id ${identifier.id}`
81 );
82 }
83 }
compiler/forget/src/HIR/ValidateTerminalSuccessors.ts
+3 -2
@@ -15,10 +15,11 @@ export function validateTerminalSuccessors(fn: HIRFunction): void {
15 mapTerminalSuccessors(block.terminal, (successor) => {
16 if (!fn.body.blocks.has(successor)) {
17 CompilerError.invariant(
18 + `Terminal successor references unknown block`,
19 + (block.terminal as any).loc ?? GeneratedSource,
20 `Block bb${successor} does not exist for terminal '${printTerminal(
21 block.terminal
20 - )}'`,
21 - (block.terminal as any).loc ?? GeneratedSource
22 + )}'`
23 );
24 }
25 return successor;
compiler/forget/src/Inference/InferReferenceEffects.ts
+7 -7
@@ -219,10 +219,9 @@ class InferenceState {
219 }
220 if (mergedKind === null) {
221 CompilerError.invariant(
222 - `InferReferenceEffects::kind: Expected at least one value at '${printPlace(
223 - place
224 - )}'`,
225 - place.loc
222 + `InferReferenceEffects::kind: Expected at least one value`,
223 + place.loc,
224 + `No value found at '${printPlace(place)}'`
225 );
226 }
227 return mergedKind;
@@ -312,10 +311,11 @@ class InferenceState {
311 } else {
312 if (shouldError) {
313 CompilerError.invalidInput(
315 - `InferReferenceEffects: inferred mutation of known immutable value ${printIdentifier(
314 + `InferReferenceEffects: inferred mutation of known immutable value`,
315 + place.loc,
316 + `Found mutation of ${printIdentifier(
317 place.identifier
317 - )}${printType(place.identifier.type)} (${valueKind})`,
318 - place.loc
318 + )}${printType(place.identifier.type)} (${valueKind})`
319 );
320 }
321 effect = Effect.Read;
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+6 -4
@@ -318,8 +318,9 @@ function codegenTerminal(
318 case "for-of": {
319 if (terminal.init.kind !== "SequenceExpression") {
320 CompilerError.invariant(
321 - `Expected a sequence expression init for ForOf, got: ${terminal.init.kind}`,
322 - terminal.init.loc
321 + `Expected a sequence expression init for ForOf`,
322 + terminal.init.loc,
323 + `Got '${terminal.init.kind}' expression instead`
324 );
325 }
326 if (terminal.init.instructions.length !== 2) {
@@ -342,8 +343,9 @@ function codegenTerminal(
343 }
344 default:
345 CompilerError.invariant(
345 - `Expected a StoreLocal or Destructure to be assigned to the collection, got: ${iterableItem.value.kind}`,
346 - iterableItem.value.loc
346 + `Expected a StoreLocal or Destructure to be assigned to the collection`,
347 + iterableItem.value.loc,
348 + `Found ${iterableItem.value.kind}`
349 );
350 }
351 let varDeclKind: "const" | "let";
compiler/forget/src/SSA/EnterSSA.ts
+3 -4
@@ -86,10 +86,9 @@ class SSABuilder {
86 const oldId = oldPlace.identifier;
87 if (this.#unknown.has(oldId)) {
88 CompilerError.invariant(
89 - `identifier ${printIdentifier(
90 - oldId
91 - )} should have been defined before use`,
92 - oldPlace.loc
89 + `EnterSSA: Expected identifier to be defined before being used`,
90 + oldPlace.loc,
91 + `Identifier ${printIdentifier(oldId)} is undfined`
92 );
93 }
94
compiler/forget/src/SSA/LeaveSSA.ts
+15 -11
@@ -133,8 +133,9 @@ export function leaveSSA(fn: HIRFunction): void {
133 if (name !== null) {
134 if (declarations.has(name)) {
135 CompilerError.invariant(
136 - `Unexpected duplicate declaration of '${name}'`,
137 - value.lvalue.place.loc
136 + `Unexpected duplicate declaration`,
137 + value.lvalue.place.loc,
138 + `Found duplicate declaration for '${name}'`
139 );
140 }
141 declarations.set(name, {
@@ -177,10 +178,11 @@ export function leaveSSA(fn: HIRFunction): void {
178 if (place.identifier.name == null) {
179 if (kind !== null && kind !== InstructionKind.Const) {
180 CompilerError.invariant(
180 - `Expected consistent kind for destructuring, other places were '${kind}' but '${printPlace(
181 + `Expected consistent kind for destructuring`,
182 + place.loc,
183 + `other places were '${kind}' but '${printPlace(
184 place
182 - )}' is const`,
183 - place.loc
185 + )}' is const`
186 );
187 }
188 kind = InstructionKind.Const;
@@ -202,20 +204,22 @@ export function leaveSSA(fn: HIRFunction): void {
204 });
205 if (kind !== null && kind !== InstructionKind.Const) {
206 CompilerError.invariant(
205 - `Expected consistent kind for destructuring, other places were '${kind}' but '${printPlace(
207 + `Expected consistent kind for destructuring`,
208 + place.loc,
209 + `Other places were '${kind}' but '${printPlace(
210 place
207 - )}' is const`,
208 - place.loc
211 + )}' is const`
212 );
213 }
214 kind = InstructionKind.Const;
215 } else {
216 if (kind !== null && kind !== InstructionKind.Reassign) {
217 CompilerError.invariant(
215 - `Expected consistent kind for destructuring, other places were '${kind}' but '${printPlace(
218 + `Expected consistent kind for destructuring`,
219 + place.loc,
220 + `Other places were '${kind}' but '${printPlace(
221 place
217 - )}' is reassigned`,
218 - place.loc
222 + )}' is reassigned`
223 );
224 }
225 kind = InstructionKind.Reassign;
compiler/forget/src/__tests__/fixtures/compiler/error.hoisted-function-declaration.expect.md
+1 -1
@@ -17,7 +17,7 @@ function component(a) {
17 ## Error
18
19 ```
20 -[ReactForget] Invariant: identifier x$6 should have been defined before use (4:4)
20 +[ReactForget] Invariant: EnterSSA: Expected identifier to be defined before being used. Identifier x$6 is undfined (4:4)
21 ```
22
23
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/error.mutate-after-aliased-freeze.expect.md
+1 -1
@@ -25,7 +25,7 @@ function Component(props) {
25 ## Error
26
27 ```
28 -[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value $42:TObject<BuiltInArray> (frozen) (13:13)
28 +[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $42:TObject<BuiltInArray> (frozen) (13:13)
29 ```
30
31
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/error.mutate-after-freeze.expect.md
+1 -1
@@ -19,7 +19,7 @@ function Component(props) {
19 ## Error
20
21 ```
22 -[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value $25:TObject<BuiltInArray> (frozen) (7:7)
22 +[ReactForget] InvalidInput: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $25:TObject<BuiltInArray> (frozen) (7:7)
23 ```
24
25
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/error.mutate-captured-arg-separately.expect.md
+1 -1
@@ -19,7 +19,7 @@ function component(a) {
19 ## Error
20
21 ```
22 -[ReactForget] Invariant: identifier x$2 should have been defined before use (7:7)
22 +[ReactForget] Invariant: EnterSSA: Expected identifier to be defined before being used. Identifier x$2 is undfined (7:7)
23 ```
24
25
\ No newline at end of file