@samitouri / QOS-React-2 / commits / 18d4913406

[babel] Don't use source location for hoisting check

Instead of using the source location to check for hoisting, just stop checking for a given component after we reach it's declaration. By definition all references to it before are (potential) hoisting errors. Note that there could be false positives but that's ok.

Sathya Gunasekaran committed Nov 28, 2023 at 14:18 UTC 18d4913406d7cb628b2d110f9b4767ce108e99d2
4 files changed +31 -57
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts
+13 -19
@@ -618,7 +618,18 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel(
618 program.traverse({
619 Identifier(id) {
620 const fn = fnNames.get(id.node.name);
621 - if (fnIds.has(id.node) || !fn) {
621 + // We're not tracking this identifier.
622 + if (!fn) {
623 + return;
624 + }
625 +
626 + /*
627 + * We've reached the declaration, hoisting is no longer possible, stop
628 + * checking for this component name.
629 + */
630 + if (fnIds.has(id.node)) {
631 + fnIds.delete(id.node);
632 + fnNames.delete(id.node.name);
633 return;
634 }
635
@@ -627,12 +638,7 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel(
638 * A null scope means there's no function scope, which means we're at the
639 * top level scope.
640 */
630 - if (
631 - scope === null &&
632 - id.node.loc &&
633 - fn.loc &&
634 - occursBefore(id.node.loc, fn.loc)
635 - ) {
641 + if (scope === null) {
642 errors.pushErrorDetail(
643 new CompilerErrorDetail({
644 reason: `Encountered ${fn.name} used before declaration which breaks Forget's gating codegen due to hoisting`,
@@ -649,15 +655,3 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel(
655
656 return errors.details.length > 0 ? errors : null;
657 }
652 -
653 -function occursBefore(a: t.SourceLocation, b: t.SourceLocation): boolean {
654 - if (a.start.line > b.start.line) {
655 - return false;
656 - }
657 -
658 - if (a.start.line < b.start.line) {
659 - return true;
660 - }
661 -
662 - return a.start.column < b.start.column;
663 -}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/component-syntax-ref-gating.flow.expect.md deleted
-38
@@ -1,38 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @flow @gating
6 -component Foo(ref: React.RefSetter<Controls>) {
7 - return <Bar ref={ref}/>;
8 -}
9 -```
10 -
11 -## Code
12 -
13 -```javascript
14 -import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
15 -import { unstable_useMemoCache as useMemoCache } from "react";
16 -const Foo = React.forwardRef(Foo_withRef);
17 -const Foo_withRef = isForgetEnabled_Fixtures()
18 - ? function Foo_withRef(_$$empty_props_placeholder$$, ref) {
19 - const $ = useMemoCache(2);
20 - let t0;
21 - if ($[0] !== ref) {
22 - t0 = <Bar ref={ref} />;
23 - $[0] = ref;
24 - $[1] = t0;
25 - } else {
26 - t0 = $[1];
27 - }
28 - return t0;
29 - }
30 - : function Foo_withRef(
31 - _$$empty_props_placeholder$$: $ReadOnly<{ ... }>,
32 - ref: React.RefSetter<Controls>
33 - ) {
34 - return <Bar ref={ref} />;
35 - };
36 -
37 -```
38 -
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.component-syntax-ref-gating.flow.expect.md new
+18
@@ -0,0 +1,18 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @flow @gating
6 +component Foo(ref: React.RefSetter<Controls>) {
7 + return <Bar ref={ref}/>;
8 +}
9 +```
10 +
11 +
12 +## Error
13 +
14 +```
15 +[ReactForget] Invariant: Encountered Foo_withRef used before declaration which breaks Forget's gating codegen due to hoisting. Rewrite the reference to not use hoisting to fix this issue (2:2)
16 +```
17 +
18 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.component-syntax-ref-gating.flow.js renamed