@samitouri / QOS-React-2 / commits / aae9b01f91

Test cases for inferring mutability of function expressions (as values)

This PR contains just the test cases from #1717, showing some tricky cases around function expression mutability inference.

Joe Savona committed Jun 15, 2023 at 09:32 UTC aae9b01f91347e3417a20da861762ea0402d7cfb
8 files changed +150
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-function-expression-captures-value-later-frozen-jsx.expect.md new
+27
@@ -0,0 +1,27 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + let x = {};
7 + // onChange should be inferred as immutable, because the value
8 + // it captures (`x`) is frozen by the time the function is referenced
9 + const onChange = (e) => {
10 + maybeMutate(x, e.target.value);
11 + };
12 + if (props.cond) {
13 + <div>{x}</div>;
14 + }
15 + return <Foo value={x} onChange={onChange} />;
16 +}
17 +
18 +```
19 +
20 +
21 +## Error
22 +
23 +```
24 +[ReactForget] InvalidInput: Cannot use a mutable function where an immutable value is expected (11:11)
25 +```
26 +
27 +
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-function-expression-captures-value-later-frozen-jsx.js new
+12
@@ -0,0 +1,12 @@
1 +function Component(props) {
2 + let x = {};
3 + // onChange should be inferred as immutable, because the value
4 + // it captures (`x`) is frozen by the time the function is referenced
5 + const onChange = (e) => {
6 + maybeMutate(x, e.target.value);
7 + };
8 + if (props.cond) {
9 + <div>{x}</div>;
10 + }
11 + return <Foo value={x} onChange={onChange} />;
12 +}
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-function-expression-maybe-mutates-hook-return-value.expect.md new
+27
@@ -0,0 +1,27 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const id = useSelectedEntitytId();
7 + // this example should infer `id` as mutable, and then infer `onLoad` as mutable,
8 + // and be rejected because onLoad cannot be passed as a frozen value in the JSX.
9 + // however, we likely have to allow this example to work, because hook return
10 + // values are generally immutable in practice and are also widely referenced in
11 + // callbacks.
12 + const onLoad = () => {
13 + log(id);
14 + };
15 + return <Foo onLoad={onLoad} />;
16 +}
17 +
18 +```
19 +
20 +
21 +## Error
22 +
23 +```
24 +[ReactForget] InvalidInput: Cannot use a mutable function where an immutable value is expected (11:11)
25 +```
26 +
27 +
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-function-expression-maybe-mutates-hook-return-value.js new
+12
@@ -0,0 +1,12 @@
1 +function Component(props) {
2 + const id = useSelectedEntitytId();
3 + // this example should infer `id` as mutable, and then infer `onLoad` as mutable,
4 + // and be rejected because onLoad cannot be passed as a frozen value in the JSX.
5 + // however, we likely have to allow this example to work, because hook return
6 + // values are generally immutable in practice and are also widely referenced in
7 + // callbacks.
8 + const onLoad = () => {
9 + log(id);
10 + };
11 + return <Foo onLoad={onLoad} />;
12 +}
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo-function-expression-captures-value-later-frozen.expect.md new
+58
@@ -0,0 +1,58 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + let x = {};
7 + // onChange should be inferred as immutable, because the value
8 + // it captures (`x`) is frozen by the time the function is referenced
9 + const onChange = (e) => {
10 + maybeMutate(x, e.target.value);
11 + };
12 + if (props.cond) {
13 + <div>{x}</div>;
14 + }
15 + // ideally this call would be outside the memoization block for `x`
16 + onChange();
17 + return <Foo value={x} />;
18 +}
19 +
20 +```
21 +
22 +## Code
23 +
24 +```javascript
25 +import { unstable_useMemoCache as useMemoCache } from "react";
26 +function Component(props) {
27 + const $ = useMemoCache(4);
28 + const c_0 = $[0] !== props.cond;
29 + let x;
30 + if (c_0) {
31 + x = {};
32 +
33 + const onChange = (e) => {
34 + maybeMutate(x, e.target.value);
35 + };
36 + if (props.cond) {
37 + }
38 +
39 + onChange();
40 + $[0] = props.cond;
41 + $[1] = x;
42 + } else {
43 + x = $[1];
44 + }
45 + const c_2 = $[2] !== x;
46 + let t0;
47 + if (c_2) {
48 + t0 = <Foo value={x} />;
49 + $[2] = x;
50 + $[3] = t0;
51 + } else {
52 + t0 = $[3];
53 + }
54 + return t0;
55 +}
56 +
57 +```
58 +
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo-function-expression-captures-value-later-frozen.js new
+14
@@ -0,0 +1,14 @@
1 +function Component(props) {
2 + let x = {};
3 + // onChange should be inferred as immutable, because the value
4 + // it captures (`x`) is frozen by the time the function is referenced
5 + const onChange = (e) => {
6 + maybeMutate(x, e.target.value);
7 + };
8 + if (props.cond) {
9 + <div>{x}</div>;
10 + }
11 + // ideally this call would be outside the memoization block for `x`
12 + onChange();
13 + return <Foo value={x} />;
14 +}
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo-invalid-function-expression-mutates-immutable-value.expect.md renamed
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo-invalid-function-expression-mutates-immutable-value.js renamed