[compiler] Bail out on local variables named 'fbt'
ghstack-source-id: c4e2b802a029b9dd7941ebcdeaaf471ddd95f868 Pull Request resolved: https://github.com/facebook/react/pull/30524
Mofei Zhang committed
Jul 29, 2024 at 21:24 UTC
704c34e21f7468fc72c33aa843051c7949ebeccd
7 files changed
+76
-144
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+4
-3
@@ -2133,9 +2133,10 @@ function lowerExpression(
2133
const tagIdentifier = openingIdentifier.isJSXIdentifier()
2134
? builder.resolveIdentifier(openingIdentifier)
2135
: null;
2136
- if (tagIdentifier != null && tagIdentifier.kind === 'Identifier') {
2137
- CompilerError.throwTodo({
2138
- reason: `Support <${tagName}> tags where '${tagName}' is a local variable instead of a global`,
2136
+ if (tagIdentifier != null) {
2137
+ // This is already checked in builder.resolveIdentifier
2138
+ CompilerError.invariant(tagIdentifier.kind !== 'Identifier', {
2139
+ reason: `<${tagName}> tags should be module-level imports`,
2140
loc: openingIdentifier.node.loc ?? GeneratedSource,
2141
description: null,
2142
suggestions: null,
compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts
+6
@@ -311,6 +311,12 @@ export default class HIRBuilder {
311
}
312
313
resolveBinding(node: t.Identifier): Identifier {
314
+ if (node.name === 'fbt') {
315
+ CompilerError.throwTodo({
316
+ reason: 'Support local variables named "fbt"',
317
+ loc: node.loc ?? null,
318
+ });
319
+ }
320
const originalName = node.name;
321
let name = originalName;
322
let index = 0;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-fbt-as-local.expect.md
new
+62
@@ -0,0 +1,62 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import fbt from 'fbt';
6
+import {identity} from 'shared-runtime';
7
+
8
+/**
9
+ * Note that the fbt transform looks for callsites with a `fbt`-named callee.
10
+ * This is incompatible with react-compiler as we rename local variables in
11
+ * HIRBuilder + RenameVariables.
12
+ *
13
+ * See evaluator error:
14
+ * Found differences in evaluator results
15
+ * Non-forget (expected):
16
+ * (kind: ok) <div>Hello, Sathya!Goodbye, Sathya!</div>
17
+ * Forget:
18
+ * (kind: exception) fbt$0.param is not a function
19
+ */
20
+
21
+function Foo(props) {
22
+ const getText1 = fbt =>
23
+ fbt(
24
+ `Hello, ${fbt.param('(key) name', identity(props.name))}!`,
25
+ '(description) Greeting'
26
+ );
27
+
28
+ const getText2 = fbt =>
29
+ fbt(
30
+ `Goodbye, ${fbt.param('(key) name', identity(props.name))}!`,
31
+ '(description) Greeting2'
32
+ );
33
+
34
+ return (
35
+ <div>
36
+ {getText1(fbt)}
37
+ {getText2(fbt)}
38
+ </div>
39
+ );
40
+}
41
+
42
+export const FIXTURE_ENTRYPOINT = {
43
+ fn: Foo,
44
+ params: [{name: 'Sathya'}],
45
+};
46
+
47
+```
48
+
49
+
50
+## Error
51
+
52
+```
53
+ 16 |
54
+ 17 | function Foo(props) {
55
+> 18 | const getText1 = fbt =>
56
+ | ^^^ Todo: Support local variables named "fbt" (18:18)
57
+ 19 | fbt(
58
+ 20 | `Hello, ${fbt.param('(key) name', identity(props.name))}!`,
59
+ 21 | '(description) Greeting'
60
+```
61
+
62
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-fbt-as-local.js
renamed
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-locally-require-fbt.expect.md
+4
-4
@@ -14,12 +14,12 @@ function Component(props) {
14
## Error
15
16
```
17
- 2 | const fbt = require('fbt');
17
+ 1 | function Component(props) {
18
+> 2 | const fbt = require('fbt');
19
+ | ^^^ Todo: Support local variables named "fbt" (2:2)
20
3 |
19
-> 4 | return <fbt desc="Description">{'Text'}</fbt>;
20
- | ^^^ Todo: Support <fbt> tags where 'fbt' is a local variable instead of a global (4:4)
21
+ 4 | return <fbt desc="Description">{'Text'}</fbt>;
22
5 | }
22
- 6 |
23
```
24
25
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/todo-fbt-as-local.expect.md
deleted
-136
@@ -1,136 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-import fbt from 'fbt';
6
-import {identity} from 'shared-runtime';
7
-
8
-/**
9
- * Note that the fbt transform looks for callsites with a `fbt`-named callee.
10
- * This is incompatible with react-compiler as we rename local variables in
11
- * HIRBuilder + RenameVariables.
12
- *
13
- * See evaluator error:
14
- * Found differences in evaluator results
15
- * Non-forget (expected):
16
- * (kind: ok) <div>Hello, Sathya!Goodbye, Sathya!</div>
17
- * Forget:
18
- * (kind: exception) fbt$0.param is not a function
19
- */
20
-
21
-function Foo(props) {
22
- const getText1 = fbt =>
23
- fbt(
24
- `Hello, ${fbt.param('(key) name', identity(props.name))}!`,
25
- '(description) Greeting'
26
- );
27
-
28
- const getText2 = fbt =>
29
- fbt(
30
- `Goodbye, ${fbt.param('(key) name', identity(props.name))}!`,
31
- '(description) Greeting2'
32
- );
33
-
34
- return (
35
- <div>
36
- {getText1(fbt)}
37
- {getText2(fbt)}
38
- </div>
39
- );
40
-}
41
-
42
-export const FIXTURE_ENTRYPOINT = {
43
- fn: Foo,
44
- params: [{name: 'Sathya'}],
45
-};
46
-
47
-```
48
-
49
-## Code
50
-
51
-```javascript
52
-import { c as _c } from "react/compiler-runtime";
53
-import fbt from "fbt";
54
-import { identity } from "shared-runtime";
55
-
56
-/**
57
- * Note that the fbt transform looks for callsites with a `fbt`-named callee.
58
- * This is incompatible with react-compiler as we rename local variables in
59
- * HIRBuilder + RenameVariables.
60
- *
61
- * See evaluator error:
62
- * Found differences in evaluator results
63
- * Non-forget (expected):
64
- * (kind: ok) <div>Hello, Sathya!Goodbye, Sathya!</div>
65
- * Forget:
66
- * (kind: exception) fbt$0.param is not a function
67
- */
68
-
69
-function Foo(props) {
70
- const $ = _c(11);
71
- let t0;
72
- if ($[0] !== props.name) {
73
- t0 = (fbt$0) =>
74
- fbt$0(
75
- `Hello, ${fbt$0.param("(key) name", identity(props.name))}!`,
76
- "(description) Greeting",
77
- );
78
- $[0] = props.name;
79
- $[1] = t0;
80
- } else {
81
- t0 = $[1];
82
- }
83
- const getText1 = t0;
84
- let t1;
85
- if ($[2] !== props.name) {
86
- t1 = (fbt_0) =>
87
- fbt_0(
88
- `Goodbye, ${fbt_0.param("(key) name", identity(props.name))}!`,
89
- "(description) Greeting2",
90
- );
91
- $[2] = props.name;
92
- $[3] = t1;
93
- } else {
94
- t1 = $[3];
95
- }
96
- const getText2 = t1;
97
- let t2;
98
- if ($[4] !== getText1) {
99
- t2 = getText1(fbt);
100
- $[4] = getText1;
101
- $[5] = t2;
102
- } else {
103
- t2 = $[5];
104
- }
105
- let t3;
106
- if ($[6] !== getText2) {
107
- t3 = getText2(fbt);
108
- $[6] = getText2;
109
- $[7] = t3;
110
- } else {
111
- t3 = $[7];
112
- }
113
- let t4;
114
- if ($[8] !== t2 || $[9] !== t3) {
115
- t4 = (
116
- <div>
117
- {t2}
118
- {t3}
119
- </div>
120
- );
121
- $[8] = t2;
122
- $[9] = t3;
123
- $[10] = t4;
124
- } else {
125
- t4 = $[10];
126
- }
127
- return t4;
128
-}
129
-
130
-export const FIXTURE_ENTRYPOINT = {
131
- fn: Foo,
132
- params: [{ name: "Sathya" }],
133
-};
134
-
135
-```
136
-
\ No newline at end of file
compiler/packages/snap/src/SproutTodoFilter.ts
-1
@@ -484,7 +484,6 @@ const skipFilter = new Set([
484
'rules-of-hooks/rules-of-hooks-69521d94fa03',
485
486
// bugs
487
- 'fbt/todo-fbt-as-local',
487
'bug-invalid-hoisting-functionexpr',
488
'original-reactive-scopes-fork/bug-nonmutating-capture-in-unsplittable-memo-block',
489
'original-reactive-scopes-fork/bug-hoisted-declaration-with-scope',