diff --git a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts index d1546038edcbe..3dc532c87d6ea 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Inference/InferReferenceEffects.ts @@ -37,7 +37,7 @@ import { import {FunctionSignature} from '../HIR/ObjectShape'; import { printIdentifier, - printMixedHIR, + printInstructionValue, printPlace, printSourceLocation, } from '../HIR/PrintHIR'; @@ -669,7 +669,14 @@ class InferenceState { } for (const [value, kind] of this.#values) { const id = identify(value); - result.values[id] = {kind, value: printMixedHIR(value)}; + result.values[id] = { + abstract: { + kind: kind.kind, + context: [...kind.context].map(printPlace), + reason: [...kind.reason], + }, + value: printInstructionValue(value), + }; } for (const [variable, values] of this.#variables) { result.variables[`$${variable}`] = [...values].map(identify); @@ -1844,11 +1851,11 @@ function getContextRefOperand( ): Array { const result = []; for (const place of eachInstructionValueOperand(instrValue)) { - if ( - state.isDefined(place) && - state.kind(place).kind === ValueKind.Context - ) { - result.push(place); + if (state.isDefined(place)) { + const kind = state.kind(place); + if (kind.kind === ValueKind.Context) { + result.push(...kind.context); + } } } return result; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-use-effect-function-mutates-ref.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-use-effect-function-mutates-ref.expect.md new file mode 100644 index 0000000000000..b3f1104239938 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-use-effect-function-mutates-ref.expect.md @@ -0,0 +1,48 @@ + +## Input + +```javascript +// @validateNoFreezingKnownMutableFunctions + +import {useCallback, useEffect, useRef} from 'react'; +import {useHook} from 'shared-runtime'; + +function Component() { + const params = useHook(); + const update = useCallback( + partialParams => { + const nextParams = { + ...params, + ...partialParams, + }; + nextParams.param = 'value'; + console.log(nextParams); + }, + [params] + ); + const ref = useRef(null); + useEffect(() => { + if (ref.current === null) { + update(); + } + }, [update]); + + return 'ok'; +} + +``` + + +## Error + +``` + 12 | ...partialParams, + 13 | }; +> 14 | nextParams.param = 'value'; + | ^^^^^^^^^^ InvalidReact: Mutating a value returned from a function whose return value should not be mutated. Found mutation of `params` (14:14) + 15 | console.log(nextParams); + 16 | }, + 17 | [params] +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-use-effect-function-mutates-ref.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-use-effect-function-mutates-ref.js new file mode 100644 index 0000000000000..2e8b7827d09d9 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-use-effect-function-mutates-ref.js @@ -0,0 +1,27 @@ +// @validateNoFreezingKnownMutableFunctions + +import {useCallback, useEffect, useRef} from 'react'; +import {useHook} from 'shared-runtime'; + +function Component() { + const params = useHook(); + const update = useCallback( + partialParams => { + const nextParams = { + ...params, + ...partialParams, + }; + nextParams.param = 'value'; + console.log(nextParams); + }, + [params] + ); + const ref = useRef(null); + useEffect(() => { + if (ref.current === null) { + update(); + } + }, [update]); + + return 'ok'; +}