From 188502524e72b86d61a070c312b17aae6f0baff7 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Fri, 30 Jan 2026 18:47:18 -0800 Subject: [PATCH] [compiler] Handle sequential optionals The previous PR (#35606) handled maybe-throw terminals within value blocks, and the changes there also made BuildReactiveFunction able to handle sequential optionals. However, CollectOptionalChainDependencies doesn't yet handle sequential value blocks, like `foo(a?.b, b?.c) ?? {}`. This PR fixes the remaining case, which means we should no longer have arbitrary limitations around value blocks. --- .../HIR/CollectOptionalChainDependencies.ts | 124 +++++++++++++++++- ...ested-optional-member-in-logical.expect.md | 25 ++++ .../bug-nested-optional-member-in-logical.js | 4 + ...-optional-call-chain-in-optional.expect.md | 39 ------ .../optional-call-chain-in-optional.expect.md | 53 ++++++++ ....ts => optional-call-chain-in-optional.ts} | 0 ...call-with-multiple-optional-args.expect.md | 22 ++++ ...tional-call-with-multiple-optional-args.js | 3 + ...tional-chain-and-logical-in-call.expect.md | 24 ++++ .../optional-chain-and-logical-in-call.js | 4 + ...ptional-chain-deeply-nested-args.expect.md | 24 ++++ .../optional-chain-deeply-nested-args.js | 4 + ...double-method-with-optional-args.expect.md | 35 +++++ ...-chain-double-method-with-optional-args.js | 4 + .../optional-chain-in-ternary-test.expect.md | 24 ++++ .../optional-chain-in-ternary-test.js | 4 + ...n-method-call-with-optional-args.expect.md | 35 +++++ ...al-chain-method-call-with-optional-args.js | 4 + ...-nested-calls-with-optional-args.expect.md | 24 ++++ ...l-chain-nested-calls-with-optional-args.js | 4 + ...hain-nested-optional-in-optional.expect.md | 24 ++++ ...ional-chain-nested-optional-in-optional.js | 4 + ...ain-short-circuit-with-optionals.expect.md | 24 ++++ ...onal-chain-short-circuit-with-optionals.js | 4 + ...optional-chain-with-logical-args.expect.md | 24 ++++ .../optional-chain-with-logical-args.js | 4 + ...h-multiple-optionals-and-logical.expect.md | 34 +++++ ...ain-with-multiple-optionals-and-logical.js | 4 + ...in-with-nullish-coalescing-chain.expect.md | 35 +++++ ...nal-chain-with-nullish-coalescing-chain.js | 4 + ...in-with-optional-and-ternary-arg.expect.md | 24 ++++ ...nal-chain-with-optional-and-ternary-arg.js | 4 + ...nal-chain-with-optional-call-arg.expect.md | 34 +++++ .../optional-chain-with-optional-call-arg.js | 4 + ...ith-ternary-containing-optionals.expect.md | 24 ++++ ...chain-with-ternary-containing-optionals.js | 4 + ...l-chain-with-three-optional-args.expect.md | 24 ++++ ...optional-chain-with-three-optional-args.js | 4 + ...-optional-call-chain-in-optional.expect.md | 40 ------ .../optional-call-chain-in-optional.expect.md | 54 ++++++++ ....ts => optional-call-chain-in-optional.ts} | 0 41 files changed, 755 insertions(+), 82 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-nested-optional-member-in-logical.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-nested-optional-member-in-logical.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-chain-in-optional.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{error.todo-optional-call-chain-in-optional.ts => optional-call-chain-in-optional.ts} (100%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-with-multiple-optional-args.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-with-multiple-optional-args.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-and-logical-in-call.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-and-logical-in-call.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-deeply-nested-args.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-deeply-nested-args.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-double-method-with-optional-args.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-double-method-with-optional-args.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-in-ternary-test.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-in-ternary-test.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-method-call-with-optional-args.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-method-call-with-optional-args.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-calls-with-optional-args.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-calls-with-optional-args.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-optional-in-optional.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-optional-in-optional.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-short-circuit-with-optionals.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-short-circuit-with-optionals.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-logical-args.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-logical-args.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-multiple-optionals-and-logical.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-multiple-optionals-and-logical.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-nullish-coalescing-chain.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-nullish-coalescing-chain.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-and-ternary-arg.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-and-ternary-arg.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-call-arg.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-call-arg.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-ternary-containing-optionals.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-ternary-containing-optionals.js create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-three-optional-args.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-three-optional-args.js delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/optional-call-chain-in-optional.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/{error.todo-optional-call-chain-in-optional.ts => optional-call-chain-in-optional.ts} (100%) diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts index 75dad4c1bfe..6fb8cedb2d6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts @@ -226,6 +226,121 @@ function matchOptionalTestBlock( return null; } +/** + * Finds the test block for an optional chain by traversing through intermediate + * value blocks and nested value block terminals. This handles cases like + * `foo(a?.value, b?.value)?.result` where multiple optional chains or other + * value blocks (logicals, ternaries) appear as arguments, causing the fallthrough + * of one value block to contain another value block terminal rather than directly + * containing a branch. + * + * This function also ensures any nested optional chains encountered are properly + * traversed and added to the context. + */ +function findOptionalTestBlock( + blockId: BlockId, + context: OptionalTraversalContext, +): BasicBlock | null { + const block = context.blocks.get(blockId); + if (block == null) { + return null; + } + + const terminal = block.terminal; + switch (terminal.kind) { + case 'branch': { + // Found the branch terminal we're looking for + return block; + } + case 'optional': { + // Traverse the nested optional to collect its dependencies + if (!context.seenOptionals.has(block.id)) { + traverseOptionalBlock( + block as TBasicBlock, + context, + null, + ); + } + // Continue searching in the fallthrough of this optional + return findOptionalTestBlock(terminal.fallthrough, context); + } + case 'logical': + case 'ternary': { + // Traverse nested value blocks in the test branch + const testBlock = context.blocks.get(terminal.test); + if (testBlock?.terminal.kind === 'branch') { + traverseValueBlock(testBlock.terminal.consequent, context); + traverseValueBlock(testBlock.terminal.alternate, context); + } + // Continue searching in the fallthrough + return findOptionalTestBlock(terminal.fallthrough, context); + } + case 'sequence': { + // Traverse the sequence block for any nested optionals + traverseValueBlock(terminal.block, context); + // Continue searching in the fallthrough + return findOptionalTestBlock(terminal.fallthrough, context); + } + default: { + // Unexpected terminal kind - return the block so caller can handle/error + return block; + } + } +} + +/** + * Traverses a value block looking for nested optional chains to collect. + * This handles cases where optional chains appear inside logical, ternary, + * or sequence expressions. + */ +function traverseValueBlock( + blockId: BlockId, + context: OptionalTraversalContext, +): void { + const block = context.blocks.get(blockId); + if (block == null) { + return; + } + + const terminal = block.terminal; + switch (terminal.kind) { + case 'optional': { + if (!context.seenOptionals.has(block.id)) { + traverseOptionalBlock( + block as TBasicBlock, + context, + null, + ); + } + break; + } + case 'logical': + case 'ternary': { + const testBlock = context.blocks.get(terminal.test); + if (testBlock?.terminal.kind === 'branch') { + traverseValueBlock(testBlock.terminal.consequent, context); + traverseValueBlock(testBlock.terminal.alternate, context); + } + traverseValueBlock(terminal.fallthrough, context); + break; + } + case 'sequence': { + traverseValueBlock(terminal.block, context); + traverseValueBlock(terminal.fallthrough, context); + break; + } + case 'branch': + case 'goto': { + // Terminal blocks, nothing more to traverse + break; + } + default: { + // Other terminal kinds - don't recurse + break; + } + } +} + /** * Traverse into the optional block and all transitively referenced blocks to * collect sidemaps of optional chain dependencies. @@ -302,15 +417,18 @@ function traverseOptionalBlock( * - * - a optional base block with a separate nested optional-chain (e.g. a(c?.d)?.d) */ - const testBlock = context.blocks.get(maybeTest.terminal.fallthrough)!; - if (testBlock!.terminal.kind !== 'branch') { + const testBlock = findOptionalTestBlock( + maybeTest.terminal.fallthrough, + context, + ); + if (testBlock == null || testBlock.terminal.kind !== 'branch') { /** * Fallthrough of the inner optional should be a block with no * instructions, terminating with Test($) */ CompilerError.throwTodo({ - reason: `Unexpected terminal kind \`${testBlock.terminal.kind}\` for optional fallthrough block`, + reason: `Unexpected terminal kind \`${testBlock?.terminal.kind ?? '(null)'}\` for optional fallthrough block`, loc: maybeTest.terminal.loc, }); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-nested-optional-member-in-logical.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-nested-optional-member-in-logical.expect.md new file mode 100644 index 00000000000..238fbf5c869 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-nested-optional-member-in-logical.expect.md @@ -0,0 +1,25 @@ + +## Input + +```javascript +function Component({a, b}) { + 'use memo'; + return useHook(a?.value, b?.value) ?? {}; +} + +``` + +## Code + +```javascript +function Component(t0) { + "use memo"; + const { a, b } = t0; + + return useHook(a?.value, b?.value) ?? {}; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-nested-optional-member-in-logical.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-nested-optional-member-in-logical.js new file mode 100644 index 00000000000..18abc95958d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/bug-nested-optional-member-in-logical.js @@ -0,0 +1,4 @@ +function Component({a, b}) { + 'use memo'; + return useHook(a?.value, b?.value) ?? {}; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md deleted file mode 100644 index 6551bb8d40f..00000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md +++ /dev/null @@ -1,39 +0,0 @@ - -## Input - -```javascript -function useFoo(props: {value: {x: string; y: string} | null}) { - const value = props.value; - return createArray(value?.x, value?.y)?.join(', '); -} - -function createArray(...args: Array): Array { - return args; -} - -export const FIXTURE_ENTRYPONT = { - fn: useFoo, - props: [{value: null}], -}; - -``` - - -## Error - -``` -Found 1 error: - -Todo: Unexpected terminal kind `optional` for optional fallthrough block - -error.todo-optional-call-chain-in-optional.ts:3:21 - 1 | function useFoo(props: {value: {x: string; y: string} | null}) { - 2 | const value = props.value; -> 3 | return createArray(value?.x, value?.y)?.join(', '); - | ^^^^^^^^ Unexpected terminal kind `optional` for optional fallthrough block - 4 | } - 5 | - 6 | function createArray(...args: Array): Array { -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-chain-in-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-chain-in-optional.expect.md new file mode 100644 index 00000000000..c9d71a8c3b7 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-chain-in-optional.expect.md @@ -0,0 +1,53 @@ + +## Input + +```javascript +function useFoo(props: {value: {x: string; y: string} | null}) { + const value = props.value; + return createArray(value?.x, value?.y)?.join(', '); +} + +function createArray(...args: Array): Array { + return args; +} + +export const FIXTURE_ENTRYPONT = { + fn: useFoo, + props: [{value: null}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function useFoo(props) { + const $ = _c(3); + const value = props.value; + let t0; + if ($[0] !== value?.x || $[1] !== value?.y) { + t0 = createArray(value?.x, value?.y)?.join(", "); + $[0] = value?.x; + $[1] = value?.y; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; +} + +function createArray(...t0) { + const args = t0; + return args; +} + +export const FIXTURE_ENTRYPONT = { + fn: useFoo, + props: [{ value: null }], +}; + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-chain-in-optional.ts similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.ts rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-chain-in-optional.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-with-multiple-optional-args.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-with-multiple-optional-args.expect.md new file mode 100644 index 00000000000..c5eea250b8f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-with-multiple-optional-args.expect.md @@ -0,0 +1,22 @@ + +## Input + +```javascript +function Component({a, b}) { + return foo(a?.value, b?.value)?.result; +} + +``` + +## Code + +```javascript +function Component(t0) { + const { a, b } = t0; + return foo(a?.value, b?.value)?.result; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-with-multiple-optional-args.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-with-multiple-optional-args.js new file mode 100644 index 00000000000..04c6473316b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-call-with-multiple-optional-args.js @@ -0,0 +1,3 @@ +function Component({a, b}) { + return foo(a?.value, b?.value)?.result; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-and-logical-in-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-and-logical-in-call.expect.md new file mode 100644 index 00000000000..0e4bc47c17b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-and-logical-in-call.expect.md @@ -0,0 +1,24 @@ + +## Input + +```javascript +// Mix of optional chain and logical AND in call args +function Component({a, b, c}) { + return foo(a?.value, b && b.value, c?.value)?.result; +} + +``` + +## Code + +```javascript +// Mix of optional chain and logical AND in call args +function Component(t0) { + const { a, b, c } = t0; + return foo(a?.value, b && b.value, c?.value)?.result; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-and-logical-in-call.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-and-logical-in-call.js new file mode 100644 index 00000000000..a615ee6ee40 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-and-logical-in-call.js @@ -0,0 +1,4 @@ +// Mix of optional chain and logical AND in call args +function Component({a, b, c}) { + return foo(a?.value, b && b.value, c?.value)?.result; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-deeply-nested-args.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-deeply-nested-args.expect.md new file mode 100644 index 00000000000..487e81507fe --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-deeply-nested-args.expect.md @@ -0,0 +1,24 @@ + +## Input + +```javascript +// Deeply nested optional chains as args +function Component({a, b}) { + return foo(a?.b?.c?.d, b?.e?.f?.g)?.result?.final; +} + +``` + +## Code + +```javascript +// Deeply nested optional chains as args +function Component(t0) { + const { a, b } = t0; + return foo(a?.b?.c?.d, b?.e?.f?.g)?.result?.final; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-deeply-nested-args.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-deeply-nested-args.js new file mode 100644 index 00000000000..e8cae0bfb98 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-deeply-nested-args.js @@ -0,0 +1,4 @@ +// Deeply nested optional chains as args +function Component({a, b}) { + return foo(a?.b?.c?.d, b?.e?.f?.g)?.result?.final; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-double-method-with-optional-args.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-double-method-with-optional-args.expect.md new file mode 100644 index 00000000000..d8b1718fb81 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-double-method-with-optional-args.expect.md @@ -0,0 +1,35 @@ + +## Input + +```javascript +// Chained optional method calls with optional args +function Component({obj, a, b, c}) { + return obj(a?.value)?.second(b?.value); +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // Chained optional method calls with optional args +function Component(t0) { + const $ = _c(4); + const { obj, a, b } = t0; + let t1; + if ($[0] !== a?.value || $[1] !== b?.value || $[2] !== obj) { + t1 = obj(a?.value)?.second(b?.value); + $[0] = a?.value; + $[1] = b?.value; + $[2] = obj; + $[3] = t1; + } else { + t1 = $[3]; + } + return t1; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-double-method-with-optional-args.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-double-method-with-optional-args.js new file mode 100644 index 00000000000..fc7e81f3276 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-double-method-with-optional-args.js @@ -0,0 +1,4 @@ +// Chained optional method calls with optional args +function Component({obj, a, b, c}) { + return obj(a?.value)?.second(b?.value); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-in-ternary-test.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-in-ternary-test.expect.md new file mode 100644 index 00000000000..503c2e7dd4e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-in-ternary-test.expect.md @@ -0,0 +1,24 @@ + +## Input + +```javascript +// Optional chain in ternary condition +function Component({a, b, c}) { + return foo(a?.value, b?.value) ? c?.result : null; +} + +``` + +## Code + +```javascript +// Optional chain in ternary condition +function Component(t0) { + const { a, b, c } = t0; + return foo(a?.value, b?.value) ? c?.result : null; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-in-ternary-test.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-in-ternary-test.js new file mode 100644 index 00000000000..1b01c6b2604 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-in-ternary-test.js @@ -0,0 +1,4 @@ +// Optional chain in ternary condition +function Component({a, b, c}) { + return foo(a?.value, b?.value) ? c?.result : null; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-method-call-with-optional-args.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-method-call-with-optional-args.expect.md new file mode 100644 index 00000000000..3c201edb952 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-method-call-with-optional-args.expect.md @@ -0,0 +1,35 @@ + +## Input + +```javascript +// Optional method call with optional args +function Component({obj, a, b}) { + return obj?.method(a?.value, b?.value); +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // Optional method call with optional args +function Component(t0) { + const $ = _c(4); + const { obj, a, b } = t0; + let t1; + if ($[0] !== a?.value || $[1] !== b?.value || $[2] !== obj) { + t1 = obj?.method(a?.value, b?.value); + $[0] = a?.value; + $[1] = b?.value; + $[2] = obj; + $[3] = t1; + } else { + t1 = $[3]; + } + return t1; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-method-call-with-optional-args.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-method-call-with-optional-args.js new file mode 100644 index 00000000000..934250905ce --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-method-call-with-optional-args.js @@ -0,0 +1,4 @@ +// Optional method call with optional args +function Component({obj, a, b}) { + return obj?.method(a?.value, b?.value); +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-calls-with-optional-args.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-calls-with-optional-args.expect.md new file mode 100644 index 00000000000..70536e4bc9a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-calls-with-optional-args.expect.md @@ -0,0 +1,24 @@ + +## Input + +```javascript +// Nested function calls with optional args at multiple levels +function Component({a, b, c}) { + return outer(inner(a?.value, b?.value)?.mid, c?.value)?.result; +} + +``` + +## Code + +```javascript +// Nested function calls with optional args at multiple levels +function Component(t0) { + const { a, b, c } = t0; + return outer(inner(a?.value, b?.value)?.mid, c?.value)?.result; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-calls-with-optional-args.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-calls-with-optional-args.js new file mode 100644 index 00000000000..ea321132000 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-calls-with-optional-args.js @@ -0,0 +1,4 @@ +// Nested function calls with optional args at multiple levels +function Component({a, b, c}) { + return outer(inner(a?.value, b?.value)?.mid, c?.value)?.result; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-optional-in-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-optional-in-optional.expect.md new file mode 100644 index 00000000000..328d5945c60 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-optional-in-optional.expect.md @@ -0,0 +1,24 @@ + +## Input + +```javascript +// Nested optional chains: outer optional calling inner optional result +function Component({a, b}) { + return foo(a?.bar?.baz, b?.qux)?.result; +} + +``` + +## Code + +```javascript +// Nested optional chains: outer optional calling inner optional result +function Component(t0) { + const { a, b } = t0; + return foo(a?.bar?.baz, b?.qux)?.result; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-optional-in-optional.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-optional-in-optional.js new file mode 100644 index 00000000000..018f6efd6b3 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-nested-optional-in-optional.js @@ -0,0 +1,4 @@ +// Nested optional chains: outer optional calling inner optional result +function Component({a, b}) { + return foo(a?.bar?.baz, b?.qux)?.result; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-short-circuit-with-optionals.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-short-circuit-with-optionals.expect.md new file mode 100644 index 00000000000..1ea8c0adcff --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-short-circuit-with-optionals.expect.md @@ -0,0 +1,24 @@ + +## Input + +```javascript +// Short-circuit logical with multiple optional chains +function Component({a, b, c}) { + return a?.value && foo(b?.value, c?.value)?.result; +} + +``` + +## Code + +```javascript +// Short-circuit logical with multiple optional chains +function Component(t0) { + const { a, b, c } = t0; + return a?.value && foo(b?.value, c?.value)?.result; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-short-circuit-with-optionals.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-short-circuit-with-optionals.js new file mode 100644 index 00000000000..ef4a734fe0e --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-short-circuit-with-optionals.js @@ -0,0 +1,4 @@ +// Short-circuit logical with multiple optional chains +function Component({a, b, c}) { + return a?.value && foo(b?.value, c?.value)?.result; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-logical-args.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-logical-args.expect.md new file mode 100644 index 00000000000..64267e95a48 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-logical-args.expect.md @@ -0,0 +1,24 @@ + +## Input + +```javascript +// Optional chain with logical expressions as args +function Component({a, b, c}) { + return foo(a?.value ?? b, c?.value || 'default')?.result; +} + +``` + +## Code + +```javascript +// Optional chain with logical expressions as args +function Component(t0) { + const { a, b, c } = t0; + return foo(a?.value ?? b, c?.value || "default")?.result; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-logical-args.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-logical-args.js new file mode 100644 index 00000000000..ad03221850f --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-logical-args.js @@ -0,0 +1,4 @@ +// Optional chain with logical expressions as args +function Component({a, b, c}) { + return foo(a?.value ?? b, c?.value || 'default')?.result; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-multiple-optionals-and-logical.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-multiple-optionals-and-logical.expect.md new file mode 100644 index 00000000000..eb635746dcf --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-multiple-optionals-and-logical.expect.md @@ -0,0 +1,34 @@ + +## Input + +```javascript +// Multiple optional args with logical fallback +function Component({a, b}) { + return foo(a?.value, b?.value)?.result ?? []; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // Multiple optional args with logical fallback +function Component(t0) { + const $ = _c(3); + const { a, b } = t0; + let t1; + if ($[0] !== a?.value || $[1] !== b?.value) { + t1 = foo(a?.value, b?.value)?.result ?? []; + $[0] = a?.value; + $[1] = b?.value; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-multiple-optionals-and-logical.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-multiple-optionals-and-logical.js new file mode 100644 index 00000000000..a3cfec16896 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-multiple-optionals-and-logical.js @@ -0,0 +1,4 @@ +// Multiple optional args with logical fallback +function Component({a, b}) { + return foo(a?.value, b?.value)?.result ?? []; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-nullish-coalescing-chain.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-nullish-coalescing-chain.expect.md new file mode 100644 index 00000000000..c21146114f1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-nullish-coalescing-chain.expect.md @@ -0,0 +1,35 @@ + +## Input + +```javascript +// Nullish coalescing with optional chain result +function Component({a, b, fallback}) { + return (foo(a?.value, b?.value) ?? fallback)?.result; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // Nullish coalescing with optional chain result +function Component(t0) { + const $ = _c(4); + const { a, b, fallback } = t0; + let t1; + if ($[0] !== a?.value || $[1] !== b?.value || $[2] !== fallback) { + t1 = (foo(a?.value, b?.value) ?? fallback)?.result; + $[0] = a?.value; + $[1] = b?.value; + $[2] = fallback; + $[3] = t1; + } else { + t1 = $[3]; + } + return t1; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-nullish-coalescing-chain.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-nullish-coalescing-chain.js new file mode 100644 index 00000000000..f3e8fb81fc3 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-nullish-coalescing-chain.js @@ -0,0 +1,4 @@ +// Nullish coalescing with optional chain result +function Component({a, b, fallback}) { + return (foo(a?.value, b?.value) ?? fallback)?.result; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-and-ternary-arg.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-and-ternary-arg.expect.md new file mode 100644 index 00000000000..f1ca138bb5a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-and-ternary-arg.expect.md @@ -0,0 +1,24 @@ + +## Input + +```javascript +// Optional chain with ternary and optional args +function Component({a, b, cond}) { + return foo(a?.value, cond ? b : null)?.result; +} + +``` + +## Code + +```javascript +// Optional chain with ternary and optional args +function Component(t0) { + const { a, b, cond } = t0; + return foo(a?.value, cond ? b : null)?.result; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-and-ternary-arg.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-and-ternary-arg.js new file mode 100644 index 00000000000..4f293593392 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-and-ternary-arg.js @@ -0,0 +1,4 @@ +// Optional chain with ternary and optional args +function Component({a, b, cond}) { + return foo(a?.value, cond ? b : null)?.result; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-call-arg.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-call-arg.expect.md new file mode 100644 index 00000000000..f055a94c351 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-call-arg.expect.md @@ -0,0 +1,34 @@ + +## Input + +```javascript +// Optional call as argument to another optional chain +function Component({a, b}) { + return foo(a?.(), b?.value)?.result; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // Optional call as argument to another optional chain +function Component(t0) { + const $ = _c(3); + const { a, b } = t0; + let t1; + if ($[0] !== a || $[1] !== b?.value) { + t1 = foo(a?.(), b?.value)?.result; + $[0] = a; + $[1] = b?.value; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-call-arg.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-call-arg.js new file mode 100644 index 00000000000..909e67edcfb --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-optional-call-arg.js @@ -0,0 +1,4 @@ +// Optional call as argument to another optional chain +function Component({a, b}) { + return foo(a?.(), b?.value)?.result; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-ternary-containing-optionals.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-ternary-containing-optionals.expect.md new file mode 100644 index 00000000000..69dab996528 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-ternary-containing-optionals.expect.md @@ -0,0 +1,24 @@ + +## Input + +```javascript +// Ternary with optional chains in both branches +function Component({a, b, cond}) { + return foo(cond ? a?.value : b?.value)?.result; +} + +``` + +## Code + +```javascript +// Ternary with optional chains in both branches +function Component(t0) { + const { a, b, cond } = t0; + return foo(cond ? a?.value : b?.value)?.result; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-ternary-containing-optionals.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-ternary-containing-optionals.js new file mode 100644 index 00000000000..ea1fc9dcd30 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-ternary-containing-optionals.js @@ -0,0 +1,4 @@ +// Ternary with optional chains in both branches +function Component({a, b, cond}) { + return foo(cond ? a?.value : b?.value)?.result; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-three-optional-args.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-three-optional-args.expect.md new file mode 100644 index 00000000000..b30069653bd --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-three-optional-args.expect.md @@ -0,0 +1,24 @@ + +## Input + +```javascript +// Three optional chain arguments +function Component({a, b, c}) { + return foo(a?.value, b?.value, c?.value)?.result; +} + +``` + +## Code + +```javascript +// Three optional chain arguments +function Component(t0) { + const { a, b, c } = t0; + return foo(a?.value, b?.value, c?.value)?.result; +} + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-three-optional-args.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-three-optional-args.js new file mode 100644 index 00000000000..93509c62316 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-chain-with-three-optional-args.js @@ -0,0 +1,4 @@ +// Three optional chain arguments +function Component({a, b, c}) { + return foo(a?.value, b?.value, c?.value)?.result; +} diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.expect.md deleted file mode 100644 index 5da7122c76f..00000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.expect.md +++ /dev/null @@ -1,40 +0,0 @@ - -## Input - -```javascript -// @enablePropagateDepsInHIR -function useFoo(props: {value: {x: string; y: string} | null}) { - const value = props.value; - return createArray(value?.x, value?.y)?.join(', '); -} - -function createArray(...args: Array): Array { - return args; -} - -export const FIXTURE_ENTRYPONT = { - fn: useFoo, - props: [{value: null}], -}; - -``` - - -## Error - -``` -Found 1 error: - -Todo: Unexpected terminal kind `optional` for optional fallthrough block - -error.todo-optional-call-chain-in-optional.ts:4:21 - 2 | function useFoo(props: {value: {x: string; y: string} | null}) { - 3 | const value = props.value; -> 4 | return createArray(value?.x, value?.y)?.join(', '); - | ^^^^^^^^ Unexpected terminal kind `optional` for optional fallthrough block - 5 | } - 6 | - 7 | function createArray(...args: Array): Array { -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/optional-call-chain-in-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/optional-call-chain-in-optional.expect.md new file mode 100644 index 00000000000..af046d58b71 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/optional-call-chain-in-optional.expect.md @@ -0,0 +1,54 @@ + +## Input + +```javascript +// @enablePropagateDepsInHIR +function useFoo(props: {value: {x: string; y: string} | null}) { + const value = props.value; + return createArray(value?.x, value?.y)?.join(', '); +} + +function createArray(...args: Array): Array { + return args; +} + +export const FIXTURE_ENTRYPONT = { + fn: useFoo, + props: [{value: null}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR +function useFoo(props) { + const $ = _c(3); + const value = props.value; + let t0; + if ($[0] !== value?.x || $[1] !== value?.y) { + t0 = createArray(value?.x, value?.y)?.join(", "); + $[0] = value?.x; + $[1] = value?.y; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; +} + +function createArray(...t0) { + const args = t0; + return args; +} + +export const FIXTURE_ENTRYPONT = { + fn: useFoo, + props: [{ value: null }], +}; + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/optional-call-chain-in-optional.ts similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.ts rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/optional-call-chain-in-optional.ts