Infer type of ref argument
Extend type inference to infer second argument of component as a ref type
Sathya Gunasekaran committed
Feb 29, 2024 at 14:47 UTC
7f994241b0ad54436b024c7097112e15b7cf0f26
5 files changed
+60
-64
compiler/packages/babel-plugin-react-forget/src/TypeInference/InferTypes.ts
+15
-1
@@ -18,7 +18,11 @@ import {
18
TypeId,
19
TypeVar,
20
} from "../HIR/HIR";
21
-import { BuiltInArrayId, BuiltInObjectId } from "../HIR/ObjectShape";
21
+import {
22
+ BuiltInArrayId,
23
+ BuiltInObjectId,
24
+ BuiltInUseRefId,
25
+} from "../HIR/ObjectShape";
26
import { eachInstructionLValue, eachInstructionOperand } from "../HIR/visitors";
27
import { assertExhaustive } from "../Utils/utils";
28
@@ -94,6 +98,16 @@ function equation(left: Type, right: Type): TypeEquation {
98
function* generate(
99
func: HIRFunction
100
): Generator<TypeEquation, void, undefined> {
101
+ if (func.env.fnType === "Component") {
102
+ const [_, ref] = func.params;
103
+ if (ref && ref.kind === "Identifier") {
104
+ yield equation(ref.identifier.type, {
105
+ kind: "Object",
106
+ shapeId: BuiltInUseRefId,
107
+ });
108
+ }
109
+ }
110
+
111
for (const [_, block] of func.body.blocks) {
112
for (const phi of block.phis) {
113
yield equation(phi.type, {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.validate-mutate-ref-arg-in-render.expect.md
new
+32
@@ -0,0 +1,32 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @validateRefAccessDuringRender:true
6
+function Foo(props, ref) {
7
+ console.log(ref.current);
8
+ return <div>{props.bar}</div>;
9
+}
10
+
11
+export const FIXTURE_ENTRYPOINT = {
12
+ fn: Foo,
13
+ params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }],
14
+ isComponent: true,
15
+};
16
+
17
+```
18
+
19
+
20
+## Error
21
+
22
+```
23
+ 1 | // @validateRefAccessDuringRender:true
24
+ 2 | function Foo(props, ref) {
25
+> 3 | console.log(ref.current);
26
+ | ^^^^^^^^^^^ [ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at read $16:TObject<BuiltInRefValue> (3:3)
27
+ 4 | return <div>{props.bar}</div>;
28
+ 5 | }
29
+ 6 |
30
+```
31
+
32
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.validate-mutate-ref-arg-in-render.js
renamed
+2
-2
@@ -1,6 +1,6 @@
1
-// @validateRefAccessDuringRender: true
1
+// @validateRefAccessDuringRender:true
2
function Foo(props, ref) {
3
- ref.current = 2;
3
+ console.log(ref.current);
4
return <div>{props.bar}</div>;
5
}
6
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ref-parameter-mutate-in-effect.expect.md
+11
-15
@@ -25,32 +25,28 @@ export const FIXTURE_ENTRYPOINT = {
25
import { useEffect, unstable_useMemoCache as useMemoCache } from "react";
26
27
function Foo(props, ref) {
28
- const $ = useMemoCache(5);
28
+ const $ = useMemoCache(4);
29
let t0;
30
- if ($[0] !== ref.current) {
30
+ let t1;
31
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32
t0 = () => {
33
ref.current = 2;
34
};
34
- $[0] = ref.current;
35
- $[1] = t0;
36
- } else {
37
- t0 = $[1];
38
- }
39
- let t1;
40
- if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
35
t1 = [];
42
- $[2] = t1;
36
+ $[0] = t0;
37
+ $[1] = t1;
38
} else {
44
- t1 = $[2];
39
+ t0 = $[0];
40
+ t1 = $[1];
41
}
42
useEffect(t0, t1);
43
let t2;
48
- if ($[3] !== props.bar) {
44
+ if ($[2] !== props.bar) {
45
t2 = <div>{props.bar}</div>;
50
- $[3] = props.bar;
51
- $[4] = t2;
46
+ $[2] = props.bar;
47
+ $[3] = t2;
48
} else {
53
- t2 = $[4];
49
+ t2 = $[3];
50
}
51
return t2;
52
}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo.validate-mutate-ref-arg-in-render.expect.md
deleted
-46
@@ -1,46 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @validateRefAccessDuringRender: true
6
-function Foo(props, ref) {
7
- ref.current = 2;
8
- return <div>{props.bar}</div>;
9
-}
10
-
11
-export const FIXTURE_ENTRYPOINT = {
12
- fn: Foo,
13
- params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }],
14
- isComponent: true,
15
-};
16
-
17
-```
18
-
19
-## Code
20
-
21
-```javascript
22
-import { unstable_useMemoCache as useMemoCache } from "react"; // @validateRefAccessDuringRender: true
23
-function Foo(props, ref) {
24
- const $ = useMemoCache(2);
25
- ref.current = 2;
26
- let t0;
27
- if ($[0] !== props.bar) {
28
- t0 = <div>{props.bar}</div>;
29
- $[0] = props.bar;
30
- $[1] = t0;
31
- } else {
32
- t0 = $[1];
33
- }
34
- return t0;
35
-}
36
-
37
-export const FIXTURE_ENTRYPOINT = {
38
- fn: Foo,
39
- params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }],
40
- isComponent: true,
41
-};
42
-
43
-```
44
-
45
-### Eval output
46
-(kind: ok) <div>foo</div>
\ No newline at end of file