From 3a6a2e7e4a807003bc42e103d157ae783602c362 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Thu, 21 Mar 2024 21:40:02 -0700 Subject: [PATCH] Repro for "context variables are always mutable" error w callbacks I haven't debugged to understand exactly why this pattern fails, but there are a few instances of this internally. It's especially weird because ```javascript // @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions function Component(props) { const [_state, setState] = useState(); const a = () => { return b(); }; const b = () => { return ( <>
onClick(true)} />
onClick(false)} /> // <---- only repros if there's a second call! ); }; const onClick = (value) => { setState(value); }; return
{a()}
; } ``` Here, if `b()` only had one nested function expression that called `onClick` it would work. Also, if we disable `@enableTransitivelyFreezeFunctionExpressions` then it works. But the combination of multiple calls plus that mode causes "context variables are always mutable". I'm guessing we're freezing `onClick` twice and the second time reports an error since it calls `setState`. --- ...ted-callback-from-other-callback.expect.md | 46 +++++++++++++++++++ ...to-hoisted-callback-from-other-callback.js | 25 ++++++++++ 2 files changed, 71 insertions(+) create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-multiple-calls-to-hoisted-callback-from-other-callback.expect.md create mode 100644 compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-multiple-calls-to-hoisted-callback-from-other-callback.js diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-multiple-calls-to-hoisted-callback-from-other-callback.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-multiple-calls-to-hoisted-callback-from-other-callback.expect.md new file mode 100644 index 0000000000..902b33880f --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-multiple-calls-to-hoisted-callback-from-other-callback.expect.md @@ -0,0 +1,46 @@ + +## Input + +```javascript +// @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions +function Component(props) { + const [_state, setState] = useState(); + const a = () => { + return b(); + }; + const b = () => { + return ( + <> +
onClick(true)} /> +
onClick(false)} /> + + ); + }; + const onClick = (value) => { + setState(value); + }; + + return
{a()}
; +} + +export const FIXTURE_ENTRYPONT = { + fn: Component, + props: [{}], +}; + +``` + + +## Error + +``` + 9 | <> + 10 |
onClick(true)} /> +> 11 |
onClick(false)} /> + | ^^^^^^^ [ReactForget] Invariant: [InferReferenceEffects] Context variables are always mutable. (11:11) + 12 | + 13 | ); + 14 | }; +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-multiple-calls-to-hoisted-callback-from-other-callback.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-multiple-calls-to-hoisted-callback-from-other-callback.js new file mode 100644 index 0000000000..fa91f3b0f4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-multiple-calls-to-hoisted-callback-from-other-callback.js @@ -0,0 +1,25 @@ +// @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions +function Component(props) { + const [_state, setState] = useState(); + const a = () => { + return b(); + }; + const b = () => { + return ( + <> +
onClick(true)} /> +
onClick(false)} /> + + ); + }; + const onClick = (value) => { + setState(value); + }; + + return
{a()}
; +} + +export const FIXTURE_ENTRYPONT = { + fn: Component, + props: [{}], +};