@samitouri / QOS-React-2 / commits / 4575c8a5f6

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 | }