Repository: facebook/react
Pull request: [compiler] Check if local identifier is a hook when resolving globals

When resolving import specifiers from the react namespace (`import {imported as local} from 'react'`), we were previously only checking if the `imported` identifier was a hook if we didn't already have its definition in the global registry. We also need to check if `local` is a hook in the case of aliasing since there may be hook-like APIs in react that don't start with `use` (eg they are experimental or unstable).

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31384).
* #31385
* __->__ #31384
* #31383

Changed files:
filename: compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
patch: @@ -852,7 +852,9 @@ export class Environment {
            */
           return (
             this.#globals.get(binding.imported) ??
-            (isHookName(binding.imported) ? this.#getCustomHookType() : null)
+            (isHookName(binding.imported) || isHookName(binding.name)
+              ? this.#getCustomHookType()
+              : null)
           );
         } else {
           const moduleType = this.#resolveModuleType(binding.module, loc);

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/import-as-local.expect.md
patch: @@ -0,0 +1,140 @@
+
+## Input
+
+```javascript
+import {
+  useEffect,
+  useRef,
+  // @ts-expect-error
+  experimental_useEffectEvent as useEffectEvent,
+} from 'react';
+
+let id = 0;
+function uniqueId() {
+  'use no memo';
+  return id++;
+}
+
+export function useCustomHook(src: string): void {
+  const uidRef = useRef(uniqueId());
+  const destroyed = useRef(false);
+  const getItem = (srcName, uid) => {
+    return {srcName, uid};
+  };
+
+  const getItemEvent = useEffectEvent(() => {
+    if (destroyed.current) return;
+
+    getItem(src, uidRef.current);
+  });
+
+  useEffect(() => {
+    destroyed.current = false;
+    getItemEvent();
+  }, []);
+}
+
+function Component() {
+  useCustomHook('hello');
+  return <div>Hello</div>;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  isComponent: true,
+  params: [{x: 1}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+import {
+  useEffect,
+  useRef,
+  // @ts-expect-error
+  experimental_useEffectEvent as useEffectEvent,
+} from "react";
+
+let id = 0;
+function uniqueId() {
+  "use no memo";
+  return id++;
+}
+
+export function useCustomHook(src) {
+  const $ = _c(6);
+  let t0;
+  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
+    t0 = uniqueId();
+    $[0] = t0;
+  } else {
+    t0 = $[0];
+  }
+  const uidRef = useRef(t0);
+  const destroyed = useRef(false);
+  const getItem = _temp;
+  let t1;
+  if ($[1] !== src) {
+    t1 = () => {
+      if (destroyed.current) {
+        return;
+      }
+
+      getItem(src, uidRef.current);
+    };
+    $[1] = src;
+    $[2] = t1;
+  } else {
+    t1 = $[2];
+  }
+  const getItemEvent = useEffectEvent(t1);
+  let t2;
+  if ($[3] !== getItemEvent) {
+    t2 = () => {
+      destroyed.current = false;
+      getItemEvent();
+    };
+    $[3] = getItemEvent;
+    $[4] = t2;
+  } else {
+    t2 = $[4];
+  }
+  let t3;
+  if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
+    t3 = [];
+    $[5] = t3;
+  } else {
+    t3 = $[5];
+  }
+  useEffect(t2, t3);
+}
+function _temp(srcName, uid) {
+  return { srcName, uid };
+}
+
+function Component() {
+  const $ = _c(1);
+  useCustomHook("hello");
+  let t0;
+  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
+    t0 = <div>Hello</div>;
+    $[0] = t0;
+  } else {
+    t0 = $[0];
+  }
+  return t0;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  isComponent: true,
+  params: [{ x: 1 }],
+};
+
+```
+      
+### Eval output
+(kind: exception) (0 , _react.experimental_useEffectEvent) is not a function
\ No newline at end of file

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/import-as-local.tsx
patch: @@ -0,0 +1,42 @@
+import {
+  useEffect,
+  useRef,
+  // @ts-expect-error
+  experimental_useEffectEvent as useEffectEvent,
+} from 'react';
+
+let id = 0;
+function uniqueId() {
+  'use no memo';
+  return id++;
+}
+
+export function useCustomHook(src: string): void {
+  const uidRef = useRef(uniqueId());
+  const destroyed = useRef(false);
+  const getItem = (srcName, uid) => {
+    return {srcName, uid};
+  };
+
+  const getItemEvent = useEffectEvent(() => {
+    if (destroyed.current) return;
+
+    getItem(src, uidRef.current);
+  });
+
+  useEffect(() => {
+    destroyed.current = false;
+    getItemEvent();
+  }, []);
+}
+
+function Component() {
+  useCustomHook('hello');
+  return <div>Hello</div>;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  isComponent: true,
+  params: [{x: 1}],
+};