diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts index 8997ad086f5a2..296b3edb762e2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts @@ -188,6 +188,7 @@ export type ObjectShape = { * the inferred types for [] and {}. */ export type ShapeRegistry = Map; +export const BuiltInPropsId = "BuiltInProps"; export const BuiltInArrayId = "BuiltInArray"; export const BuiltInFunctionId = "BuiltInFunction"; export const BuiltInJsxId = "BuiltInJsx"; @@ -207,6 +208,11 @@ export const BuiltInDispatchId = "BuiltInDispatch"; // ShapeRegistry with default definitions for built-ins. export const BUILTIN_SHAPES: ShapeRegistry = new Map(); +// If the `ref` prop exists, it has the ref type +addObject(BUILTIN_SHAPES, BuiltInPropsId, [ + ["ref", { kind: "Object", shapeId: BuiltInUseRefId }], +]); + /* Built-in array shape */ addObject(BUILTIN_SHAPES, BuiltInArrayId, [ [ diff --git a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts index b26dea5a428b4..b77d7b861c6ea 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts @@ -23,6 +23,7 @@ import { BuiltInFunctionId, BuiltInJsxId, BuiltInObjectId, + BuiltInPropsId, BuiltInUseRefId, } from "../HIR/ObjectShape"; import { eachInstructionLValue, eachInstructionOperand } from "../HIR/visitors"; @@ -101,7 +102,13 @@ function* generate( func: HIRFunction ): Generator { if (func.env.fnType === "Component") { - const [_, ref] = func.params; + const [props, ref] = func.params; + if (props && props.kind === "Identifier") { + yield equation(props.identifier.type, { + kind: "Object", + shapeId: BuiltInPropsId, + }); + } if (ref && ref.kind === "Identifier") { yield equation(ref.identifier.type, { kind: "Object", diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-destructure.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-destructure.expect.md new file mode 100644 index 0000000000000..d18692095926e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-destructure.expect.md @@ -0,0 +1,25 @@ + +## Input + +```javascript +// @validateRefAccessDuringRender @compilationMode(infer) +function Component({ ref }) { + const value = ref.current; + return
{value}
; +} + +``` + + +## Error + +``` + 2 | function Component({ ref }) { + 3 | const value = ref.current; +> 4 | return
{value}
; + | ^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at read $17:TObject (4:4) + 5 | } + 6 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-destructure.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-destructure.js new file mode 100644 index 0000000000000..2d5cf0ef2d80b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-destructure.js @@ -0,0 +1,5 @@ +// @validateRefAccessDuringRender @compilationMode(infer) +function Component({ ref }) { + const value = ref.current; + return
{value}
; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-property-load.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-property-load.expect.md new file mode 100644 index 0000000000000..e374900a95ad4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-property-load.expect.md @@ -0,0 +1,25 @@ + +## Input + +```javascript +// @validateRefAccessDuringRender @compilationMode(infer) +function Component(props) { + const value = props.ref.current; + return
{value}
; +} + +``` + + +## Error + +``` + 2 | function Component(props) { + 3 | const value = props.ref.current; +> 4 | return
{value}
; + | ^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at read $15:TObject (4:4) + 5 | } + 6 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-property-load.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-property-load.js new file mode 100644 index 0000000000000..c31a081366e46 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-read-ref-prop-in-render-property-load.js @@ -0,0 +1,5 @@ +// @validateRefAccessDuringRender @compilationMode(infer) +function Component(props) { + const value = props.ref.current; + return
{value}
; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-write-ref-prop-in-render.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-write-ref-prop-in-render.expect.md new file mode 100644 index 0000000000000..c00aaca4fe3a3 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-write-ref-prop-in-render.expect.md @@ -0,0 +1,27 @@ + +## Input + +```javascript +// @validateRefAccessDuringRender @compilationMode(infer) +function Component(props) { + const ref = props.ref; + ref.current = true; + return
{value}
; +} + +``` + + +## Error + +``` + 2 | function Component(props) { + 3 | const ref = props.ref; +> 4 | ref.current = true; + | ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (4:4) + 5 | return
{value}
; + 6 | } + 7 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-write-ref-prop-in-render.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-write-ref-prop-in-render.js new file mode 100644 index 0000000000000..b939ea540bd13 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-write-ref-prop-in-render.js @@ -0,0 +1,6 @@ +// @validateRefAccessDuringRender @compilationMode(infer) +function Component(props) { + const ref = props.ref; + ref.current = true; + return
{value}
; +}