From 76d881b7c9b1001c07f60c29c7380606ac875dbb Mon Sep 17 00:00:00 2001 From: GokhanKabar Date: Wed, 11 Mar 2026 22:49:18 +0100 Subject: [PATCH] [Compiler] Fix overloads causing wrong function export with gating TypeScript overload signatures (TSDeclareFunction nodes) were causing getFunctionReferencedBeforeDeclarationAtTopLevel to falsely detect functions as referenced before their declaration. This triggered the hoisting code path which placed the export on the unoptimized variant instead of the dispatcher function. Fix: Skip TSDeclareFunction nodes in the reference-before-declaration traversal, consistent with how other type-only nodes (TypeAnnotation, TSTypeAnnotation, TypeAlias, TSTypeAliasDeclaration) are already skipped. Fixes #35991 --- .../src/Entrypoint/Program.ts | 3 + ...t-export-function-with-overloads.expect.md | 59 +++++++++++++++++++ ...ng-test-export-function-with-overloads.tsx | 14 +++++ 3 files changed, 76 insertions(+) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-with-overloads.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-with-overloads.tsx diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index 2880e9283c..2f8bf95f55 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -1258,6 +1258,9 @@ function getFunctionReferencedBeforeDeclarationAtTopLevel( TSTypeAliasDeclaration(path) { path.skip(); }, + TSDeclareFunction(path) { + path.skip(); + }, Identifier(id) { const fn = fnNames.get(id.node.name); // We're not tracking this identifier. diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-with-overloads.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-with-overloads.expect.md new file mode 100644 index 0000000000..611e90dd54 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-with-overloads.expect.md @@ -0,0 +1,59 @@ + +## Input + +```javascript +// @gating +import {useState} from 'react'; + +type Session = {name: string}; + +export function useSession(): Session | null; +export function useSession(selector: (session: Session) => T): T | null; +export function useSession( + selector?: (session: Session) => T +): Session | T | null { + const [session] = useState(null); + if (!session) return null; + return selector ? selector(session) : session; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +import { isForgetEnabled_Fixtures } from "ReactForgetFeatureFlag"; // @gating +import { useState } from "react"; + +type Session = { name: string }; + +export function useSession(): Session | null; +export function useSession(selector: (session: Session) => T): T | null; +export const useSession = isForgetEnabled_Fixtures() + ? function useSession(selector) { + const $ = _c(3); + + const [session] = useState(null); + if (!session) { + return null; + } + let t0; + if ($[0] !== selector || $[1] !== session) { + t0 = selector ? selector(session) : session; + $[0] = selector; + $[1] = session; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; + } + : function useSession(selector?: (session: Session) => T) { + const [session] = useState(null); + if (!session) return null; + return selector ? selector(session) : session; + }; + +``` + diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-with-overloads.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-with-overloads.tsx new file mode 100644 index 0000000000..1ceeb35f57 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/gating/gating-test-export-function-with-overloads.tsx @@ -0,0 +1,14 @@ +// @gating +import {useState} from 'react'; + +type Session = {name: string}; + +export function useSession(): Session | null; +export function useSession(selector: (session: Session) => T): T | null; +export function useSession( + selector?: (session: Session) => T, +): Session | T | null { + const [session] = useState(null); + if (!session) return null; + return selector ? selector(session) : session; +}