[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.
This commit is contained in:
Sathya Gunasekaran
2023-11-28 14:18:24 +00:00
parent 42fa01ec87
commit 18d4913406
4 changed files with 31 additions and 57 deletions
@@ -618,7 +618,18 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel(
program.traverse({
Identifier(id) {
const fn = fnNames.get(id.node.name);
if (fnIds.has(id.node) || !fn) {
// We're not tracking this identifier.
if (!fn) {
return;
}
/*
* We've reached the declaration, hoisting is no longer possible, stop
* checking for this component name.
*/
if (fnIds.has(id.node)) {
fnIds.delete(id.node);
fnNames.delete(id.node.name);
return;
}
@@ -627,12 +638,7 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel(
* A null scope means there's no function scope, which means we're at the
* top level scope.
*/
if (
scope === null &&
id.node.loc &&
fn.loc &&
occursBefore(id.node.loc, fn.loc)
) {
if (scope === null) {
errors.pushErrorDetail(
new CompilerErrorDetail({
reason: `Encountered ${fn.name} used before declaration which breaks Forget's gating codegen due to hoisting`,
@@ -649,15 +655,3 @@ function checkFunctionReferencedBeforeDeclarationAtTopLevel(
return errors.details.length > 0 ? errors : null;
}
function occursBefore(a: t.SourceLocation, b: t.SourceLocation): boolean {
if (a.start.line > b.start.line) {
return false;
}
if (a.start.line < b.start.line) {
return true;
}
return a.start.column < b.start.column;
}
@@ -1,38 +0,0 @@
## Input
```javascript
// @flow @gating
component Foo(ref: React.RefSetter<Controls>) {
return <Bar ref={ref}/>;
}
```
## Code
```javascript
import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag";
import { unstable_useMemoCache as useMemoCache } from "react";
const Foo = React.forwardRef(Foo_withRef);
const Foo_withRef = isForgetEnabled_Fixtures()
? function Foo_withRef(_$$empty_props_placeholder$$, ref) {
const $ = useMemoCache(2);
let t0;
if ($[0] !== ref) {
t0 = <Bar ref={ref} />;
$[0] = ref;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
: function Foo_withRef(
_$$empty_props_placeholder$$: $ReadOnly<{ ... }>,
ref: React.RefSetter<Controls>
) {
return <Bar ref={ref} />;
};
```
@@ -0,0 +1,18 @@
## Input
```javascript
// @flow @gating
component Foo(ref: React.RefSetter<Controls>) {
return <Bar ref={ref}/>;
}
```
## Error
```
[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)
```