@samitouri / QOS-React / commits / 2e0927dc70

[compiler] Remove local CompilerError accumulators, emit directly to env.recordError() (#35882)

Removes unnecessary indirection in 17 compiler passes that previously accumulated errors in a local `CompilerError` instance before flushing them to `env.recordErrors()` at the end of each pass. Errors are now emitted directly via `env.recordError()` as they're discovered. For passes with recursive error-detection patterns (ValidateNoRefAccessInRender, ValidateNoSetStateInRender), the internal accumulator is kept but flushed via individual `recordError()` calls. For InferMutationAliasingRanges, a `shouldRecordErrors` flag preserves the conditional suppression logic. For TransformFire, the throw-based error propagation is replaced with direct recording plus an early-exit check in Pipeline.ts. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35882). * #35888 * #35884 * #35883 * __->__ #35882

Joseph Savona committed Feb 23, 2026 at 16:11 UTC 2e0927dc70d75563192156aa3d504f5e14d3d0c7
16 files changed +780 -649
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+594 -458
@@ -11,6 +11,7 @@ import invariant from 'invariant';
11 import {
12 CompilerDiagnostic,
13 CompilerError,
14 + CompilerErrorDetail,
15 CompilerSuggestionOperation,
16 ErrorCategory,
17 } from '../CompilerError';
@@ -105,7 +106,7 @@ export function lower(
106 if (param.isIdentifier()) {
107 const binding = builder.resolveIdentifier(param);
108 if (binding.kind !== 'Identifier') {
108 - builder.errors.pushDiagnostic(
109 + builder.recordError(
110 CompilerDiagnostic.create({
111 category: ErrorCategory.Invariant,
112 reason: 'Could not find binding',
@@ -169,7 +170,7 @@ export function lower(
170 'Assignment',
171 );
172 } else {
172 - builder.errors.pushDiagnostic(
173 + builder.recordError(
174 CompilerDiagnostic.create({
175 category: ErrorCategory.Todo,
176 reason: `Handle ${param.node.type} parameters`,
@@ -200,7 +201,7 @@ export function lower(
201 lowerStatement(builder, body);
202 directives = body.get('directives').map(d => d.node.value.value);
203 } else {
203 - builder.errors.pushDiagnostic(
204 + builder.recordError(
205 CompilerDiagnostic.create({
206 category: ErrorCategory.Syntax,
207 reason: `Unexpected function body kind`,
@@ -217,7 +218,9 @@ export function lower(
218 if (id != null) {
219 const idResult = validateIdentifierName(id);
220 if (idResult.isErr()) {
220 - builder.errors.merge(idResult.unwrapErr());
221 + for (const detail of idResult.unwrapErr().details) {
222 + builder.recordError(detail);
223 + }
224 } else {
225 validatedId = idResult.unwrap().value;
226 }
@@ -241,11 +244,6 @@ export function lower(
244
245 const hirBody = builder.build();
246
244 - // Record all accumulated errors (including any from build()) on env
245 - if (builder.errors.hasAnyErrors()) {
246 - env.recordErrors(builder.errors);
247 - }
248 -
247 return {
248 id: validatedId,
249 nameHint: null,
@@ -282,13 +280,15 @@ function lowerStatement(
280 * for control-flow and is generally considered an anti-pattern. we can likely
281 * just not support this pattern, unless it really becomes necessary for some reason.
282 */
285 - builder.errors.push({
286 - reason:
287 - '(BuildHIR::lowerStatement) Support ThrowStatement inside of try/catch',
288 - category: ErrorCategory.Todo,
289 - loc: stmt.node.loc ?? null,
290 - suggestions: null,
291 - });
283 + builder.recordError(
284 + new CompilerErrorDetail({
285 + reason:
286 + '(BuildHIR::lowerStatement) Support ThrowStatement inside of try/catch',
287 + category: ErrorCategory.Todo,
288 + loc: stmt.node.loc ?? null,
289 + suggestions: null,
290 + }),
291 + );
292 }
293 const terminal: ThrowTerminal = {
294 kind: 'throw',
@@ -470,22 +470,26 @@ function lowerStatement(
470 } else if (binding.path.isFunctionDeclaration()) {
471 kind = InstructionKind.HoistedFunction;
472 } else if (!binding.path.isVariableDeclarator()) {
473 - builder.errors.push({
474 - category: ErrorCategory.Todo,
475 - reason: 'Unsupported declaration type for hoisting',
476 - description: `variable "${binding.identifier.name}" declared with ${binding.path.type}`,
477 - suggestions: null,
478 - loc: id.parentPath.node.loc ?? GeneratedSource,
479 - });
473 + builder.recordError(
474 + new CompilerErrorDetail({
475 + category: ErrorCategory.Todo,
476 + reason: 'Unsupported declaration type for hoisting',
477 + description: `variable "${binding.identifier.name}" declared with ${binding.path.type}`,
478 + suggestions: null,
479 + loc: id.parentPath.node.loc ?? GeneratedSource,
480 + }),
481 + );
482 continue;
483 } else {
482 - builder.errors.push({
483 - category: ErrorCategory.Todo,
484 - reason: 'Handle non-const declarations for hoisting',
485 - description: `variable "${binding.identifier.name}" declared with ${binding.kind}`,
486 - suggestions: null,
487 - loc: id.parentPath.node.loc ?? GeneratedSource,
488 - });
484 + builder.recordError(
485 + new CompilerErrorDetail({
486 + category: ErrorCategory.Todo,
487 + reason: 'Handle non-const declarations for hoisting',
488 + description: `variable "${binding.identifier.name}" declared with ${binding.kind}`,
489 + suggestions: null,
490 + loc: id.parentPath.node.loc ?? GeneratedSource,
491 + }),
492 + );
493 continue;
494 }
495
@@ -575,13 +579,15 @@ function lowerStatement(
579 };
580 }
581 if (!init.isVariableDeclaration()) {
578 - builder.errors.push({
579 - reason:
580 - '(BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement',
581 - category: ErrorCategory.Todo,
582 - loc: stmt.node.loc ?? null,
583 - suggestions: null,
584 - });
582 + builder.recordError(
583 + new CompilerErrorDetail({
584 + reason:
585 + '(BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement',
586 + category: ErrorCategory.Todo,
587 + loc: stmt.node.loc ?? null,
588 + suggestions: null,
589 + }),
590 + );
591 // Lower the init expression as best-effort and continue
592 if (init.isExpression()) {
593 lowerExpressionToTemporary(builder, init as NodePath<t.Expression>);
@@ -654,12 +660,14 @@ function lowerStatement(
660
661 const test = stmt.get('test');
662 if (test.node == null) {
657 - builder.errors.push({
658 - reason: `(BuildHIR::lowerStatement) Handle empty test in ForStatement`,
659 - category: ErrorCategory.Todo,
660 - loc: stmt.node.loc ?? null,
661 - suggestions: null,
662 - });
663 + builder.recordError(
664 + new CompilerErrorDetail({
665 + reason: `(BuildHIR::lowerStatement) Handle empty test in ForStatement`,
666 + category: ErrorCategory.Todo,
667 + loc: stmt.node.loc ?? null,
668 + suggestions: null,
669 + }),
670 + );
671 // Treat `for(;;)` as `while(true)` to keep the builder state consistent
672 builder.terminateWithContinuation(
673 {
@@ -822,12 +830,14 @@ function lowerStatement(
830 const testExpr = case_.get('test');
831 if (testExpr.node == null) {
832 if (hasDefault) {
825 - builder.errors.push({
826 - reason: `Expected at most one \`default\` branch in a switch statement, this code should have failed to parse`,
827 - category: ErrorCategory.Syntax,
828 - loc: case_.node.loc ?? null,
829 - suggestions: null,
830 - });
833 + builder.recordError(
834 + new CompilerErrorDetail({
835 + reason: `Expected at most one \`default\` branch in a switch statement, this code should have failed to parse`,
836 + category: ErrorCategory.Syntax,
837 + loc: case_.node.loc ?? null,
838 + suggestions: null,
839 + }),
840 + );
841 break;
842 }
843 hasDefault = true;
@@ -894,12 +904,14 @@ function lowerStatement(
904 const stmt = stmtPath as NodePath<t.VariableDeclaration>;
905 const nodeKind: t.VariableDeclaration['kind'] = stmt.node.kind;
906 if (nodeKind === 'var') {
897 - builder.errors.push({
898 - reason: `(BuildHIR::lowerStatement) Handle ${nodeKind} kinds in VariableDeclaration`,
899 - category: ErrorCategory.Todo,
900 - loc: stmt.node.loc ?? null,
901 - suggestions: null,
902 - });
907 + builder.recordError(
908 + new CompilerErrorDetail({
909 + reason: `(BuildHIR::lowerStatement) Handle ${nodeKind} kinds in VariableDeclaration`,
910 + category: ErrorCategory.Todo,
911 + loc: stmt.node.loc ?? null,
912 + suggestions: null,
913 + }),
914 + );
915 // Treat `var` as `let` so references to the variable don't break
916 }
917 const kind =
@@ -924,12 +936,14 @@ function lowerStatement(
936 } else if (id.isIdentifier()) {
937 const binding = builder.resolveIdentifier(id);
938 if (binding.kind !== 'Identifier') {
927 - builder.errors.push({
928 - reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
929 - category: ErrorCategory.Invariant,
930 - loc: id.node.loc ?? null,
931 - suggestions: null,
932 - });
939 + builder.recordError(
940 + new CompilerErrorDetail({
941 + reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
942 + category: ErrorCategory.Invariant,
943 + loc: id.node.loc ?? null,
944 + suggestions: null,
945 + }),
946 + );
947 } else {
948 const place: Place = {
949 effect: Effect.Unknown,
@@ -941,19 +955,21 @@ function lowerStatement(
955 if (builder.isContextIdentifier(id)) {
956 if (kind === InstructionKind.Const) {
957 const declRangeStart = declaration.parentPath.node.start!;
944 - builder.errors.push({
945 - reason: `Expect \`const\` declaration not to be reassigned`,
946 - category: ErrorCategory.Syntax,
947 - loc: id.node.loc ?? null,
948 - suggestions: [
949 - {
950 - description: 'Change to a `let` declaration',
951 - op: CompilerSuggestionOperation.Replace,
952 - range: [declRangeStart, declRangeStart + 5], // "const".length
953 - text: 'let',
954 - },
955 - ],
956 - });
958 + builder.recordError(
959 + new CompilerErrorDetail({
960 + reason: `Expect \`const\` declaration not to be reassigned`,
961 + category: ErrorCategory.Syntax,
962 + loc: id.node.loc ?? null,
963 + suggestions: [
964 + {
965 + description: 'Change to a `let` declaration',
966 + op: CompilerSuggestionOperation.Replace,
967 + range: [declRangeStart, declRangeStart + 5], // "const".length
968 + text: 'let',
969 + },
970 + ],
971 + }),
972 + );
973 }
974 lowerValueToTemporary(builder, {
975 kind: 'DeclareContext',
@@ -987,13 +1003,15 @@ function lowerStatement(
1003 }
1004 }
1005 } else {
990 - builder.errors.push({
991 - reason: `Expected variable declaration to be an identifier if no initializer was provided`,
992 - description: `Got a \`${id.type}\``,
993 - category: ErrorCategory.Syntax,
994 - loc: stmt.node.loc ?? null,
995 - suggestions: null,
996 - });
1006 + builder.recordError(
1007 + new CompilerErrorDetail({
1008 + reason: `Expected variable declaration to be an identifier if no initializer was provided`,
1009 + description: `Got a \`${id.type}\``,
1010 + category: ErrorCategory.Syntax,
1011 + loc: stmt.node.loc ?? null,
1012 + suggestions: null,
1013 + }),
1014 + );
1015 }
1016 }
1017 return;
@@ -1094,12 +1112,14 @@ function lowerStatement(
1112 const testBlock = builder.reserve('loop');
1113
1114 if (stmt.node.await) {
1097 - builder.errors.push({
1098 - reason: `(BuildHIR::lowerStatement) Handle for-await loops`,
1099 - category: ErrorCategory.Todo,
1100 - loc: stmt.node.loc ?? null,
1101 - suggestions: null,
1102 - });
1115 + builder.recordError(
1116 + new CompilerErrorDetail({
1117 + reason: `(BuildHIR::lowerStatement) Handle for-await loops`,
1118 + category: ErrorCategory.Todo,
1119 + loc: stmt.node.loc ?? null,
1120 + suggestions: null,
1121 + }),
1122 + );
1123 return;
1124 }
1125
@@ -1322,21 +1342,25 @@ function lowerStatement(
1342
1343 const handlerPath = stmt.get('handler');
1344 if (!hasNode(handlerPath)) {
1325 - builder.errors.push({
1326 - reason: `(BuildHIR::lowerStatement) Handle TryStatement without a catch clause`,
1327 - category: ErrorCategory.Todo,
1328 - loc: stmt.node.loc ?? null,
1329 - suggestions: null,
1330 - });
1345 + builder.recordError(
1346 + new CompilerErrorDetail({
1347 + reason: `(BuildHIR::lowerStatement) Handle TryStatement without a catch clause`,
1348 + category: ErrorCategory.Todo,
1349 + loc: stmt.node.loc ?? null,
1350 + suggestions: null,
1351 + }),
1352 + );
1353 return;
1354 }
1355 if (hasNode(stmt.get('finalizer'))) {
1334 - builder.errors.push({
1335 - reason: `(BuildHIR::lowerStatement) Handle TryStatement with a finalizer ('finally') clause`,
1336 - category: ErrorCategory.Todo,
1337 - loc: stmt.node.loc ?? null,
1338 - suggestions: null,
1339 - });
1356 + builder.recordError(
1357 + new CompilerErrorDetail({
1358 + reason: `(BuildHIR::lowerStatement) Handle TryStatement with a finalizer ('finally') clause`,
1359 + category: ErrorCategory.Todo,
1360 + loc: stmt.node.loc ?? null,
1361 + suggestions: null,
1362 + }),
1363 + );
1364 }
1365
1366 const handlerBindingPath = handlerPath.get('param');
@@ -1423,13 +1447,15 @@ function lowerStatement(
1447 return;
1448 }
1449 case 'WithStatement': {
1426 - builder.errors.push({
1427 - reason: `JavaScript 'with' syntax is not supported`,
1428 - description: `'with' syntax is considered deprecated and removed from JavaScript standards, consider alternatives`,
1429 - category: ErrorCategory.UnsupportedSyntax,
1430 - loc: stmtPath.node.loc ?? null,
1431 - suggestions: null,
1432 - });
1450 + builder.recordError(
1451 + new CompilerErrorDetail({
1452 + reason: `JavaScript 'with' syntax is not supported`,
1453 + description: `'with' syntax is considered deprecated and removed from JavaScript standards, consider alternatives`,
1454 + category: ErrorCategory.UnsupportedSyntax,
1455 + loc: stmtPath.node.loc ?? null,
1456 + suggestions: null,
1457 + }),
1458 + );
1459 lowerValueToTemporary(builder, {
1460 kind: 'UnsupportedNode',
1461 loc: stmtPath.node.loc ?? GeneratedSource,
@@ -1443,13 +1469,15 @@ function lowerStatement(
1469 * and complex enough to support that we don't anticipate supporting anytime soon. Developers
1470 * are encouraged to lift classes out of component/hook declarations.
1471 */
1446 - builder.errors.push({
1447 - reason: 'Inline `class` declarations are not supported',
1448 - description: `Move class declarations outside of components/hooks`,
1449 - category: ErrorCategory.UnsupportedSyntax,
1450 - loc: stmtPath.node.loc ?? null,
1451 - suggestions: null,
1452 - });
1472 + builder.recordError(
1473 + new CompilerErrorDetail({
1474 + reason: 'Inline `class` declarations are not supported',
1475 + description: `Move class declarations outside of components/hooks`,
1476 + category: ErrorCategory.UnsupportedSyntax,
1477 + loc: stmtPath.node.loc ?? null,
1478 + suggestions: null,
1479 + }),
1480 + );
1481 lowerValueToTemporary(builder, {
1482 kind: 'UnsupportedNode',
1483 loc: stmtPath.node.loc ?? GeneratedSource,
@@ -1472,13 +1500,15 @@ function lowerStatement(
1500 case 'ImportDeclaration':
1501 case 'TSExportAssignment':
1502 case 'TSImportEqualsDeclaration': {
1475 - builder.errors.push({
1476 - reason:
1477 - 'JavaScript `import` and `export` statements may only appear at the top level of a module',
1478 - category: ErrorCategory.Syntax,
1479 - loc: stmtPath.node.loc ?? null,
1480 - suggestions: null,
1481 - });
1503 + builder.recordError(
1504 + new CompilerErrorDetail({
1505 + reason:
1506 + 'JavaScript `import` and `export` statements may only appear at the top level of a module',
1507 + category: ErrorCategory.Syntax,
1508 + loc: stmtPath.node.loc ?? null,
1509 + suggestions: null,
1510 + }),
1511 + );
1512 lowerValueToTemporary(builder, {
1513 kind: 'UnsupportedNode',
1514 loc: stmtPath.node.loc ?? GeneratedSource,
@@ -1487,13 +1517,15 @@ function lowerStatement(
1517 return;
1518 }
1519 case 'TSNamespaceExportDeclaration': {
1490 - builder.errors.push({
1491 - reason:
1492 - 'TypeScript `namespace` statements may only appear at the top level of a module',
1493 - category: ErrorCategory.Syntax,
1494 - loc: stmtPath.node.loc ?? null,
1495 - suggestions: null,
1496 - });
1520 + builder.recordError(
1521 + new CompilerErrorDetail({
1522 + reason:
1523 + 'TypeScript `namespace` statements may only appear at the top level of a module',
1524 + category: ErrorCategory.Syntax,
1525 + loc: stmtPath.node.loc ?? null,
1526 + suggestions: null,
1527 + }),
1528 + );
1529 lowerValueToTemporary(builder, {
1530 kind: 'UnsupportedNode',
1531 loc: stmtPath.node.loc ?? GeneratedSource,
@@ -1574,12 +1606,14 @@ function lowerObjectPropertyKey(
1606 };
1607 }
1608
1577 - builder.errors.push({
1578 - reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
1579 - category: ErrorCategory.Todo,
1580 - loc: key.node.loc ?? null,
1581 - suggestions: null,
1582 - });
1609 + builder.recordError(
1610 + new CompilerErrorDetail({
1611 + reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`,
1612 + category: ErrorCategory.Todo,
1613 + loc: key.node.loc ?? null,
1614 + suggestions: null,
1615 + }),
1616 + );
1617 return null;
1618 }
1619
@@ -1631,12 +1665,14 @@ function lowerExpression(
1665 }
1666 const valuePath = propertyPath.get('value');
1667 if (!valuePath.isExpression()) {
1634 - builder.errors.push({
1635 - reason: `(BuildHIR::lowerExpression) Handle ${valuePath.type} values in ObjectExpression`,
1636 - category: ErrorCategory.Todo,
1637 - loc: valuePath.node.loc ?? null,
1638 - suggestions: null,
1639 - });
1668 + builder.recordError(
1669 + new CompilerErrorDetail({
1670 + reason: `(BuildHIR::lowerExpression) Handle ${valuePath.type} values in ObjectExpression`,
1671 + category: ErrorCategory.Todo,
1672 + loc: valuePath.node.loc ?? null,
1673 + suggestions: null,
1674 + }),
1675 + );
1676 continue;
1677 }
1678 const value = lowerExpressionToTemporary(builder, valuePath);
@@ -1657,12 +1693,14 @@ function lowerExpression(
1693 });
1694 } else if (propertyPath.isObjectMethod()) {
1695 if (propertyPath.node.kind !== 'method') {
1660 - builder.errors.push({
1661 - reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.node.kind} functions in ObjectExpression`,
1662 - category: ErrorCategory.Todo,
1663 - loc: propertyPath.node.loc ?? null,
1664 - suggestions: null,
1665 - });
1696 + builder.recordError(
1697 + new CompilerErrorDetail({
1698 + reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.node.kind} functions in ObjectExpression`,
1699 + category: ErrorCategory.Todo,
1700 + loc: propertyPath.node.loc ?? null,
1701 + suggestions: null,
1702 + }),
1703 + );
1704 continue;
1705 }
1706 const method = lowerObjectMethod(builder, propertyPath);
@@ -1678,12 +1716,14 @@ function lowerExpression(
1716 key: loweredKey,
1717 });
1718 } else {
1681 - builder.errors.push({
1682 - reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.type} properties in ObjectExpression`,
1683 - category: ErrorCategory.Todo,
1684 - loc: propertyPath.node.loc ?? null,
1685 - suggestions: null,
1686 - });
1719 + builder.recordError(
1720 + new CompilerErrorDetail({
1721 + reason: `(BuildHIR::lowerExpression) Handle ${propertyPath.type} properties in ObjectExpression`,
1722 + category: ErrorCategory.Todo,
1723 + loc: propertyPath.node.loc ?? null,
1724 + suggestions: null,
1725 + }),
1726 + );
1727 continue;
1728 }
1729 }
@@ -1711,12 +1751,14 @@ function lowerExpression(
1751 );
1752 elements.push({kind: 'Spread', place});
1753 } else {
1714 - builder.errors.push({
1715 - reason: `(BuildHIR::lowerExpression) Handle ${element.type} elements in ArrayExpression`,
1716 - category: ErrorCategory.Todo,
1717 - loc: element.node.loc ?? null,
1718 - suggestions: null,
1719 - });
1754 + builder.recordError(
1755 + new CompilerErrorDetail({
1756 + reason: `(BuildHIR::lowerExpression) Handle ${element.type} elements in ArrayExpression`,
1757 + category: ErrorCategory.Todo,
1758 + loc: element.node.loc ?? null,
1759 + suggestions: null,
1760 + }),
1761 + );
1762 continue;
1763 }
1764 }
@@ -1730,13 +1772,15 @@ function lowerExpression(
1772 const expr = exprPath as NodePath<t.NewExpression>;
1773 const calleePath = expr.get('callee');
1774 if (!calleePath.isExpression()) {
1733 - builder.errors.push({
1734 - reason: `Expected an expression as the \`new\` expression receiver (v8 intrinsics are not supported)`,
1735 - description: `Got a \`${calleePath.node.type}\``,
1736 - category: ErrorCategory.Syntax,
1737 - loc: calleePath.node.loc ?? null,
1738 - suggestions: null,
1739 - });
1775 + builder.recordError(
1776 + new CompilerErrorDetail({
1777 + reason: `Expected an expression as the \`new\` expression receiver (v8 intrinsics are not supported)`,
1778 + description: `Got a \`${calleePath.node.type}\``,
1779 + category: ErrorCategory.Syntax,
1780 + loc: calleePath.node.loc ?? null,
1781 + suggestions: null,
1782 + }),
1783 + );
1784 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
1785 }
1786 const callee = lowerExpressionToTemporary(builder, calleePath);
@@ -1757,12 +1801,14 @@ function lowerExpression(
1801 const expr = exprPath as NodePath<t.CallExpression>;
1802 const calleePath = expr.get('callee');
1803 if (!calleePath.isExpression()) {
1760 - builder.errors.push({
1761 - reason: `Expected Expression, got ${calleePath.type} in CallExpression (v8 intrinsics not supported). This error is likely caused by a bug in React Compiler. Please file an issue`,
1762 - category: ErrorCategory.Todo,
1763 - loc: calleePath.node.loc ?? null,
1764 - suggestions: null,
1765 - });
1804 + builder.recordError(
1805 + new CompilerErrorDetail({
1806 + reason: `Expected Expression, got ${calleePath.type} in CallExpression (v8 intrinsics not supported). This error is likely caused by a bug in React Compiler. Please file an issue`,
1807 + category: ErrorCategory.Todo,
1808 + loc: calleePath.node.loc ?? null,
1809 + suggestions: null,
1810 + }),
1811 + );
1812 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
1813 }
1814 if (calleePath.isMemberExpression()) {
@@ -1791,24 +1837,28 @@ function lowerExpression(
1837 const expr = exprPath as NodePath<t.BinaryExpression>;
1838 const leftPath = expr.get('left');
1839 if (!leftPath.isExpression()) {
1794 - builder.errors.push({
1795 - reason: `(BuildHIR::lowerExpression) Expected Expression, got ${leftPath.type} lval in BinaryExpression`,
1796 - category: ErrorCategory.Todo,
1797 - loc: leftPath.node.loc ?? null,
1798 - suggestions: null,
1799 - });
1840 + builder.recordError(
1841 + new CompilerErrorDetail({
1842 + reason: `(BuildHIR::lowerExpression) Expected Expression, got ${leftPath.type} lval in BinaryExpression`,
1843 + category: ErrorCategory.Todo,
1844 + loc: leftPath.node.loc ?? null,
1845 + suggestions: null,
1846 + }),
1847 + );
1848 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
1849 }
1850 const left = lowerExpressionToTemporary(builder, leftPath);
1851 const right = lowerExpressionToTemporary(builder, expr.get('right'));
1852 const operator = expr.node.operator;
1853 if (operator === '|>') {
1806 - builder.errors.push({
1807 - reason: `(BuildHIR::lowerExpression) Pipe operator not supported`,
1808 - category: ErrorCategory.Todo,
1809 - loc: leftPath.node.loc ?? null,
1810 - suggestions: null,
1811 - });
1854 + builder.recordError(
1855 + new CompilerErrorDetail({
1856 + reason: `(BuildHIR::lowerExpression) Pipe operator not supported`,
1857 + category: ErrorCategory.Todo,
1858 + loc: leftPath.node.loc ?? null,
1859 + suggestions: null,
1860 + }),
1861 + );
1862 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
1863 }
1864 return {
@@ -1832,12 +1882,14 @@ function lowerExpression(
1882 last = lowerExpressionToTemporary(builder, item);
1883 }
1884 if (last === null) {
1835 - builder.errors.push({
1836 - reason: `Expected sequence expression to have at least one expression`,
1837 - category: ErrorCategory.Syntax,
1838 - loc: expr.node.loc ?? null,
1839 - suggestions: null,
1840 - });
1885 + builder.recordError(
1886 + new CompilerErrorDetail({
1887 + reason: `Expected sequence expression to have at least one expression`,
1888 + category: ErrorCategory.Syntax,
1889 + loc: expr.node.loc ?? null,
1890 + suggestions: null,
1891 + }),
1892 + );
1893 } else {
1894 lowerValueToTemporary(builder, {
1895 kind: 'StoreLocal',
@@ -2043,13 +2095,15 @@ function lowerExpression(
2095 * OptionalMemberExpressions as the left side of an AssignmentExpression are Stage 1 and
2096 * not supported by React Compiler yet.
2097 */
2046 - builder.errors.push({
2047 - reason: `(BuildHIR::lowerExpression) Unsupported syntax on the left side of an AssignmentExpression`,
2048 - description: `Expected an LVal, got: ${left.type}`,
2049 - category: ErrorCategory.Todo,
2050 - loc: left.node.loc ?? null,
2051 - suggestions: null,
2052 - });
2098 + builder.recordError(
2099 + new CompilerErrorDetail({
2100 + reason: `(BuildHIR::lowerExpression) Unsupported syntax on the left side of an AssignmentExpression`,
2101 + description: `Expected an LVal, got: ${left.type}`,
2102 + category: ErrorCategory.Todo,
2103 + loc: left.node.loc ?? null,
2104 + suggestions: null,
2105 + }),
2106 + );
2107 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2108 }
2109 }
@@ -2072,12 +2126,14 @@ function lowerExpression(
2126 };
2127 const binaryOperator = operators[operator];
2128 if (binaryOperator == null) {
2075 - builder.errors.push({
2076 - reason: `(BuildHIR::lowerExpression) Handle ${operator} operators in AssignmentExpression`,
2077 - category: ErrorCategory.Todo,
2078 - loc: expr.node.loc ?? null,
2079 - suggestions: null,
2080 - });
2129 + builder.recordError(
2130 + new CompilerErrorDetail({
2131 + reason: `(BuildHIR::lowerExpression) Handle ${operator} operators in AssignmentExpression`,
2132 + category: ErrorCategory.Todo,
2133 + loc: expr.node.loc ?? null,
2134 + suggestions: null,
2135 + }),
2136 + );
2137 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2138 }
2139 const left = expr.get('left');
@@ -2171,12 +2227,14 @@ function lowerExpression(
2227 }
2228 }
2229 default: {
2174 - builder.errors.push({
2175 - reason: `(BuildHIR::lowerExpression) Expected Identifier or MemberExpression, got ${expr.type} lval in AssignmentExpression`,
2176 - category: ErrorCategory.Todo,
2177 - loc: expr.node.loc ?? null,
2178 - suggestions: null,
2179 - });
2230 + builder.recordError(
2231 + new CompilerErrorDetail({
2232 + reason: `(BuildHIR::lowerExpression) Expected Identifier or MemberExpression, got ${expr.type} lval in AssignmentExpression`,
2233 + category: ErrorCategory.Todo,
2234 + loc: expr.node.loc ?? null,
2235 + suggestions: null,
2236 + }),
2237 + );
2238 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2239 }
2240 }
@@ -2210,12 +2268,14 @@ function lowerExpression(
2268 continue;
2269 }
2270 if (!attribute.isJSXAttribute()) {
2213 - builder.errors.push({
2214 - reason: `(BuildHIR::lowerExpression) Handle ${attribute.type} attributes in JSXElement`,
2215 - category: ErrorCategory.Todo,
2216 - loc: attribute.node.loc ?? null,
2217 - suggestions: null,
2218 - });
2271 + builder.recordError(
2272 + new CompilerErrorDetail({
2273 + reason: `(BuildHIR::lowerExpression) Handle ${attribute.type} attributes in JSXElement`,
2274 + category: ErrorCategory.Todo,
2275 + loc: attribute.node.loc ?? null,
2276 + suggestions: null,
2277 + }),
2278 + );
2279 continue;
2280 }
2281 const namePath = attribute.get('name');
@@ -2223,12 +2283,14 @@ function lowerExpression(
2283 if (namePath.isJSXIdentifier()) {
2284 propName = namePath.node.name;
2285 if (propName.indexOf(':') !== -1) {
2226 - builder.errors.push({
2227 - reason: `(BuildHIR::lowerExpression) Unexpected colon in attribute name \`${propName}\``,
2228 - category: ErrorCategory.Todo,
2229 - loc: namePath.node.loc ?? null,
2230 - suggestions: null,
2231 - });
2286 + builder.recordError(
2287 + new CompilerErrorDetail({
2288 + reason: `(BuildHIR::lowerExpression) Unexpected colon in attribute name \`${propName}\``,
2289 + category: ErrorCategory.Todo,
2290 + loc: namePath.node.loc ?? null,
2291 + suggestions: null,
2292 + }),
2293 + );
2294 }
2295 } else {
2296 CompilerError.invariant(namePath.isJSXNamespacedName(), {
@@ -2251,22 +2313,26 @@ function lowerExpression(
2313 });
2314 } else {
2315 if (!valueExpr.isJSXExpressionContainer()) {
2254 - builder.errors.push({
2255 - reason: `(BuildHIR::lowerExpression) Handle ${valueExpr.type} attribute values in JSXElement`,
2256 - category: ErrorCategory.Todo,
2257 - loc: valueExpr.node?.loc ?? null,
2258 - suggestions: null,
2259 - });
2316 + builder.recordError(
2317 + new CompilerErrorDetail({
2318 + reason: `(BuildHIR::lowerExpression) Handle ${valueExpr.type} attribute values in JSXElement`,
2319 + category: ErrorCategory.Todo,
2320 + loc: valueExpr.node?.loc ?? null,
2321 + suggestions: null,
2322 + }),
2323 + );
2324 continue;
2325 }
2326 const expression = valueExpr.get('expression');
2327 if (!expression.isExpression()) {
2264 - builder.errors.push({
2265 - reason: `(BuildHIR::lowerExpression) Handle ${expression.type} expressions in JSXExpressionContainer within JSXElement`,
2266 - category: ErrorCategory.Todo,
2267 - loc: valueExpr.node.loc ?? null,
2268 - suggestions: null,
2269 - });
2328 + builder.recordError(
2329 + new CompilerErrorDetail({
2330 + reason: `(BuildHIR::lowerExpression) Handle ${expression.type} expressions in JSXExpressionContainer within JSXElement`,
2331 + category: ErrorCategory.Todo,
2332 + loc: valueExpr.node.loc ?? null,
2333 + suggestions: null,
2334 + }),
2335 + );
2336 continue;
2337 }
2338 value = lowerExpressionToTemporary(builder, expression);
@@ -2317,7 +2383,7 @@ function lowerExpression(
2383 });
2384 for (const [name, locations] of Object.entries(fbtLocations)) {
2385 if (locations.length > 1) {
2320 - builder.errors.pushDiagnostic(
2386 + builder.recordError(
2387 new CompilerDiagnostic({
2388 category: ErrorCategory.Todo,
2389 reason: 'Support duplicate fbt tags',
@@ -2378,13 +2444,15 @@ function lowerExpression(
2444 case 'TaggedTemplateExpression': {
2445 const expr = exprPath as NodePath<t.TaggedTemplateExpression>;
2446 if (expr.get('quasi').get('expressions').length !== 0) {
2381 - builder.errors.push({
2382 - reason:
2383 - '(BuildHIR::lowerExpression) Handle tagged template with interpolations',
2384 - category: ErrorCategory.Todo,
2385 - loc: exprPath.node.loc ?? null,
2386 - suggestions: null,
2387 - });
2447 + builder.recordError(
2448 + new CompilerErrorDetail({
2449 + reason:
2450 + '(BuildHIR::lowerExpression) Handle tagged template with interpolations',
2451 + category: ErrorCategory.Todo,
2452 + loc: exprPath.node.loc ?? null,
2453 + suggestions: null,
2454 + }),
2455 + );
2456 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2457 }
2458 CompilerError.invariant(expr.get('quasi').get('quasis').length == 1, {
@@ -2394,13 +2462,15 @@ function lowerExpression(
2462 });
2463 const value = expr.get('quasi').get('quasis').at(0)!.node.value;
2464 if (value.raw !== value.cooked) {
2397 - builder.errors.push({
2398 - reason:
2399 - '(BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value',
2400 - category: ErrorCategory.Todo,
2401 - loc: exprPath.node.loc ?? null,
2402 - suggestions: null,
2403 - });
2465 + builder.recordError(
2466 + new CompilerErrorDetail({
2467 + reason:
2468 + '(BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value',
2469 + category: ErrorCategory.Todo,
2470 + loc: exprPath.node.loc ?? null,
2471 + suggestions: null,
2472 + }),
2473 + );
2474 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2475 }
2476
@@ -2417,22 +2487,26 @@ function lowerExpression(
2487 const quasis = expr.get('quasis');
2488
2489 if (subexprs.length !== quasis.length - 1) {
2420 - builder.errors.push({
2421 - reason: `Unexpected quasi and subexpression lengths in template literal`,
2422 - category: ErrorCategory.Syntax,
2423 - loc: exprPath.node.loc ?? null,
2424 - suggestions: null,
2425 - });
2490 + builder.recordError(
2491 + new CompilerErrorDetail({
2492 + reason: `Unexpected quasi and subexpression lengths in template literal`,
2493 + category: ErrorCategory.Syntax,
2494 + loc: exprPath.node.loc ?? null,
2495 + suggestions: null,
2496 + }),
2497 + );
2498 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2499 }
2500
2501 if (subexprs.some(e => !e.isExpression())) {
2430 - builder.errors.push({
2431 - reason: `(BuildHIR::lowerAssignment) Handle TSType in TemplateLiteral.`,
2432 - category: ErrorCategory.Todo,
2433 - loc: exprPath.node.loc ?? null,
2434 - suggestions: null,
2435 - });
2502 + builder.recordError(
2503 + new CompilerErrorDetail({
2504 + reason: `(BuildHIR::lowerAssignment) Handle TSType in TemplateLiteral.`,
2505 + category: ErrorCategory.Todo,
2506 + loc: exprPath.node.loc ?? null,
2507 + suggestions: null,
2508 + }),
2509 + );
2510 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2511 }
2512
@@ -2469,8 +2543,26 @@ function lowerExpression(
2543 };
2544 }
2545 } else {
2472 - builder.errors.push({
2473 - reason: `Only object properties can be deleted`,
2546 + builder.recordError(
2547 + new CompilerErrorDetail({
2548 + reason: `Only object properties can be deleted`,
2549 + category: ErrorCategory.Syntax,
2550 + loc: expr.node.loc ?? null,
2551 + suggestions: [
2552 + {
2553 + description: 'Remove this line',
2554 + range: [expr.node.start!, expr.node.end!],
2555 + op: CompilerSuggestionOperation.Remove,
2556 + },
2557 + ],
2558 + }),
2559 + );
2560 + return {kind: 'UnsupportedNode', node: expr.node, loc: exprLoc};
2561 + }
2562 + } else if (expr.node.operator === 'throw') {
2563 + builder.recordError(
2564 + new CompilerErrorDetail({
2565 + reason: `Throw expressions are not supported`,
2566 category: ErrorCategory.Syntax,
2567 loc: expr.node.loc ?? null,
2568 suggestions: [
@@ -2480,22 +2572,8 @@ function lowerExpression(
2572 op: CompilerSuggestionOperation.Remove,
2573 },
2574 ],
2483 - });
2484 - return {kind: 'UnsupportedNode', node: expr.node, loc: exprLoc};
2485 - }
2486 - } else if (expr.node.operator === 'throw') {
2487 - builder.errors.push({
2488 - reason: `Throw expressions are not supported`,
2489 - category: ErrorCategory.Syntax,
2490 - loc: expr.node.loc ?? null,
2491 - suggestions: [
2492 - {
2493 - description: 'Remove this line',
2494 - range: [expr.node.start!, expr.node.end!],
2495 - op: CompilerSuggestionOperation.Remove,
2496 - },
2497 - ],
2498 - });
2575 + }),
2576 + );
2577 return {kind: 'UnsupportedNode', node: expr.node, loc: exprLoc};
2578 } else {
2579 return {
@@ -2605,20 +2683,24 @@ function lowerExpression(
2683 };
2684 }
2685 if (!argument.isIdentifier()) {
2608 - builder.errors.push({
2609 - reason: `(BuildHIR::lowerExpression) Handle UpdateExpression with ${argument.type} argument`,
2610 - category: ErrorCategory.Todo,
2611 - loc: exprPath.node.loc ?? null,
2612 - suggestions: null,
2613 - });
2686 + builder.recordError(
2687 + new CompilerErrorDetail({
2688 + reason: `(BuildHIR::lowerExpression) Handle UpdateExpression with ${argument.type} argument`,
2689 + category: ErrorCategory.Todo,
2690 + loc: exprPath.node.loc ?? null,
2691 + suggestions: null,
2692 + }),
2693 + );
2694 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2695 } else if (builder.isContextIdentifier(argument)) {
2616 - builder.errors.push({
2617 - reason: `(BuildHIR::lowerExpression) Handle UpdateExpression to variables captured within lambdas.`,
2618 - category: ErrorCategory.Todo,
2619 - loc: exprPath.node.loc ?? null,
2620 - suggestions: null,
2621 - });
2696 + builder.recordError(
2697 + new CompilerErrorDetail({
2698 + reason: `(BuildHIR::lowerExpression) Handle UpdateExpression to variables captured within lambdas.`,
2699 + category: ErrorCategory.Todo,
2700 + loc: exprPath.node.loc ?? null,
2701 + suggestions: null,
2702 + }),
2703 + );
2704 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2705 }
2706 const lvalue = lowerIdentifierForAssignment(
@@ -2632,22 +2714,26 @@ function lowerExpression(
2714 * lowerIdentifierForAssignment should have already reported an error if it returned null,
2715 * we check here just in case
2716 */
2635 - if (!builder.errors.hasAnyErrors()) {
2636 - builder.errors.push({
2637 - reason: `(BuildHIR::lowerExpression) Found an invalid UpdateExpression without a previously reported error`,
2638 - category: ErrorCategory.Invariant,
2639 - loc: exprLoc,
2640 - suggestions: null,
2641 - });
2717 + if (!builder.environment.hasErrors()) {
2718 + builder.recordError(
2719 + new CompilerErrorDetail({
2720 + reason: `(BuildHIR::lowerExpression) Found an invalid UpdateExpression without a previously reported error`,
2721 + category: ErrorCategory.Invariant,
2722 + loc: exprLoc,
2723 + suggestions: null,
2724 + }),
2725 + );
2726 }
2727 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2728 } else if (lvalue.kind === 'Global') {
2645 - builder.errors.push({
2646 - reason: `(BuildHIR::lowerExpression) Support UpdateExpression where argument is a global`,
2647 - category: ErrorCategory.Todo,
2648 - loc: exprLoc,
2649 - suggestions: null,
2650 - });
2729 + builder.recordError(
2730 + new CompilerErrorDetail({
2731 + reason: `(BuildHIR::lowerExpression) Support UpdateExpression where argument is a global`,
2732 + category: ErrorCategory.Todo,
2733 + loc: exprLoc,
2734 + suggestions: null,
2735 + }),
2736 + );
2737 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2738 }
2739 const value = lowerIdentifier(builder, argument);
@@ -2697,21 +2783,25 @@ function lowerExpression(
2783 };
2784 }
2785
2700 - builder.errors.push({
2701 - reason: `(BuildHIR::lowerExpression) Handle MetaProperty expressions other than import.meta`,
2702 - category: ErrorCategory.Todo,
2703 - loc: exprPath.node.loc ?? null,
2704 - suggestions: null,
2705 - });
2786 + builder.recordError(
2787 + new CompilerErrorDetail({
2788 + reason: `(BuildHIR::lowerExpression) Handle MetaProperty expressions other than import.meta`,
2789 + category: ErrorCategory.Todo,
2790 + loc: exprPath.node.loc ?? null,
2791 + suggestions: null,
2792 + }),
2793 + );
2794 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2795 }
2796 default: {
2709 - builder.errors.push({
2710 - reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`,
2711 - category: ErrorCategory.Todo,
2712 - loc: exprPath.node.loc ?? null,
2713 - suggestions: null,
2714 - });
2797 + builder.recordError(
2798 + new CompilerErrorDetail({
2799 + reason: `(BuildHIR::lowerExpression) Handle ${exprPath.type} expressions`,
2800 + category: ErrorCategory.Todo,
2801 + loc: exprPath.node.loc ?? null,
2802 + suggestions: null,
2803 + }),
2804 + );
2805 return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
2806 }
2807 }
@@ -3001,12 +3091,14 @@ function lowerReorderableExpression(
3091 expr: NodePath<t.Expression>,
3092 ): Place {
3093 if (!isReorderableExpression(builder, expr, true)) {
3004 - builder.errors.push({
3005 - reason: `(BuildHIR::node.lowerReorderableExpression) Expression type \`${expr.type}\` cannot be safely reordered`,
3006 - category: ErrorCategory.Todo,
3007 - loc: expr.node.loc ?? null,
3008 - suggestions: null,
3009 - });
3094 + builder.recordError(
3095 + new CompilerErrorDetail({
3096 + reason: `(BuildHIR::node.lowerReorderableExpression) Expression type \`${expr.type}\` cannot be safely reordered`,
3097 + category: ErrorCategory.Todo,
3098 + loc: expr.node.loc ?? null,
3099 + suggestions: null,
3100 + }),
3101 + );
3102 }
3103 return lowerExpressionToTemporary(builder, expr);
3104 }
@@ -3203,12 +3295,14 @@ function lowerArguments(
3295 } else if (argPath.isExpression()) {
3296 args.push(lowerExpressionToTemporary(builder, argPath));
3297 } else {
3206 - builder.errors.push({
3207 - reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in CallExpression`,
3208 - category: ErrorCategory.Todo,
3209 - loc: argPath.node.loc ?? null,
3210 - suggestions: null,
3211 - });
3298 + builder.recordError(
3299 + new CompilerErrorDetail({
3300 + reason: `(BuildHIR::lowerExpression) Handle ${argPath.type} arguments in CallExpression`,
3301 + category: ErrorCategory.Todo,
3302 + loc: argPath.node.loc ?? null,
3303 + suggestions: null,
3304 + }),
3305 + );
3306 }
3307 }
3308 return args;
@@ -3238,12 +3332,14 @@ function lowerMemberExpression(
3332 } else if (propertyNode.isNumericLiteral()) {
3333 property = makePropertyLiteral(propertyNode.node.value);
3334 } else {
3241 - builder.errors.push({
3242 - reason: `(BuildHIR::lowerMemberExpression) Handle ${propertyNode.type} property`,
3243 - category: ErrorCategory.Todo,
3244 - loc: propertyNode.node.loc ?? null,
3245 - suggestions: null,
3246 - });
3335 + builder.recordError(
3336 + new CompilerErrorDetail({
3337 + reason: `(BuildHIR::lowerMemberExpression) Handle ${propertyNode.type} property`,
3338 + category: ErrorCategory.Todo,
3339 + loc: propertyNode.node.loc ?? null,
3340 + suggestions: null,
3341 + }),
3342 + );
3343 return {
3344 object,
3345 property: propertyNode.toString(),
@@ -3259,12 +3355,14 @@ function lowerMemberExpression(
3355 return {object, property, value};
3356 } else {
3357 if (!propertyNode.isExpression()) {
3262 - builder.errors.push({
3263 - reason: `(BuildHIR::lowerMemberExpression) Expected Expression, got ${propertyNode.type} property`,
3264 - category: ErrorCategory.Todo,
3265 - loc: propertyNode.node.loc ?? null,
3266 - suggestions: null,
3267 - });
3358 + builder.recordError(
3359 + new CompilerErrorDetail({
3360 + reason: `(BuildHIR::lowerMemberExpression) Expected Expression, got ${propertyNode.type} property`,
3361 + category: ErrorCategory.Todo,
3362 + loc: propertyNode.node.loc ?? null,
3363 + suggestions: null,
3364 + }),
3365 + );
3366 return {
3367 object,
3368 property: propertyNode.toString(),
@@ -3317,13 +3415,15 @@ function lowerJsxElementName(
3415 const name = exprPath.node.name.name;
3416 const tag = `${namespace}:${name}`;
3417 if (namespace.indexOf(':') !== -1 || name.indexOf(':') !== -1) {
3320 - builder.errors.push({
3321 - reason: `Expected JSXNamespacedName to have no colons in the namespace or name`,
3322 - description: `Got \`${namespace}\` : \`${name}\``,
3323 - category: ErrorCategory.Syntax,
3324 - loc: exprPath.node.loc ?? null,
3325 - suggestions: null,
3326 - });
3418 + builder.recordError(
3419 + new CompilerErrorDetail({
3420 + reason: `Expected JSXNamespacedName to have no colons in the namespace or name`,
3421 + description: `Got \`${namespace}\` : \`${name}\``,
3422 + category: ErrorCategory.Syntax,
3423 + loc: exprPath.node.loc ?? null,
3424 + suggestions: null,
3425 + }),
3426 + );
3427 }
3428 const place = lowerValueToTemporary(builder, {
3429 kind: 'Primitive',
@@ -3332,12 +3432,14 @@ function lowerJsxElementName(
3432 });
3433 return place;
3434 } else {
3335 - builder.errors.push({
3336 - reason: `(BuildHIR::lowerJsxElementName) Handle ${exprPath.type} tags`,
3337 - category: ErrorCategory.Todo,
3338 - loc: exprPath.node.loc ?? null,
3339 - suggestions: null,
3340 - });
3435 + builder.recordError(
3436 + new CompilerErrorDetail({
3437 + reason: `(BuildHIR::lowerJsxElementName) Handle ${exprPath.type} tags`,
3438 + category: ErrorCategory.Todo,
3439 + loc: exprPath.node.loc ?? null,
3440 + suggestions: null,
3441 + }),
3442 + );
3443 return lowerValueToTemporary(builder, {
3444 kind: 'UnsupportedNode',
3445 node: exprNode,
@@ -3426,12 +3528,14 @@ function lowerJsxElement(
3528 });
3529 return place;
3530 } else {
3429 - builder.errors.push({
3430 - reason: `(BuildHIR::lowerJsxElement) Unhandled JsxElement, got: ${exprPath.type}`,
3431 - category: ErrorCategory.Todo,
3432 - loc: exprPath.node.loc ?? null,
3433 - suggestions: null,
3434 - });
3531 + builder.recordError(
3532 + new CompilerErrorDetail({
3533 + reason: `(BuildHIR::lowerJsxElement) Unhandled JsxElement, got: ${exprPath.type}`,
3534 + category: ErrorCategory.Todo,
3535 + loc: exprPath.node.loc ?? null,
3536 + suggestions: null,
3537 + }),
3538 + );
3539 const place = lowerValueToTemporary(builder, {
3540 kind: 'UnsupportedNode',
3541 node: exprNode,
@@ -3598,14 +3702,16 @@ function lowerIdentifier(
3702 }
3703 default: {
3704 if (binding.kind === 'Global' && binding.name === 'eval') {
3601 - builder.errors.push({
3602 - reason: `The 'eval' function is not supported`,
3603 - description:
3604 - 'Eval is an anti-pattern in JavaScript, and the code executed cannot be evaluated by React Compiler',
3605 - category: ErrorCategory.UnsupportedSyntax,
3606 - loc: exprPath.node.loc ?? null,
3607 - suggestions: null,
3608 - });
3705 + builder.recordError(
3706 + new CompilerErrorDetail({
3707 + reason: `The 'eval' function is not supported`,
3708 + description:
3709 + 'Eval is an anti-pattern in JavaScript, and the code executed cannot be evaluated by React Compiler',
3710 + category: ErrorCategory.UnsupportedSyntax,
3711 + loc: exprPath.node.loc ?? null,
3712 + suggestions: null,
3713 + }),
3714 + );
3715 }
3716 return lowerValueToTemporary(builder, {
3717 kind: 'LoadGlobal',
@@ -3656,27 +3762,31 @@ function lowerIdentifierForAssignment(
3762 return {kind: 'Global', name: path.node.name};
3763 } else {
3764 // Else its an internal error bc we couldn't find the binding
3659 - builder.errors.push({
3660 - reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
3661 - category: ErrorCategory.Invariant,
3662 - loc: path.node.loc ?? null,
3663 - suggestions: null,
3664 - });
3765 + builder.recordError(
3766 + new CompilerErrorDetail({
3767 + reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
3768 + category: ErrorCategory.Invariant,
3769 + loc: path.node.loc ?? null,
3770 + suggestions: null,
3771 + }),
3772 + );
3773 return null;
3774 }
3775 } else if (
3776 binding.bindingKind === 'const' &&
3777 kind === InstructionKind.Reassign
3778 ) {
3671 - builder.errors.push({
3672 - reason: `Cannot reassign a \`const\` variable`,
3673 - category: ErrorCategory.Syntax,
3674 - loc: path.node.loc ?? null,
3675 - description:
3676 - binding.identifier.name != null
3677 - ? `\`${binding.identifier.name.value}\` is declared as const`
3678 - : null,
3679 - });
3779 + builder.recordError(
3780 + new CompilerErrorDetail({
3781 + reason: `Cannot reassign a \`const\` variable`,
3782 + category: ErrorCategory.Syntax,
3783 + loc: path.node.loc ?? null,
3784 + description:
3785 + binding.identifier.name != null
3786 + ? `\`${binding.identifier.name.value}\` is declared as const`
3787 + : null,
3788 + }),
3789 + );
3790 return null;
3791 }
3792
@@ -3725,12 +3835,14 @@ function lowerAssignment(
3835 let temporary;
3836 if (builder.isContextIdentifier(lvalue)) {
3837 if (kind === InstructionKind.Const && !isHoistedIdentifier) {
3728 - builder.errors.push({
3729 - reason: `Expected \`const\` declaration not to be reassigned`,
3730 - category: ErrorCategory.Syntax,
3731 - loc: lvalue.node.loc ?? null,
3732 - suggestions: null,
3733 - });
3838 + builder.recordError(
3839 + new CompilerErrorDetail({
3840 + reason: `Expected \`const\` declaration not to be reassigned`,
3841 + category: ErrorCategory.Syntax,
3842 + loc: lvalue.node.loc ?? null,
3843 + suggestions: null,
3844 + }),
3845 + );
3846 }
3847
3848 if (
@@ -3739,12 +3851,14 @@ function lowerAssignment(
3851 kind !== InstructionKind.Let &&
3852 kind !== InstructionKind.Function
3853 ) {
3742 - builder.errors.push({
3743 - reason: `Unexpected context variable kind`,
3744 - category: ErrorCategory.Syntax,
3745 - loc: lvalue.node.loc ?? null,
3746 - suggestions: null,
3747 - });
3854 + builder.recordError(
3855 + new CompilerErrorDetail({
3856 + reason: `Unexpected context variable kind`,
3857 + category: ErrorCategory.Syntax,
3858 + loc: lvalue.node.loc ?? null,
3859 + suggestions: null,
3860 + }),
3861 + );
3862 temporary = lowerValueToTemporary(builder, {
3863 kind: 'UnsupportedNode',
3864 node: lvalueNode,
@@ -3808,24 +3922,28 @@ function lowerAssignment(
3922 loc,
3923 });
3924 } else {
3811 - builder.errors.push({
3812 - reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in MemberExpression`,
3813 - category: ErrorCategory.Todo,
3814 - loc: property.node.loc ?? null,
3815 - suggestions: null,
3816 - });
3925 + builder.recordError(
3926 + new CompilerErrorDetail({
3927 + reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in MemberExpression`,
3928 + category: ErrorCategory.Todo,
3929 + loc: property.node.loc ?? null,
3930 + suggestions: null,
3931 + }),
3932 + );
3933 return {kind: 'UnsupportedNode', node: lvalueNode, loc};
3934 }
3935 return {kind: 'LoadLocal', place: temporary, loc: temporary.loc};
3936 } else {
3937 if (!property.isExpression()) {
3822 - builder.errors.push({
3823 - reason:
3824 - '(BuildHIR::lowerAssignment) Expected private name to appear as a non-computed property',
3825 - category: ErrorCategory.Todo,
3826 - loc: property.node.loc ?? null,
3827 - suggestions: null,
3828 - });
3938 + builder.recordError(
3939 + new CompilerErrorDetail({
3940 + reason:
3941 + '(BuildHIR::lowerAssignment) Expected private name to appear as a non-computed property',
3942 + category: ErrorCategory.Todo,
3943 + loc: property.node.loc ?? null,
3944 + suggestions: null,
3945 + }),
3946 + );
3947 return {kind: 'UnsupportedNode', node: lvalueNode, loc};
3948 }
3949 const propertyPlace = lowerExpressionToTemporary(builder, property);
@@ -3886,12 +4004,14 @@ function lowerAssignment(
4004 if (identifier === null) {
4005 continue;
4006 } else if (identifier.kind === 'Global') {
3889 - builder.errors.push({
3890 - category: ErrorCategory.Todo,
3891 - reason:
3892 - 'Expected reassignment of globals to enable forceTemporaries',
3893 - loc: element.node.loc ?? GeneratedSource,
3894 - });
4007 + builder.recordError(
4008 + new CompilerErrorDetail({
4009 + category: ErrorCategory.Todo,
4010 + reason:
4011 + 'Expected reassignment of globals to enable forceTemporaries',
4012 + loc: element.node.loc ?? GeneratedSource,
4013 + }),
4014 + );
4015 continue;
4016 }
4017 items.push({
@@ -3925,12 +4045,14 @@ function lowerAssignment(
4045 if (identifier === null) {
4046 continue;
4047 } else if (identifier.kind === 'Global') {
3928 - builder.errors.push({
3929 - category: ErrorCategory.Todo,
3930 - reason:
3931 - 'Expected reassignment of globals to enable forceTemporaries',
3932 - loc: element.node.loc ?? GeneratedSource,
3933 - });
4048 + builder.recordError(
4049 + new CompilerErrorDetail({
4050 + category: ErrorCategory.Todo,
4051 + reason:
4052 + 'Expected reassignment of globals to enable forceTemporaries',
4053 + loc: element.node.loc ?? GeneratedSource,
4054 + }),
4055 + );
4056 continue;
4057 }
4058 items.push(identifier);
@@ -3998,12 +4120,14 @@ function lowerAssignment(
4120 if (property.isRestElement()) {
4121 const argument = property.get('argument');
4122 if (!argument.isIdentifier()) {
4001 - builder.errors.push({
4002 - reason: `(BuildHIR::lowerAssignment) Handle ${argument.node.type} rest element in ObjectPattern`,
4003 - category: ErrorCategory.Todo,
4004 - loc: argument.node.loc ?? null,
4005 - suggestions: null,
4006 - });
4123 + builder.recordError(
4124 + new CompilerErrorDetail({
4125 + reason: `(BuildHIR::lowerAssignment) Handle ${argument.node.type} rest element in ObjectPattern`,
4126 + category: ErrorCategory.Todo,
4127 + loc: argument.node.loc ?? null,
4128 + suggestions: null,
4129 + }),
4130 + );
4131 continue;
4132 }
4133 if (
@@ -4030,12 +4154,14 @@ function lowerAssignment(
4154 if (identifier === null) {
4155 continue;
4156 } else if (identifier.kind === 'Global') {
4033 - builder.errors.push({
4034 - category: ErrorCategory.Todo,
4035 - reason:
4036 - 'Expected reassignment of globals to enable forceTemporaries',
4037 - loc: property.node.loc ?? GeneratedSource,
4038 - });
4157 + builder.recordError(
4158 + new CompilerErrorDetail({
4159 + category: ErrorCategory.Todo,
4160 + reason:
4161 + 'Expected reassignment of globals to enable forceTemporaries',
4162 + loc: property.node.loc ?? GeneratedSource,
4163 + }),
4164 + );
4165 continue;
4166 }
4167 properties.push({
@@ -4046,21 +4172,25 @@ function lowerAssignment(
4172 } else {
4173 // TODO: this should always be true given the if/else
4174 if (!property.isObjectProperty()) {
4049 - builder.errors.push({
4050 - reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in ObjectPattern`,
4051 - category: ErrorCategory.Todo,
4052 - loc: property.node.loc ?? null,
4053 - suggestions: null,
4054 - });
4175 + builder.recordError(
4176 + new CompilerErrorDetail({
4177 + reason: `(BuildHIR::lowerAssignment) Handle ${property.type} properties in ObjectPattern`,
4178 + category: ErrorCategory.Todo,
4179 + loc: property.node.loc ?? null,
4180 + suggestions: null,
4181 + }),
4182 + );
4183 continue;
4184 }
4185 if (property.node.computed) {
4058 - builder.errors.push({
4059 - reason: `(BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern`,
4060 - category: ErrorCategory.Todo,
4061 - loc: property.node.loc ?? null,
4062 - suggestions: null,
4063 - });
4186 + builder.recordError(
4187 + new CompilerErrorDetail({
4188 + reason: `(BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern`,
4189 + category: ErrorCategory.Todo,
4190 + loc: property.node.loc ?? null,
4191 + suggestions: null,
4192 + }),
4193 + );
4194 continue;
4195 }
4196 const loweredKey = lowerObjectPropertyKey(builder, property);
@@ -4069,12 +4199,14 @@ function lowerAssignment(
4199 }
4200 const element = property.get('value');
4201 if (!element.isLVal()) {
4072 - builder.errors.push({
4073 - reason: `(BuildHIR::lowerAssignment) Expected object property value to be an LVal, got: ${element.type}`,
4074 - category: ErrorCategory.Todo,
4075 - loc: element.node.loc ?? null,
4076 - suggestions: null,
4077 - });
4202 + builder.recordError(
4203 + new CompilerErrorDetail({
4204 + reason: `(BuildHIR::lowerAssignment) Expected object property value to be an LVal, got: ${element.type}`,
4205 + category: ErrorCategory.Todo,
4206 + loc: element.node.loc ?? null,
4207 + suggestions: null,
4208 + }),
4209 + );
4210 continue;
4211 }
4212 if (
@@ -4092,12 +4224,14 @@ function lowerAssignment(
4224 if (identifier === null) {
4225 continue;
4226 } else if (identifier.kind === 'Global') {
4095 - builder.errors.push({
4096 - category: ErrorCategory.Todo,
4097 - reason:
4098 - 'Expected reassignment of globals to enable forceTemporaries',
4099 - loc: element.node.loc ?? GeneratedSource,
4100 - });
4227 + builder.recordError(
4228 + new CompilerErrorDetail({
4229 + category: ErrorCategory.Todo,
4230 + reason:
4231 + 'Expected reassignment of globals to enable forceTemporaries',
4232 + loc: element.node.loc ?? GeneratedSource,
4233 + }),
4234 + );
4235 continue;
4236 }
4237 properties.push({
@@ -4241,12 +4375,14 @@ function lowerAssignment(
4375 );
4376 }
4377 default: {
4244 - builder.errors.push({
4245 - reason: `(BuildHIR::lowerAssignment) Handle ${lvaluePath.type} assignments`,
4246 - category: ErrorCategory.Todo,
4247 - loc: lvaluePath.node.loc ?? null,
4248 - suggestions: null,
4249 - });
4378 + builder.recordError(
4379 + new CompilerErrorDetail({
4380 + reason: `(BuildHIR::lowerAssignment) Handle ${lvaluePath.type} assignments`,
4381 + category: ErrorCategory.Todo,
4382 + loc: lvaluePath.node.loc ?? null,
4383 + suggestions: null,
4384 + }),
4385 + );
4386 return {kind: 'UnsupportedNode', node: lvalueNode, loc};
4387 }
4388 }
compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts
+39 -25
@@ -7,7 +7,12 @@
7
8 import {Binding, NodePath} from '@babel/traverse';
9 import * as t from '@babel/types';
10 -import {CompilerError, ErrorCategory} from '../CompilerError';
10 +import {
11 + CompilerError,
12 + CompilerDiagnostic,
13 + CompilerErrorDetail,
14 + ErrorCategory,
15 +} from '../CompilerError';
16 import {Environment} from './Environment';
17 import {
18 BasicBlock,
@@ -110,7 +115,6 @@ export default class HIRBuilder {
115 #bindings: Bindings;
116 #env: Environment;
117 #exceptionHandlerStack: Array<BlockId> = [];
113 - errors: CompilerError = new CompilerError();
118 /**
119 * Traversal context: counts the number of `fbt` tag parents
120 * of the current babel node.
@@ -148,6 +152,10 @@ export default class HIRBuilder {
152 this.#current = newBlock(this.#entry, options?.entryBlockKind ?? 'block');
153 }
154
155 + recordError(error: CompilerDiagnostic | CompilerErrorDetail): void {
156 + this.#env.recordError(error);
157 + }
158 +
159 currentBlockKind(): BlockKind {
160 return this.#current.kind;
161 }
@@ -308,24 +316,28 @@ export default class HIRBuilder {
316
317 resolveBinding(node: t.Identifier): Identifier {
318 if (node.name === 'fbt') {
311 - this.errors.push({
312 - category: ErrorCategory.Todo,
313 - reason: 'Support local variables named `fbt`',
314 - description:
315 - 'Local variables named `fbt` may conflict with the fbt plugin and are not yet supported',
316 - loc: node.loc ?? GeneratedSource,
317 - suggestions: null,
318 - });
319 + this.recordError(
320 + new CompilerErrorDetail({
321 + category: ErrorCategory.Todo,
322 + reason: 'Support local variables named `fbt`',
323 + description:
324 + 'Local variables named `fbt` may conflict with the fbt plugin and are not yet supported',
325 + loc: node.loc ?? GeneratedSource,
326 + suggestions: null,
327 + }),
328 + );
329 }
330 if (node.name === 'this') {
321 - this.errors.push({
322 - category: ErrorCategory.UnsupportedSyntax,
323 - reason: '`this` is not supported syntax',
324 - description:
325 - 'React Compiler does not support compiling functions that use `this`',
326 - loc: node.loc ?? GeneratedSource,
327 - suggestions: null,
328 - });
331 + this.recordError(
332 + new CompilerErrorDetail({
333 + category: ErrorCategory.UnsupportedSyntax,
334 + reason: '`this` is not supported syntax',
335 + description:
336 + 'React Compiler does not support compiling functions that use `this`',
337 + loc: node.loc ?? GeneratedSource,
338 + suggestions: null,
339 + }),
340 + );
341 }
342 const originalName = node.name;
343 let name = originalName;
@@ -371,13 +383,15 @@ export default class HIRBuilder {
383 instr => instr.value.kind === 'FunctionExpression',
384 )
385 ) {
374 - this.errors.push({
375 - reason: `Support functions with unreachable code that may contain hoisted declarations`,
376 - loc: block.instructions[0]?.loc ?? block.terminal.loc,
377 - description: null,
378 - suggestions: null,
379 - category: ErrorCategory.Todo,
380 - });
386 + this.recordError(
387 + new CompilerErrorDetail({
388 + reason: `Support functions with unreachable code that may contain hoisted declarations`,
389 + loc: block.instructions[0]?.loc ?? block.terminal.loc,
390 + description: null,
391 + suggestions: null,
392 + category: ErrorCategory.Todo,
393 + }),
394 + );
395 }
396 }
397 ir.blocks = rpoBlocks;
compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts
+6 -11
@@ -293,7 +293,7 @@ function extractManualMemoizationArgs(
293 instr: TInstruction<CallExpression> | TInstruction<MethodCall>,
294 kind: 'useCallback' | 'useMemo',
295 sidemap: IdentifierSidemap,
296 - errors: CompilerError,
296 + env: Environment,
297 ): {
298 fnPlace: Place;
299 depsList: Array<ManualMemoDependency> | null;
@@ -303,7 +303,7 @@ function extractManualMemoizationArgs(
303 Place | SpreadPattern | undefined
304 >;
305 if (fnPlace == null || fnPlace.kind !== 'Identifier') {
306 - errors.pushDiagnostic(
306 + env.recordError(
307 CompilerDiagnostic.create({
308 category: ErrorCategory.UseMemo,
309 reason: `Expected a callback function to be passed to ${kind}`,
@@ -335,7 +335,7 @@ function extractManualMemoizationArgs(
335 ? sidemap.maybeDepsLists.get(depsListPlace.identifier.id)
336 : null;
337 if (maybeDepsList == null) {
338 - errors.pushDiagnostic(
338 + env.recordError(
339 CompilerDiagnostic.create({
340 category: ErrorCategory.UseMemo,
341 reason: `Expected the dependency list for ${kind} to be an array literal`,
@@ -354,7 +354,7 @@ function extractManualMemoizationArgs(
354 for (const dep of maybeDepsList.deps) {
355 const maybeDep = sidemap.maybeDeps.get(dep.identifier.id);
356 if (maybeDep == null) {
357 - errors.pushDiagnostic(
357 + env.recordError(
358 CompilerDiagnostic.create({
359 category: ErrorCategory.UseMemo,
360 reason: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
@@ -389,7 +389,6 @@ function extractManualMemoizationArgs(
389 * is only used for memoizing values and not for running arbitrary side effects.
390 */
391 export function dropManualMemoization(func: HIRFunction): void {
392 - const errors = new CompilerError();
392 const isValidationEnabled =
393 func.env.config.validatePreserveExistingMemoizationGuarantees ||
394 func.env.config.validateNoSetStateInRender ||
@@ -436,7 +435,7 @@ export function dropManualMemoization(func: HIRFunction): void {
435 instr as TInstruction<CallExpression> | TInstruction<MethodCall>,
436 manualMemo.kind,
437 sidemap,
439 - errors,
438 + func.env,
439 );
440
441 if (memoDetails == null) {
@@ -464,7 +463,7 @@ export function dropManualMemoization(func: HIRFunction): void {
463 * is rare and likely sketchy.
464 */
465 if (!sidemap.functions.has(fnPlace.identifier.id)) {
467 - errors.pushDiagnostic(
466 + func.env.recordError(
467 CompilerDiagnostic.create({
468 category: ErrorCategory.UseMemo,
469 reason: `Expected the first argument to be an inline function expression`,
@@ -549,10 +548,6 @@ export function dropManualMemoization(func: HIRFunction): void {
548 markInstructionIds(func.body);
549 }
550 }
552 -
553 - if (errors.hasAnyErrors()) {
554 - func.env.recordErrors(errors);
555 - }
551 }
552
553 function findOptionalPlaces(fn: HIRFunction): Set<IdentifierId> {
compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingRanges.ts
+19 -19
@@ -20,6 +20,7 @@ import {
20 Place,
21 isPrimitiveType,
22 } from '../HIR/HIR';
23 +import {Environment} from '../HIR/Environment';
24 import {
25 eachInstructionLValue,
26 eachInstructionValueOperand,
@@ -107,7 +108,7 @@ export function inferMutationAliasingRanges(
108
109 let index = 0;
110
110 - const errors = new CompilerError();
111 + const shouldRecordErrors = !isFunctionExpression && fn.env.enableValidations;
112
113 for (const param of [...fn.params, ...fn.context, fn.returns]) {
114 const place = param.kind === 'Identifier' ? param : param.place;
@@ -200,7 +201,9 @@ export function inferMutationAliasingRanges(
201 effect.kind === 'MutateGlobal' ||
202 effect.kind === 'Impure'
203 ) {
203 - errors.pushDiagnostic(effect.error);
204 + if (shouldRecordErrors) {
205 + fn.env.recordError(effect.error);
206 + }
207 functionEffects.push(effect);
208 } else if (effect.kind === 'Render') {
209 renders.push({index: index++, place: effect.place});
@@ -245,11 +248,15 @@ export function inferMutationAliasingRanges(
248 mutation.kind,
249 mutation.place.loc,
250 mutation.reason,
248 - errors,
251 + shouldRecordErrors ? fn.env : null,
252 );
253 }
254 for (const render of renders) {
252 - state.render(render.index, render.place.identifier, errors);
255 + state.render(
256 + render.index,
257 + render.place.identifier,
258 + shouldRecordErrors ? fn.env : null,
259 + );
260 }
261 for (const param of [...fn.context, ...fn.params]) {
262 const place = param.kind === 'Identifier' ? param : param.place;
@@ -498,7 +505,6 @@ export function inferMutationAliasingRanges(
505 * would be transitively mutated needs a capture relationship.
506 */
507 const tracked: Array<Place> = [];
501 - const ignoredErrors = new CompilerError();
508 for (const param of [...fn.params, ...fn.context, fn.returns]) {
509 const place = param.kind === 'Identifier' ? param : param.place;
510 tracked.push(place);
@@ -513,7 +519,7 @@ export function inferMutationAliasingRanges(
519 MutationKind.Conditional,
520 into.loc,
521 null,
516 - ignoredErrors,
522 + null,
523 );
524 for (const from of tracked) {
525 if (
@@ -547,23 +553,17 @@ export function inferMutationAliasingRanges(
553 }
554 }
555
550 - if (
551 - errors.hasAnyErrors() &&
552 - !isFunctionExpression &&
553 - fn.env.enableValidations
554 - ) {
555 - fn.env.recordErrors(errors);
556 - }
556 return functionEffects;
557 }
558
560 -function appendFunctionErrors(errors: CompilerError, fn: HIRFunction): void {
559 +function appendFunctionErrors(env: Environment | null, fn: HIRFunction): void {
560 + if (env == null) return;
561 for (const effect of fn.aliasingEffects ?? []) {
562 switch (effect.kind) {
563 case 'Impure':
564 case 'MutateFrozen':
565 case 'MutateGlobal': {
566 - errors.pushDiagnostic(effect.error);
566 + env.recordError(effect.error);
567 break;
568 }
569 }
@@ -664,7 +664,7 @@ class AliasingState {
664 }
665 }
666
667 - render(index: number, start: Identifier, errors: CompilerError): void {
667 + render(index: number, start: Identifier, env: Environment | null): void {
668 const seen = new Set<Identifier>();
669 const queue: Array<Identifier> = [start];
670 while (queue.length !== 0) {
@@ -678,7 +678,7 @@ class AliasingState {
678 continue;
679 }
680 if (node.value.kind === 'Function') {
681 - appendFunctionErrors(errors, node.value.function);
681 + appendFunctionErrors(env, node.value.function);
682 }
683 for (const [alias, when] of node.createdFrom) {
684 if (when >= index) {
@@ -710,7 +710,7 @@ class AliasingState {
710 startKind: MutationKind,
711 loc: SourceLocation,
712 reason: MutationReason | null,
713 - errors: CompilerError,
713 + env: Environment | null,
714 ): void {
715 const seen = new Map<Identifier, MutationKind>();
716 const queue: Array<{
@@ -742,7 +742,7 @@ class AliasingState {
742 node.transitive == null &&
743 node.local == null
744 ) {
745 - appendFunctionErrors(errors, node.value.function);
745 + appendFunctionErrors(env, node.value.function);
746 }
747 if (transitive) {
748 if (node.transitive == null || node.transitive.kind < kind) {
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+59 -44
@@ -13,7 +13,11 @@ import {
13 pruneUnusedLabels,
14 renameVariables,
15 } from '.';
16 -import {CompilerError, ErrorCategory} from '../CompilerError';
16 +import {
17 + CompilerError,
18 + CompilerErrorDetail,
19 + ErrorCategory,
20 +} from '../CompilerError';
21 import {Environment, ExternalFunction} from '../HIR';
22 import {
23 ArrayPattern,
@@ -347,10 +351,6 @@ function codegenReactiveFunction(
351 }
352 }
353
350 - if (cx.errors.hasAnyErrors()) {
351 - fn.env.recordErrors(cx.errors);
352 - }
353 -
354 const countMemoBlockVisitor = new CountMemoBlockVisitor(fn.env);
355 visitReactiveFunction(fn, countMemoBlockVisitor, undefined);
356
@@ -420,7 +420,6 @@ class Context {
420 */
421 #declarations: Set<DeclarationId> = new Set();
422 temp: Temporaries;
423 - errors: CompilerError = new CompilerError();
423 objectMethods: Map<IdentifierId, ObjectMethod> = new Map();
424 uniqueIdentifiers: Set<string>;
425 fbtOperands: Set<IdentifierId>;
@@ -439,6 +438,10 @@ class Context {
438 this.fbtOperands = fbtOperands;
439 this.temp = temporaries !== null ? new Map(temporaries) : new Map();
440 }
441 +
442 + recordError(error: CompilerErrorDetail): void {
443 + this.env.recordError(error);
444 + }
445 get nextCacheIndex(): number {
446 return this.#nextCacheIndex++;
447 }
@@ -775,12 +778,14 @@ function codegenTerminal(
778 loc: terminal.init.loc,
779 });
780 if (terminal.init.instructions.length !== 2) {
778 - cx.errors.push({
779 - reason: 'Support non-trivial for..in inits',
780 - category: ErrorCategory.Todo,
781 - loc: terminal.init.loc,
782 - suggestions: null,
783 - });
781 + cx.recordError(
782 + new CompilerErrorDetail({
783 + reason: 'Support non-trivial for..in inits',
784 + category: ErrorCategory.Todo,
785 + loc: terminal.init.loc,
786 + suggestions: null,
787 + }),
788 + );
789 return t.emptyStatement();
790 }
791 const iterableCollection = terminal.init.instructions[0];
@@ -796,12 +801,14 @@ function codegenTerminal(
801 break;
802 }
803 case 'StoreContext': {
799 - cx.errors.push({
800 - reason: 'Support non-trivial for..in inits',
801 - category: ErrorCategory.Todo,
802 - loc: terminal.init.loc,
803 - suggestions: null,
804 - });
804 + cx.recordError(
805 + new CompilerErrorDetail({
806 + reason: 'Support non-trivial for..in inits',
807 + category: ErrorCategory.Todo,
808 + loc: terminal.init.loc,
809 + suggestions: null,
810 + }),
811 + );
812 return t.emptyStatement();
813 }
814 default:
@@ -872,12 +879,14 @@ function codegenTerminal(
879 loc: terminal.test.loc,
880 });
881 if (terminal.test.instructions.length !== 2) {
875 - cx.errors.push({
876 - reason: 'Support non-trivial for..of inits',
877 - category: ErrorCategory.Todo,
878 - loc: terminal.init.loc,
879 - suggestions: null,
880 - });
882 + cx.recordError(
883 + new CompilerErrorDetail({
884 + reason: 'Support non-trivial for..of inits',
885 + category: ErrorCategory.Todo,
886 + loc: terminal.init.loc,
887 + suggestions: null,
888 + }),
889 + );
890 return t.emptyStatement();
891 }
892 const iterableItem = terminal.test.instructions[1];
@@ -892,12 +901,14 @@ function codegenTerminal(
901 break;
902 }
903 case 'StoreContext': {
895 - cx.errors.push({
896 - reason: 'Support non-trivial for..of inits',
897 - category: ErrorCategory.Todo,
898 - loc: terminal.init.loc,
899 - suggestions: null,
900 - });
904 + cx.recordError(
905 + new CompilerErrorDetail({
906 + reason: 'Support non-trivial for..of inits',
907 + category: ErrorCategory.Todo,
908 + loc: terminal.init.loc,
909 + suggestions: null,
910 + }),
911 + );
912 return t.emptyStatement();
913 }
914 default:
@@ -1957,22 +1968,26 @@ function codegenInstructionValue(
1968 } else {
1969 if (t.isVariableDeclaration(stmt)) {
1970 const declarator = stmt.declarations[0];
1960 - cx.errors.push({
1961 - reason: `(CodegenReactiveFunction::codegenInstructionValue) Cannot declare variables in a value block, tried to declare '${
1962 - (declarator.id as t.Identifier).name
1963 - }'`,
1964 - category: ErrorCategory.Todo,
1965 - loc: declarator.loc ?? null,
1966 - suggestions: null,
1967 - });
1971 + cx.recordError(
1972 + new CompilerErrorDetail({
1973 + reason: `(CodegenReactiveFunction::codegenInstructionValue) Cannot declare variables in a value block, tried to declare '${
1974 + (declarator.id as t.Identifier).name
1975 + }'`,
1976 + category: ErrorCategory.Todo,
1977 + loc: declarator.loc ?? null,
1978 + suggestions: null,
1979 + }),
1980 + );
1981 return t.stringLiteral(`TODO handle ${declarator.id}`);
1982 } else {
1970 - cx.errors.push({
1971 - reason: `(CodegenReactiveFunction::codegenInstructionValue) Handle conversion of ${stmt.type} to expression`,
1972 - category: ErrorCategory.Todo,
1973 - loc: stmt.loc ?? null,
1974 - suggestions: null,
1975 - });
1983 + cx.recordError(
1984 + new CompilerErrorDetail({
1985 + reason: `(CodegenReactiveFunction::codegenInstructionValue) Handle conversion of ${stmt.type} to expression`,
1986 + category: ErrorCategory.Todo,
1987 + loc: stmt.loc ?? null,
1988 + suggestions: null,
1989 + }),
1990 + );
1991 return t.stringLiteral(`TODO handle ${stmt.type}`);
1992 }
1993 }
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts
+2 -6
@@ -102,7 +102,6 @@ export function validateExhaustiveDependencies(fn: HIRFunction): void {
102 loc: place.loc,
103 });
104 }
105 - const error = new CompilerError();
105 let startMemo: StartMemoize | null = null;
106
107 function onStartMemoize(
@@ -143,7 +142,7 @@ export function validateExhaustiveDependencies(fn: HIRFunction): void {
142 'all',
143 );
144 if (diagnostic != null) {
146 - error.pushDiagnostic(diagnostic);
145 + fn.env.recordError(diagnostic);
146 }
147 }
148
@@ -208,15 +207,12 @@ export function validateExhaustiveDependencies(fn: HIRFunction): void {
207 effectReportMode,
208 );
209 if (diagnostic != null) {
211 - error.pushDiagnostic(diagnostic);
210 + fn.env.recordError(diagnostic);
211 }
212 },
213 },
214 false, // isFunctionExpression
215 );
217 - if (error.hasAnyErrors()) {
218 - fn.env.recordErrors(error);
219 - }
216 }
217
218 function validateDependencies(
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts
+12 -20
@@ -6,13 +6,9 @@
6 */
7
8 import * as t from '@babel/types';
9 -import {
10 - CompilerError,
11 - CompilerErrorDetail,
12 - ErrorCategory,
13 -} from '../CompilerError';
9 +import {CompilerErrorDetail, ErrorCategory} from '../CompilerError';
10 import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks';
15 -import {isHookName} from '../HIR/Environment';
11 +import {Environment, isHookName} from '../HIR/Environment';
12 import {
13 HIRFunction,
14 IdentifierId,
@@ -90,15 +86,14 @@ function joinKinds(a: Kind, b: Kind): Kind {
86 export function validateHooksUsage(fn: HIRFunction): void {
87 const unconditionalBlocks = computeUnconditionalBlocks(fn);
88
93 - const errors = new CompilerError();
89 const errorsByPlace = new Map<t.SourceLocation, CompilerErrorDetail>();
90
96 - function recordError(
91 + function trackError(
92 loc: SourceLocation,
93 errorDetail: CompilerErrorDetail,
94 ): void {
95 if (typeof loc === 'symbol') {
101 - errors.pushErrorDetail(errorDetail);
96 + fn.env.recordError(errorDetail);
97 } else {
98 errorsByPlace.set(loc, errorDetail);
99 }
@@ -118,7 +113,7 @@ export function validateHooksUsage(fn: HIRFunction): void {
113 * If that same place is also used as a conditional call, upgrade the error to a conditonal hook error
114 */
115 if (previousError === undefined || previousError.reason !== reason) {
121 - recordError(
116 + trackError(
117 place.loc,
118 new CompilerErrorDetail({
119 category: ErrorCategory.Hooks,
@@ -134,7 +129,7 @@ export function validateHooksUsage(fn: HIRFunction): void {
129 const previousError =
130 typeof place.loc !== 'symbol' ? errorsByPlace.get(place.loc) : undefined;
131 if (previousError === undefined) {
137 - recordError(
132 + trackError(
133 place.loc,
134 new CompilerErrorDetail({
135 category: ErrorCategory.Hooks,
@@ -151,7 +146,7 @@ export function validateHooksUsage(fn: HIRFunction): void {
146 const previousError =
147 typeof place.loc !== 'symbol' ? errorsByPlace.get(place.loc) : undefined;
148 if (previousError === undefined) {
154 - recordError(
149 + trackError(
150 place.loc,
151 new CompilerErrorDetail({
152 category: ErrorCategory.Hooks,
@@ -396,7 +391,7 @@ export function validateHooksUsage(fn: HIRFunction): void {
391 }
392 case 'ObjectMethod':
393 case 'FunctionExpression': {
399 - visitFunctionExpression(errors, instr.value.loweredFunc.func);
394 + visitFunctionExpression(fn.env, instr.value.loweredFunc.func);
395 break;
396 }
397 default: {
@@ -421,20 +416,17 @@ export function validateHooksUsage(fn: HIRFunction): void {
416 }
417
418 for (const [, error] of errorsByPlace) {
424 - errors.pushErrorDetail(error);
425 - }
426 - if (errors.hasAnyErrors()) {
427 - fn.env.recordErrors(errors);
419 + fn.env.recordError(error);
420 }
421 }
422
431 -function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void {
423 +function visitFunctionExpression(env: Environment, fn: HIRFunction): void {
424 for (const [, block] of fn.body.blocks) {
425 for (const instr of block.instructions) {
426 switch (instr.value.kind) {
427 case 'ObjectMethod':
428 case 'FunctionExpression': {
437 - visitFunctionExpression(errors, instr.value.loweredFunc.func);
429 + visitFunctionExpression(env, instr.value.loweredFunc.func);
430 break;
431 }
432 case 'MethodCall':
@@ -445,7 +437,7 @@ function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void {
437 : instr.value.property;
438 const hookKind = getHookKind(fn.env, callee.identifier);
439 if (hookKind != null) {
448 - errors.pushErrorDetail(
440 + env.recordError(
441 new CompilerErrorDetail({
442 category: ErrorCategory.Hooks,
443 reason:
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts
+10 -12
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import {CompilerError, CompilerErrorDetail, EnvironmentConfig} from '..';
8 +import {CompilerErrorDetail, EnvironmentConfig} from '..';
9 import {ErrorCategory} from '../CompilerError';
10 import {HIRFunction, IdentifierId} from '../HIR';
11 import {DEFAULT_GLOBALS} from '../HIR/Globals';
@@ -20,7 +20,6 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
20 return ALLOW_LIST.has(name);
21 };
22
23 - const errors = new CompilerError();
23 const capitalLoadGlobals = new Map<IdentifierId, string>();
24 const capitalizedProperties = new Map<IdentifierId, string>();
25 const reason =
@@ -72,20 +71,19 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
71 const propertyIdentifier = value.property.identifier.id;
72 const propertyName = capitalizedProperties.get(propertyIdentifier);
73 if (propertyName != null) {
75 - errors.push({
76 - category: ErrorCategory.CapitalizedCalls,
77 - reason,
78 - description: `${propertyName} may be a component`,
79 - loc: value.loc,
80 - suggestions: null,
81 - });
74 + fn.env.recordError(
75 + new CompilerErrorDetail({
76 + category: ErrorCategory.CapitalizedCalls,
77 + reason,
78 + description: `${propertyName} may be a component`,
79 + loc: value.loc,
80 + suggestions: null,
81 + }),
82 + );
83 }
84 break;
85 }
86 }
87 }
88 }
88 - if (errors.hasAnyErrors()) {
89 - fn.env.recordErrors(errors);
90 - }
89 }
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoDerivedComputationsInEffects.ts
+14 -14
@@ -6,7 +6,7 @@
6 */
7
8 import {CompilerError, SourceLocation} from '..';
9 -import {ErrorCategory} from '../CompilerError';
9 +import {CompilerErrorDetail, ErrorCategory} from '../CompilerError';
10 import {
11 ArrayExpression,
12 BlockId,
@@ -20,6 +20,7 @@ import {
20 eachInstructionValueOperand,
21 eachTerminalOperand,
22 } from '../HIR/visitors';
23 +import {Environment} from '../HIR/Environment';
24
25 /**
26 * Validates that useEffect is not used for derived computations which could/should
@@ -49,8 +50,6 @@ export function validateNoDerivedComputationsInEffects(fn: HIRFunction): void {
50 const functions: Map<IdentifierId, FunctionExpression> = new Map();
51 const locals: Map<IdentifierId, IdentifierId> = new Map();
52
52 - const errors = new CompilerError();
53 -
53 for (const block of fn.body.blocks.values()) {
54 for (const instr of block.instructions) {
55 const {lvalue, value} = instr;
@@ -90,20 +89,19 @@ export function validateNoDerivedComputationsInEffects(fn: HIRFunction): void {
89 validateEffect(
90 effectFunction.loweredFunc.func,
91 dependencies,
93 - errors,
92 + fn.env,
93 );
94 }
95 }
96 }
97 }
98 }
100 - fn.env.recordErrors(errors);
99 }
100
101 function validateEffect(
102 effectFunction: HIRFunction,
103 effectDeps: Array<IdentifierId>,
106 - errors: CompilerError,
104 + env: Environment,
105 ): void {
106 for (const operand of effectFunction.context) {
107 if (isSetStateType(operand.identifier)) {
@@ -217,13 +215,15 @@ function validateEffect(
215 }
216
217 for (const loc of setStateLocations) {
220 - errors.push({
221 - category: ErrorCategory.EffectDerivationsOfState,
222 - reason:
223 - 'Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)',
224 - description: null,
225 - loc,
226 - suggestions: null,
227 - });
218 + env.recordError(
219 + new CompilerErrorDetail({
220 + category: ErrorCategory.EffectDerivationsOfState,
221 + reason:
222 + 'Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)',
223 + description: null,
224 + loc,
225 + suggestions: null,
226 + }),
227 + );
228 }
229 }
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions.ts
+2 -6
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import {CompilerDiagnostic, CompilerError, Effect} from '..';
8 +import {CompilerDiagnostic, Effect} from '..';
9 import {ErrorCategory} from '../CompilerError';
10 import {
11 HIRFunction,
@@ -43,7 +43,6 @@ import {AliasingEffect} from '../Inference/AliasingEffects';
43 * that are passed where a frozen value is expected and rejects them.
44 */
45 export function validateNoFreezingKnownMutableFunctions(fn: HIRFunction): void {
46 - const errors = new CompilerError();
46 const contextMutationEffects: Map<
47 IdentifierId,
48 Extract<AliasingEffect, {kind: 'Mutate'} | {kind: 'MutateTransitive'}>
@@ -60,7 +59,7 @@ export function validateNoFreezingKnownMutableFunctions(fn: HIRFunction): void {
59 place.identifier.name.kind === 'named'
60 ? `\`${place.identifier.name.value}\``
61 : 'a local variable';
63 - errors.pushDiagnostic(
62 + fn.env.recordError(
63 CompilerDiagnostic.create({
64 category: ErrorCategory.Immutability,
65 reason: 'Cannot modify local variables after render completes',
@@ -159,7 +158,4 @@ export function validateNoFreezingKnownMutableFunctions(fn: HIRFunction): void {
158 visitOperand(operand);
159 }
160 }
162 - if (errors.hasAnyErrors()) {
163 - fn.env.recordErrors(errors);
164 - }
161 }
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts
+2 -6
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import {CompilerDiagnostic, CompilerError} from '..';
8 +import {CompilerDiagnostic} from '..';
9 import {ErrorCategory} from '../CompilerError';
10 import {HIRFunction} from '../HIR';
11 import {getFunctionCallSignature} from '../Inference/InferMutationAliasingEffects';
@@ -20,7 +20,6 @@ import {getFunctionCallSignature} from '../Inference/InferMutationAliasingEffect
20 * and use it here.
21 */
22 export function validateNoImpureFunctionsInRender(fn: HIRFunction): void {
23 - const errors = new CompilerError();
23 for (const [, block] of fn.body.blocks) {
24 for (const instr of block.instructions) {
25 const value = instr.value;
@@ -32,7 +31,7 @@ export function validateNoImpureFunctionsInRender(fn: HIRFunction): void {
31 callee.identifier.type,
32 );
33 if (signature != null && signature.impure === true) {
35 - errors.pushDiagnostic(
34 + fn.env.recordError(
35 CompilerDiagnostic.create({
36 category: ErrorCategory.Purity,
37 reason: 'Cannot call impure function during render',
@@ -52,7 +51,4 @@ export function validateNoImpureFunctionsInRender(fn: HIRFunction): void {
51 }
52 }
53 }
55 - if (errors.hasAnyErrors()) {
56 - fn.env.recordErrors(errors);
57 - }
54 }
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts
+2 -2
@@ -124,8 +124,8 @@ export function validateNoRefAccessInRender(fn: HIRFunction): void {
124 collectTemporariesSidemap(fn, env);
125 const errors = new CompilerError();
126 validateNoRefAccessInRenderImpl(fn, env, errors);
127 - if (errors.hasAnyErrors()) {
128 - fn.env.recordErrors(errors);
127 + for (const detail of errors.details) {
128 + fn.env.recordError(detail);
129 }
130 }
131
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts
+2 -2
@@ -48,8 +48,8 @@ export function validateNoSetStateInRender(fn: HIRFunction): void {
48 fn,
49 unconditionalSetStateFunctions,
50 );
51 - if (errors.hasAnyErrors()) {
52 - fn.env.recordErrors(errors);
51 + for (const detail of errors.details) {
52 + fn.env.recordError(detail);
53 }
54 }
55
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts
+8 -8
@@ -27,6 +27,7 @@ import {
27 ScopeId,
28 SourceLocation,
29 } from '../HIR';
30 +import {Environment} from '../HIR/Environment';
31 import {printIdentifier, printManualMemoDependency} from '../HIR/PrintHIR';
32 import {
33 eachInstructionValueLValue,
@@ -48,11 +49,10 @@ import {getOrInsertDefault} from '../Utils/utils';
49 */
50 export function validatePreservedManualMemoization(fn: ReactiveFunction): void {
51 const state = {
51 - errors: new CompilerError(),
52 + env: fn.env,
53 manualMemoState: null,
54 };
55 visitReactiveFunction(fn, new Visitor(), state);
55 - fn.env.recordErrors(state.errors);
56 }
57
58 const DEBUG = false;
@@ -110,7 +110,7 @@ type ManualMemoBlockState = {
110 };
111
112 type VisitorState = {
113 - errors: CompilerError;
113 + env: Environment;
114 manualMemoState: ManualMemoBlockState | null;
115 };
116
@@ -230,7 +230,7 @@ function validateInferredDep(
230 temporaries: Map<IdentifierId, ManualMemoDependency>,
231 declsWithinMemoBlock: Set<DeclarationId>,
232 validDepsInMemoBlock: Array<ManualMemoDependency>,
233 - errorState: CompilerError,
233 + errorState: Environment,
234 memoLocation: SourceLocation,
235 ): void {
236 let normalizedDep: ManualMemoDependency;
@@ -280,7 +280,7 @@ function validateInferredDep(
280 errorDiagnostic = merge(errorDiagnostic ?? compareResult, compareResult);
281 }
282 }
283 - errorState.pushDiagnostic(
283 + errorState.recordError(
284 CompilerDiagnostic.create({
285 category: ErrorCategory.PreserveManualMemo,
286 reason: 'Existing memoization could not be preserved',
@@ -426,7 +426,7 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
426 this.temporaries,
427 state.manualMemoState.decls,
428 state.manualMemoState.depsFromSource,
429 - state.errors,
429 + state.env,
430 state.manualMemoState.loc,
431 );
432 }
@@ -529,7 +529,7 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
529 !this.scopes.has(identifier.scope.id) &&
530 !this.prunedScopes.has(identifier.scope.id)
531 ) {
532 - state.errors.pushDiagnostic(
532 + state.env.recordError(
533 CompilerDiagnostic.create({
534 category: ErrorCategory.PreserveManualMemo,
535 reason: 'Existing memoization could not be preserved',
@@ -575,7 +575,7 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
575
576 for (const identifier of decls) {
577 if (isUnmemoized(identifier, this.scopes)) {
578 - state.errors.pushDiagnostic(
578 + state.env.recordError(
579 CompilerDiagnostic.create({
580 category: ErrorCategory.PreserveManualMemo,
581 reason: 'Existing memoization could not be preserved',
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateSourceLocations.ts
+3 -7
@@ -7,7 +7,7 @@
7
8 import {NodePath} from '@babel/traverse';
9 import * as t from '@babel/types';
10 -import {CompilerDiagnostic, CompilerError, ErrorCategory} from '..';
10 +import {CompilerDiagnostic, ErrorCategory} from '..';
11 import {CodegenFunction} from '../ReactiveScopes';
12 import {Environment} from '../HIR/Environment';
13
@@ -125,8 +125,6 @@ export function validateSourceLocations(
125 generatedAst: CodegenFunction,
126 env: Environment,
127 ): void {
128 - const errors = new CompilerError();
129 -
128 /*
129 * Step 1: Collect important locations from the original source
130 * Note: Multiple node types can share the same location (e.g. VariableDeclarator and Identifier)
@@ -241,7 +239,7 @@ export function validateSourceLocations(
239 loc: t.SourceLocation,
240 nodeType: string,
241 ): void => {
244 - errors.pushDiagnostic(
242 + env.recordError(
243 CompilerDiagnostic.create({
244 category: ErrorCategory.Todo,
245 reason: 'Important source location missing in generated code',
@@ -261,7 +259,7 @@ export function validateSourceLocations(
259 expectedType: string,
260 actualTypes: Set<string>,
261 ): void => {
264 - errors.pushDiagnostic(
262 + env.recordError(
263 CompilerDiagnostic.create({
264 category: ErrorCategory.Todo,
265 reason:
@@ -309,6 +307,4 @@ export function validateSourceLocations(
307 }
308 }
309 }
312 -
313 - env.recordErrors(errors);
310 }
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts
+6 -9
@@ -16,13 +16,13 @@ import {
16 IdentifierId,
17 SourceLocation,
18 } from '../HIR';
19 +import {Environment} from '../HIR/Environment';
20 import {
21 eachInstructionValueOperand,
22 eachTerminalOperand,
23 } from '../HIR/visitors';
24
25 export function validateUseMemo(fn: HIRFunction): void {
25 - const errors = new CompilerError();
26 const voidMemoErrors = new CompilerError();
27 const useMemos = new Set<IdentifierId>();
28 const react = new Set<IdentifierId>();
@@ -90,7 +90,7 @@ export function validateUseMemo(fn: HIRFunction): void {
90 firstParam.kind === 'Identifier'
91 ? firstParam.loc
92 : firstParam.place.loc;
93 - errors.pushDiagnostic(
93 + fn.env.recordError(
94 CompilerDiagnostic.create({
95 category: ErrorCategory.UseMemo,
96 reason: 'useMemo() callbacks may not accept parameters',
@@ -106,7 +106,7 @@ export function validateUseMemo(fn: HIRFunction): void {
106 }
107
108 if (body.loweredFunc.func.async || body.loweredFunc.func.generator) {
109 - errors.pushDiagnostic(
109 + fn.env.recordError(
110 CompilerDiagnostic.create({
111 category: ErrorCategory.UseMemo,
112 reason:
@@ -122,7 +122,7 @@ export function validateUseMemo(fn: HIRFunction): void {
122 );
123 }
124
125 - validateNoContextVariableAssignment(body.loweredFunc.func, errors);
125 + validateNoContextVariableAssignment(body.loweredFunc.func, fn.env);
126
127 if (fn.env.config.validateNoVoidUseMemo) {
128 if (!hasNonVoidReturn(body.loweredFunc.func)) {
@@ -176,14 +176,11 @@ export function validateUseMemo(fn: HIRFunction): void {
176 }
177 }
178 fn.env.logErrors(voidMemoErrors.asResult());
179 - if (errors.hasAnyErrors()) {
180 - fn.env.recordErrors(errors);
181 - }
179 }
180
181 function validateNoContextVariableAssignment(
182 fn: HIRFunction,
186 - errors: CompilerError,
183 + env: Environment,
184 ): void {
185 const context = new Set(fn.context.map(place => place.identifier.id));
186 for (const block of fn.body.blocks.values()) {
@@ -192,7 +189,7 @@ function validateNoContextVariableAssignment(
189 switch (value.kind) {
190 case 'StoreContext': {
191 if (context.has(value.lvalue.place.identifier.id)) {
195 - errors.pushDiagnostic(
192 + env.recordError(
193 CompilerDiagnostic.create({
194 category: ErrorCategory.UseMemo,
195 reason: