diff --git a/compiler/packages/babel-plugin-react-forget/src/TypeInference/InferTypes.ts b/compiler/packages/babel-plugin-react-forget/src/TypeInference/InferTypes.ts index 2fe2afeb69..3576fd6895 100644 --- a/compiler/packages/babel-plugin-react-forget/src/TypeInference/InferTypes.ts +++ b/compiler/packages/babel-plugin-react-forget/src/TypeInference/InferTypes.ts @@ -18,7 +18,11 @@ import { TypeId, TypeVar, } from "../HIR/HIR"; -import { BuiltInArrayId, BuiltInObjectId } from "../HIR/ObjectShape"; +import { + BuiltInArrayId, + BuiltInObjectId, + BuiltInUseRefId, +} from "../HIR/ObjectShape"; import { eachInstructionLValue, eachInstructionOperand } from "../HIR/visitors"; import { assertExhaustive } from "../Utils/utils"; @@ -94,6 +98,16 @@ function equation(left: Type, right: Type): TypeEquation { function* generate( func: HIRFunction ): Generator { + if (func.env.fnType === "Component") { + const [_, ref] = func.params; + if (ref && ref.kind === "Identifier") { + yield equation(ref.identifier.type, { + kind: "Object", + shapeId: BuiltInUseRefId, + }); + } + } + for (const [_, block] of func.body.blocks) { for (const phi of block.phis) { yield equation(phi.type, { diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.validate-mutate-ref-arg-in-render.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.validate-mutate-ref-arg-in-render.expect.md new file mode 100644 index 0000000000..8a9cb6dd11 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.validate-mutate-ref-arg-in-render.expect.md @@ -0,0 +1,32 @@ + +## Input + +```javascript +// @validateRefAccessDuringRender:true +function Foo(props, ref) { + console.log(ref.current); + return
{props.bar}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }], + isComponent: true, +}; + +``` + + +## Error + +``` + 1 | // @validateRefAccessDuringRender:true + 2 | function Foo(props, ref) { +> 3 | console.log(ref.current); + | ^^^^^^^^^^^ [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 (3:3) + 4 | return
{props.bar}
; + 5 | } + 6 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo.validate-mutate-ref-arg-in-render.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.validate-mutate-ref-arg-in-render.js similarity index 73% rename from compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo.validate-mutate-ref-arg-in-render.js rename to compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.validate-mutate-ref-arg-in-render.js index 6163a7b007..6e21199bb5 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo.validate-mutate-ref-arg-in-render.js +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.validate-mutate-ref-arg-in-render.js @@ -1,6 +1,6 @@ -// @validateRefAccessDuringRender: true +// @validateRefAccessDuringRender:true function Foo(props, ref) { - ref.current = 2; + console.log(ref.current); return
{props.bar}
; } diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ref-parameter-mutate-in-effect.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ref-parameter-mutate-in-effect.expect.md index a3822fea87..8ecf3a6065 100644 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ref-parameter-mutate-in-effect.expect.md +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ref-parameter-mutate-in-effect.expect.md @@ -25,32 +25,28 @@ export const FIXTURE_ENTRYPOINT = { import { useEffect, unstable_useMemoCache as useMemoCache } from "react"; function Foo(props, ref) { - const $ = useMemoCache(5); + const $ = useMemoCache(4); let t0; - if ($[0] !== ref.current) { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = () => { ref.current = 2; }; - $[0] = ref.current; - $[1] = t0; - } else { - t0 = $[1]; - } - let t1; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { t1 = []; - $[2] = t1; + $[0] = t0; + $[1] = t1; } else { - t1 = $[2]; + t0 = $[0]; + t1 = $[1]; } useEffect(t0, t1); let t2; - if ($[3] !== props.bar) { + if ($[2] !== props.bar) { t2 =
{props.bar}
; - $[3] = props.bar; - $[4] = t2; + $[2] = props.bar; + $[3] = t2; } else { - t2 = $[4]; + t2 = $[3]; } return t2; } diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo.validate-mutate-ref-arg-in-render.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo.validate-mutate-ref-arg-in-render.expect.md deleted file mode 100644 index 8416b01394..0000000000 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo.validate-mutate-ref-arg-in-render.expect.md +++ /dev/null @@ -1,46 +0,0 @@ - -## Input - -```javascript -// @validateRefAccessDuringRender: true -function Foo(props, ref) { - ref.current = 2; - return
{props.bar}
; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }], - isComponent: true, -}; - -``` - -## Code - -```javascript -import { unstable_useMemoCache as useMemoCache } from "react"; // @validateRefAccessDuringRender: true -function Foo(props, ref) { - const $ = useMemoCache(2); - ref.current = 2; - let t0; - if ($[0] !== props.bar) { - t0 =
{props.bar}
; - $[0] = props.bar; - $[1] = t0; - } else { - t0 = $[1]; - } - return t0; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }], - isComponent: true, -}; - -``` - -### Eval output -(kind: ok)
foo
\ No newline at end of file