@samitouri / QOS-React / commits / 5061f31f4b

compiler: distinguish globals/imports/module-locals

We currently use `LoadGlobal` and `StoreGlobal` to represent any read (or write) of a variable defined outside the component or hook that is being compiled. This is mostly fine, but for a lot of things we want to do going forward (resolving types across modules, for example) it helps to understand the actual source of a variable. This PR is an incremental step in that direction. We continue to use LoadGlobal/StoreGlobal, but LoadGlobal now has a `binding:NonLocalBinding` instead of just the name of the global. The NonLocalBinding type tells us whether it was an import (and which kind, the source module name etc), a module-local binding, or a true global. By keeping the LoadGlobal/StoreGlobal instructions, most code that deals with "anything not declared locally" doesn't have to care about the difference. However, code that _does_ want to know the source of the value can figure it out. ghstack-source-id: e701d4ebc0fb5681a0197198ac2c2a03b3e8aae9 Pull Request resolved: https://github.com/facebook/react/pull/29188

Joe Savona committed May 24, 2024 at 06:59 UTC 5061f31f4be6626aa328ea98cb528b0bd2678655
15 files changed +285 -65
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+42 -38
@@ -99,8 +99,8 @@ export function lower(
99 const params: Array<Place | SpreadPattern> = [];
100 func.get("params").forEach((param) => {
101 if (param.isIdentifier()) {
102 - const identifier = builder.resolveIdentifier(param);
103 - if (identifier === null) {
102 + const binding = builder.resolveIdentifier(param);
103 + if (binding.kind !== "Identifier") {
104 builder.errors.push({
105 reason: `(BuildHIR::lower) Could not find binding for param \`${param.node.name}\``,
106 severity: ErrorSeverity.Invariant,
@@ -111,7 +111,7 @@ export function lower(
111 }
112 const place: Place = {
113 kind: "Identifier",
114 - identifier,
114 + identifier: binding.identifier,
115 effect: Effect.Unknown,
116 reactive: false,
117 loc: param.node.loc ?? GeneratedSource,
@@ -449,10 +449,15 @@ function lowerStatement(
449 });
450 continue;
451 }
452 - const identifier = builder.resolveIdentifier(id)!;
452 + const identifier = builder.resolveIdentifier(id);
453 + CompilerError.invariant(identifier.kind === "Identifier", {
454 + reason:
455 + "Expected hoisted binding to be a local identifier, not a global",
456 + loc: id.node.loc ?? GeneratedSource,
457 + });
458 const place: Place = {
459 effect: Effect.Unknown,
455 - identifier,
460 + identifier: identifier.identifier,
461 kind: "Identifier",
462 reactive: false,
463 loc: id.node.loc ?? GeneratedSource,
@@ -836,8 +841,8 @@ function lowerStatement(
841 : "Assignment"
842 );
843 } else if (id.isIdentifier()) {
839 - const identifier = builder.resolveIdentifier(id);
840 - if (identifier == null) {
844 + const binding = builder.resolveIdentifier(id);
845 + if (binding.kind !== "Identifier") {
846 builder.errors.push({
847 reason: `(BuildHIR::lowerAssignment) Could not find binding for declaration.`,
848 severity: ErrorSeverity.Invariant,
@@ -847,7 +852,7 @@ function lowerStatement(
852 } else {
853 const place: Place = {
854 effect: Effect.Unknown,
850 - identifier,
855 + identifier: binding.identifier,
856 kind: "Identifier",
857 reactive: false,
858 loc: id.node.loc ?? GeneratedSource,
@@ -2094,7 +2099,7 @@ function lowerExpression(
2099 const tagIdentifier = openingIdentifier.isJSXIdentifier()
2100 ? builder.resolveIdentifier(openingIdentifier)
2101 : null;
2097 - if (tagIdentifier != null) {
2102 + if (tagIdentifier != null && tagIdentifier.kind === "Identifier") {
2103 CompilerError.throwTodo({
2104 reason: `Support <${tagName}> tags where '${tagName}' is a local variable instead of a global`,
2105 loc: openingIdentifier.node.loc ?? GeneratedSource,
@@ -2722,14 +2727,12 @@ function isReorderableExpression(
2727 ): boolean {
2728 switch (expr.node.type) {
2729 case "Identifier": {
2725 - const identifier = builder.resolveIdentifier(
2726 - expr as NodePath<t.Identifier>
2727 - );
2728 - if (identifier === null) {
2730 + const binding = builder.resolveIdentifier(expr as NodePath<t.Identifier>);
2731 + if (binding.kind === "Identifier") {
2732 + return allowLocalIdentifiers;
2733 + } else {
2734 // global, definitely safe
2735 return true;
2731 - } else {
2732 - return allowLocalIdentifiers;
2736 }
2737 }
2738 case "RegExpLiteral":
@@ -2821,7 +2824,7 @@ function isReorderableExpression(
2824 }
2825 if (
2826 innerObject.isIdentifier() &&
2824 - builder.resolveIdentifier(innerObject) === null // null means global
2827 + builder.resolveIdentifier(innerObject).kind !== "Identifier"
2828 ) {
2829 // This is a property/computed load from a global, that's safe to reorder
2830 return true;
@@ -3263,25 +3266,26 @@ function lowerIdentifier(
3266 ): Place {
3267 const exprNode = exprPath.node;
3268 const exprLoc = exprNode.loc ?? GeneratedSource;
3266 - const identifier = builder.resolveIdentifier(exprPath);
3267 - if (identifier === null) {
3268 - const global = builder.resolveGlobal(exprPath);
3269 - let value: InstructionValue;
3270 - if (global !== null) {
3271 - value = { kind: "LoadGlobal", name: global.name, loc: exprLoc };
3272 - } else {
3273 - value = { kind: "UnsupportedNode", node: exprPath.node, loc: exprLoc };
3269 + const binding = builder.resolveIdentifier(exprPath);
3270 + switch (binding.kind) {
3271 + case "Identifier": {
3272 + const place: Place = {
3273 + kind: "Identifier",
3274 + identifier: binding.identifier,
3275 + effect: Effect.Unknown,
3276 + reactive: false,
3277 + loc: exprLoc,
3278 + };
3279 + return place;
3280 + }
3281 + default: {
3282 + return lowerValueToTemporary(builder, {
3283 + kind: "LoadGlobal",
3284 + binding,
3285 + loc: exprLoc,
3286 + });
3287 }
3275 - return lowerValueToTemporary(builder, value);
3288 }
3277 - const place: Place = {
3278 - kind: "Identifier",
3279 - identifier: identifier,
3280 - effect: Effect.Unknown,
3281 - reactive: false,
3282 - loc: exprLoc,
3283 - };
3284 - return place;
3289 }
3290
3291 // Creates a temporary Identifier and Place referencing that identifier.
@@ -3318,8 +3322,8 @@ function lowerIdentifierForAssignment(
3322 kind: InstructionKind,
3323 path: NodePath<t.Identifier>
3324 ): Place | { kind: "Global"; name: string } | null {
3321 - const identifier = builder.resolveIdentifier(path);
3322 - if (identifier == null) {
3325 + const binding = builder.resolveIdentifier(path);
3326 + if (binding.kind !== "Identifier") {
3327 if (kind === InstructionKind.Reassign) {
3328 return { kind: "Global", name: path.node.name };
3329 } else {
@@ -3336,7 +3340,7 @@ function lowerIdentifierForAssignment(
3340
3341 const place: Place = {
3342 kind: "Identifier",
3339 - identifier: identifier,
3343 + identifier: binding.identifier,
3344 effect: Effect.Unknown,
3345 reactive: false,
3346 loc,
@@ -3496,7 +3500,7 @@ function lowerAssignment(
3500 (element) =>
3501 element.isIdentifier() &&
3502 (getStoreKind(builder, element) !== "StoreLocal" ||
3499 - builder.resolveIdentifier(element) == null)
3503 + builder.resolveIdentifier(element).kind !== "Identifier")
3504 ));
3505 for (let i = 0; i < elements.length; i++) {
3506 const element = elements[i];
@@ -3627,7 +3631,7 @@ function lowerAssignment(
3631 (!property.get("value").isIdentifier() ||
3632 builder.resolveIdentifier(
3633 property.get("value") as NodePath<t.Identifier>
3630 - ) == null))
3634 + ).kind !== "Identifier"))
3635 );
3636 for (let i = 0; i < propertiesPaths.length; i++) {
3637 const property = propertiesPaths[i];
compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
+24 -1
@@ -1075,7 +1075,7 @@ export type PropertyLoad = {
1075
1076 export type LoadGlobal = {
1077 kind: "LoadGlobal";
1078 - name: string;
1078 + binding: NonLocalBinding;
1079 loc: SourceLocation;
1080 };
1081
@@ -1103,6 +1103,29 @@ export type MutableRange = {
1103 end: InstructionId;
1104 };
1105
1106 +export type VariableBinding =
1107 + // let, const, etc declared within the current component/hook
1108 + | { kind: "Identifier"; identifier: Identifier }
1109 + // bindings declard outside the current component/hook
1110 + | NonLocalBinding;
1111 +
1112 +export type NonLocalBinding =
1113 + // `import Foo from 'foo'`: name=Foo, module=foo
1114 + | { kind: "ImportDefault"; name: string; module: string }
1115 + // `import * as Foo from 'foo'`: name=Foo, module=foo
1116 + | { kind: "ImportNamespace"; name: string; module: string }
1117 + // `import {bar as baz} from 'foo'`: name=baz, module=foo, imported=bar
1118 + | {
1119 + kind: "ImportSpecifier";
1120 + name: string;
1121 + module: string;
1122 + imported: string;
1123 + }
1124 + // let, const, function, etc declared in the module but outside the current component/hook
1125 + | { kind: "ModuleLocal"; name: string }
1126 + // an unresolved binding
1127 + | { kind: "Global"; name: string };
1128 +
1129 // Represents a user-defined variable (has a name) or a temporary variable (no name).
1130 export type Identifier = {
1131 /*
compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts
+53 -9
@@ -23,6 +23,7 @@ import {
23 Instruction,
24 Place,
25 Terminal,
26 + VariableBinding,
27 makeBlockId,
28 makeIdentifierName,
29 makeInstructionId,
@@ -212,12 +213,6 @@ export default class HIRBuilder {
213 if (binding == null) {
214 return null;
215 }
215 - // Check if the binding is from module scope, if so return null
216 - const outerBinding =
217 - this.parentFunction.scope.parent.getBinding(originalName);
218 - if (binding === outerBinding) {
219 - return null;
220 - }
216 return binding;
217 }
218
@@ -253,22 +248,71 @@ export default class HIRBuilder {
248 */
249 resolveIdentifier(
250 path: NodePath<t.Identifier | t.JSXIdentifier>
256 - ): Identifier | null {
251 + ): VariableBinding {
252 const originalName = path.node.name;
253 const babelBinding = this.#resolveBabelBinding(path);
254 if (babelBinding == null) {
260 - return null;
255 + return { kind: "Global", name: originalName };
256 + }
257 +
258 + // Check if the binding is from module scope
259 + const outerBinding =
260 + this.parentFunction.scope.parent.getBinding(originalName);
261 + if (babelBinding === outerBinding) {
262 + const path = babelBinding.path;
263 + if (path.isImportDefaultSpecifier()) {
264 + const importDeclaration =
265 + path.parentPath as NodePath<t.ImportDeclaration>;
266 + return {
267 + kind: "ImportDefault",
268 + name: originalName,
269 + module: importDeclaration.node.source.value,
270 + };
271 + } else if (path.isImportSpecifier()) {
272 + const importDeclaration =
273 + path.parentPath as NodePath<t.ImportDeclaration>;
274 + return {
275 + kind: "ImportSpecifier",
276 + name: originalName,
277 + module: importDeclaration.node.source.value,
278 + imported:
279 + path.node.imported.type === "Identifier"
280 + ? path.node.imported.name
281 + : path.node.imported.value,
282 + };
283 + } else if (path.isImportNamespaceSpecifier()) {
284 + const importDeclaration =
285 + path.parentPath as NodePath<t.ImportDeclaration>;
286 + return {
287 + kind: "ImportNamespace",
288 + name: originalName,
289 + module: importDeclaration.node.source.value,
290 + };
291 + } else {
292 + return {
293 + kind: "ModuleLocal",
294 + name: originalName,
295 + };
296 + }
297 }
298 +
299 const resolvedBinding = this.resolveBinding(babelBinding.identifier);
300 if (resolvedBinding.name && resolvedBinding.name.value !== originalName) {
301 babelBinding.scope.rename(originalName, resolvedBinding.name.value);
302 }
266 - return resolvedBinding;
303 + return { kind: "Identifier", identifier: resolvedBinding };
304 }
305
306 isContextIdentifier(path: NodePath<t.Identifier | t.JSXIdentifier>): boolean {
307 const binding = this.#resolveBabelBinding(path);
308 if (binding) {
309 + // Check if the binding is from module scope, if so return null
310 + const outerBinding = this.parentFunction.scope.parent.getBinding(
311 + path.node.name
312 + );
313 + if (binding === outerBinding) {
314 + return false;
315 + }
316 return this.#env.isContextIdentifier(binding.identifier);
317 } else {
318 return false;
compiler/packages/babel-plugin-react-compiler/src/HIR/PrintHIR.ts
+1 -1
@@ -588,7 +588,7 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
588 break;
589 }
590 case "LoadGlobal": {
591 - value = `LoadGlobal ${instrValue.name}`;
591 + value = `LoadGlobal ${instrValue.binding.name}`;
592 break;
593 }
594 case "StoreGlobal": {
compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts
+3 -3
@@ -58,7 +58,7 @@ export function collectMaybeMemoDependencies(
58 return {
59 root: {
60 kind: "Global",
61 - identifierName: value.name,
61 + identifierName: value.binding.name,
62 },
63 path: [],
64 };
@@ -127,7 +127,7 @@ function collectTemporaries(
127 break;
128 }
129 case "LoadGlobal": {
130 - const global = env.getGlobalDeclaration(value.name);
130 + const global = env.getGlobalDeclaration(value.binding.name);
131 const hookKind = global !== null ? getHookKindForType(env, global) : null;
132 const lvalId = instr.lvalue.identifier.id;
133 if (hookKind === "useMemo" || hookKind === "useCallback") {
@@ -135,7 +135,7 @@ function collectTemporaries(
135 kind: hookKind,
136 loadInstr: instr as TInstruction<LoadGlobal>,
137 });
138 - } else if (value.name === "React") {
138 + } else if (value.binding.name === "React") {
139 sidemap.react.add(lvalId);
140 }
141 break;
compiler/packages/babel-plugin-react-compiler/src/Optimization/ConstantPropagation.ts
+1 -1
@@ -209,7 +209,7 @@ function evaluatePhi(phi: Phi, constants: Constants): Constant | null {
209 });
210
211 // different global values, can't constant propogate
212 - if (operandValue.name !== value.name) {
212 + if (operandValue.binding.name !== value.binding.name) {
213 return null;
214 }
215 break;
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+1 -1
@@ -1928,7 +1928,7 @@ function codegenInstructionValue(
1928 break;
1929 }
1930 case "LoadGlobal": {
1931 - value = t.identifier(instrValue.name);
1931 + value = t.identifier(instrValue.binding.name);
1932 break;
1933 }
1934 case "RegExpLiteral": {
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CollectReferencedGlobals.ts
+1 -1
@@ -28,7 +28,7 @@ class Visitor extends ReactiveFunctionVisitor<Set<string>> {
28 if (value.kind === "FunctionExpression" || value.kind === "ObjectMethod") {
29 this.visitHirFunction(value.loweredFunc.func, state);
30 } else if (value.kind === "LoadGlobal") {
31 - state.add(value.name);
31 + state.add(value.binding.name);
32 }
33 }
34
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/MemoizeFbtOperandsInSameScope.ts
+4 -1
@@ -67,7 +67,10 @@ function visit(fn: HIRFunction, fbtValues: Set<IdentifierId>): void {
67 * all `fbt` string literals in case they are used as a jsx tag.
68 */
69 fbtValues.add(lvalue.identifier.id);
70 - } else if (value.kind === "LoadGlobal" && FBT_TAGS.has(value.name)) {
70 + } else if (
71 + value.kind === "LoadGlobal" &&
72 + FBT_TAGS.has(value.binding.name)
73 + ) {
74 // Record references to `fbt` as a global
75 fbtValues.add(lvalue.identifier.id);
76 } else if (isFbtCallExpression(fbtValues, value)) {
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PropagateEarlyReturns.ts
+4 -1
@@ -166,7 +166,10 @@ class Transform extends ReactiveFunctionTransform<State> {
166 lvalue: { ...symbolTemp },
167 value: {
168 kind: "LoadGlobal",
169 - name: "Symbol",
169 + binding: {
170 + kind: "Global",
171 + name: "Symbol",
172 + },
173 loc,
174 },
175 },
compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts
+1 -1
@@ -199,7 +199,7 @@ function* generateInstructionTypes(
199 }
200
201 case "LoadGlobal": {
202 - const globalType = env.getGlobalDeclaration(value.name);
202 + const globalType = env.getGlobalDeclaration(value.binding.name);
203 if (globalType) {
204 yield equation(left, globalType);
205 }
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts
+5 -5
@@ -35,13 +35,13 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
35 switch (value.kind) {
36 case "LoadGlobal": {
37 if (
38 - value.name != "" &&
39 - /^[A-Z]/.test(value.name) &&
38 + value.binding.name != "" &&
39 + /^[A-Z]/.test(value.binding.name) &&
40 // We don't want to flag CONSTANTS()
41 - !(value.name.toUpperCase() === value.name) &&
42 - !isAllowed(value.name)
41 + !(value.binding.name.toUpperCase() === value.binding.name) &&
42 + !isAllowed(value.binding.name)
43 ) {
44 - capitalLoadGlobals.set(lvalue.identifier.id, value.name);
44 + capitalLoadGlobals.set(lvalue.identifier.id, value.binding.name);
45 }
46
47 break;
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts
+2 -2
@@ -16,9 +16,9 @@ export function validateUseMemo(fn: HIRFunction): void {
16 for (const { lvalue, value } of block.instructions) {
17 switch (value.kind) {
18 case "LoadGlobal": {
19 - if (value.name === "useMemo") {
19 + if (value.binding.name === "useMemo") {
20 useMemos.add(lvalue.identifier.id);
21 - } else if (value.name === "React") {
21 + } else if (value.binding.name === "React") {
22 react.add(lvalue.identifier.id);
23 }
24 break;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/module-scoped-bindings.expect.md new
+104
@@ -0,0 +1,104 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import React from "react";
6 +import { useState } from "react";
7 +
8 +const CONST = true;
9 +
10 +let NON_REASSIGNED_LET = true;
11 +
12 +let REASSIGNED_LET = false;
13 +REASSIGNED_LET = true;
14 +
15 +function reassignedFunction() {}
16 +reassignedFunction = true;
17 +
18 +function nonReassignedFunction() {}
19 +
20 +class ReassignedClass {}
21 +ReassignedClass = true;
22 +
23 +class NonReassignedClass {}
24 +
25 +function Component() {
26 + const [state] = useState(null);
27 + return [
28 + React,
29 + state,
30 + CONST,
31 + NON_REASSIGNED_LET,
32 + REASSIGNED_LET,
33 + reassignedFunction,
34 + nonReassignedFunction,
35 + ReassignedClass,
36 + NonReassignedClass,
37 + ];
38 +}
39 +
40 +export const FIXTURE_ENTRYPOINT = {
41 + fn: Component,
42 + params: [{}],
43 +};
44 +
45 +```
46 +
47 +## Code
48 +
49 +```javascript
50 +import { c as _c } from "react/compiler-runtime";
51 +import React from "react";
52 +import { useState } from "react";
53 +
54 +const CONST = true;
55 +
56 +let NON_REASSIGNED_LET = true;
57 +
58 +let REASSIGNED_LET = false;
59 +REASSIGNED_LET = true;
60 +
61 +function reassignedFunction() {}
62 +reassignedFunction = true;
63 +
64 +function nonReassignedFunction() {}
65 +
66 +class ReassignedClass {}
67 +ReassignedClass = true;
68 +
69 +class NonReassignedClass {}
70 +
71 +function Component() {
72 + const $ = _c(2);
73 + const [state] = useState(null);
74 + let t0;
75 + if ($[0] !== state) {
76 + t0 = [
77 + React,
78 +
79 + state,
80 + CONST,
81 + NON_REASSIGNED_LET,
82 + REASSIGNED_LET,
83 + reassignedFunction,
84 + nonReassignedFunction,
85 + ReassignedClass,
86 + NonReassignedClass,
87 + ];
88 + $[0] = state;
89 + $[1] = t0;
90 + } else {
91 + t0 = $[1];
92 + }
93 + return t0;
94 +}
95 +
96 +export const FIXTURE_ENTRYPOINT = {
97 + fn: Component,
98 + params: [{}],
99 +};
100 +
101 +```
102 +
103 +### Eval output
104 +(kind: ok) [{"Children":{"map":"[[ function params=3 ]]","forEach":"[[ function params=3 ]]","count":"[[ function params=1 ]]","toArray":"[[ function params=1 ]]","only":"[[ function params=1 ]]"},"Component":"[[ function params=3 ]]","PureComponent":"[[ function params=3 ]]","__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE":{"H":{"readContext":"[[ function params=1 ]]","use":"[[ function params=1 ]]","useCallback":"[[ function params=2 ]]","useContext":"[[ function params=1 ]]","useEffect":"[[ function params=2 ]]","useImperativeHandle":"[[ function params=3 ]]","useInsertionEffect":"[[ function params=2 ]]","useLayoutEffect":"[[ function params=2 ]]","useMemo":"[[ function params=2 ]]","useReducer":"[[ function params=3 ]]","useRef":"[[ function params=1 ]]","useState":"[[ function params=1 ]]","useDebugValue":"[[ function params=2 ]]","useDeferredValue":"[[ function params=2 ]]","useTransition":"[[ function params=0 ]]","useSyncExternalStore":"[[ function params=3 ]]","useId":"[[ function params=0 ]]","useCacheRefresh":"[[ function params=0 ]]","useMemoCache":"[[ function params=1 ]]","useHostTransitionStatus":"[[ function params=0 ]]","useFormState":"[[ function params=3 ]]","useActionState":"[[ function params=3 ]]","useOptimistic":"[[ function params=2 ]]"},"A":{"getCacheForType":"[[ function params=1 ]]","getOwner":"[[ function params=0 ]]"},"T":null,"actQueue":["[[ function params=0 ]]","[[ function params=1 ]]"],"isBatchingLegacy":false,"didScheduleLegacyUpdate":false,"didUsePromise":false,"thrownErrors":[],"setExtraStackFrame":"[[ function params=1 ]]","getCurrentStack":"[[ function params=0 ]]","getStackAddendum":"[[ function params=0 ]]"},"act":"[[ function params=1 ]]","cache":"[[ function params=1 ]]","cloneElement":"[[ function params=3 ]]","createContext":"[[ function params=1 ]]","createElement":"[[ function params=3 ]]","createRef":"[[ function params=0 ]]","forwardRef":"[[ function params=1 ]]","isValidElement":"[[ function params=1 ]]","lazy":"[[ function params=1 ]]","memo":"[[ function params=2 ]]","startTransition":"[[ function params=2 ]]","unstable_useCacheRefresh":"[[ function params=0 ]]","use":"[[ function params=1 ]]","useActionState":"[[ function params=3 ]]","useCallback":"[[ function params=2 ]]","useContext":"[[ function params=1 ]]","useDebugValue":"[[ function params=2 ]]","useDeferredValue":"[[ function params=2 ]]","useEffect":"[[ function params=2 ]]","useId":"[[ function params=0 ]]","useImperativeHandle":"[[ function params=3 ]]","useInsertionEffect":"[[ function params=2 ]]","useLayoutEffect":"[[ function params=2 ]]","useMemo":"[[ function params=2 ]]","useOptimistic":"[[ function params=2 ]]","useReducer":"[[ function params=3 ]]","useRef":"[[ function params=1 ]]","useState":"[[ function params=1 ]]","useSyncExternalStore":"[[ function params=3 ]]","useTransition":"[[ function params=0 ]]","version":"19.0.0-beta-b498834eab-20240506","c":"[[ function params=1 ]]"},"[[ cyclic ref *6 ]]",true,true,true,true,"[[ function params=0 ]]",true,"[[ function params=0 ]]"]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/module-scoped-bindings.js new
+39
@@ -0,0 +1,39 @@
1 +import React from "react";
2 +import { useState } from "react";
3 +
4 +const CONST = true;
5 +
6 +let NON_REASSIGNED_LET = true;
7 +
8 +let REASSIGNED_LET = false;
9 +REASSIGNED_LET = true;
10 +
11 +function reassignedFunction() {}
12 +reassignedFunction = true;
13 +
14 +function nonReassignedFunction() {}
15 +
16 +class ReassignedClass {}
17 +ReassignedClass = true;
18 +
19 +class NonReassignedClass {}
20 +
21 +function Component() {
22 + const [state] = useState(null);
23 + return [
24 + React,
25 + state,
26 + CONST,
27 + NON_REASSIGNED_LET,
28 + REASSIGNED_LET,
29 + reassignedFunction,
30 + nonReassignedFunction,
31 + ReassignedClass,
32 + NonReassignedClass,
33 + ];
34 +}
35 +
36 +export const FIXTURE_ENTRYPOINT = {
37 + fn: Component,
38 + params: [{}],
39 +};