Detect hoisting where the reference is a reassignment
Our logic to detect hoisting relies on Babel's `isReferencedIdentifier()` to determine whether a reference to an identifier is a reference or a declaration. The idea is that we want to find references to variables that may be hoistable, before the declaration — the definition of hoisting. But due to the bug in isReferencedIdentifier, we skipped over reassignments of hoisted variables. The hack here checks if an identifier is a direct child of an AssignmentExpression, ensuring we visit reassignments.
Joe Savona committed
Mar 22, 2024 at 13:49 UTC
4575c8a5f6cb2bc006d586a1a0c025d677a62c99
3 files changed
+8
-3
compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts
+6
-1
@@ -366,7 +366,12 @@ function lowerStatement(
366
ArrowFunctionExpression: withFunctionContext,
367
ObjectMethod: withFunctionContext,
368
Identifier(id: NodePath<t.Identifier>) {
369
- if (!id.isReferencedIdentifier()) {
369
+ const id2 = id;
370
+ if (
371
+ !id2.isReferencedIdentifier() &&
372
+ // isReferencedIdentifier is broken and returns false for reassignments
373
+ id.parent.type !== "AssignmentExpression"
374
+ ) {
375
return;
376
}
377
const binding = id.scope.getBinding(id.node.name);
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md
+1
-1
@@ -20,7 +20,7 @@ function Component() {
20
1 | function Component() {
21
2 | let callback = () => {
22
> 3 | onClick = () => {};
23
- | ^^^^^^^ [ReactForget] Invariant: [hoisting] Expected value kind to be initialized. read onClick$0_@1 (3:3)
23
+ | ^^^^^^^^^^^^^^^^^^ [ReactForget] Todo: Handle non-const declarations for hoisting. variable "onClick" declared with let (3:3)
24
4 | };
25
5 | let onClick;
26
6 |
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-function-expression-references-variable-its-assigned-to.expect.md
+1
-1
@@ -18,7 +18,7 @@ function Component() {
18
1 | function Component() {
19
2 | let callback = () => {
20
> 3 | callback = null;
21
- | ^^^^^^^^ [ReactForget] Invariant: [hoisting] Expected value kind to be initialized. read callback$0_@0 (3:3)
21
+ | ^^^^^^^^^^^^^^^ [ReactForget] Todo: Handle non-const declarations for hoisting. variable "callback" declared with let (3:3)
22
4 | };
23
5 | return <div onClick={callback} />;
24
6 | }