Repository: facebook/react
File: compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts (TypeScript)

Code before:
      }
      CompilerError.simpleInvariant(inferredDependency.kind === 'Local', {
        reason: 'Unexpected function dependency',
        loc: value.loc,
      });
      const isRequiredDependency = reactive.has(
        inferredDependency.identifier.id,
      );
      let hasMatchingManualDependency = false;
      for (const manualDependency of manualDependencies) {
        if (
          manualDependency.root.kind === 'NamedLocal' &&
          manualDependency.root.value.identifier.id ===
            inferredDependency.identifier.id &&
          (areEqualPaths(manualDependency.path, inferredDependency.path) ||
            isSubPathIgnoringOptionals(
              manualDependency.path,
              inferredDependency.path,
            ))
        ) {
          hasMatchingManualDependency = true;
          matched.add(manualDependency);
          if (!isRequiredDependency) {
            extra.push(manualDependency);
          }
        }
      }
      if (isRequiredDependency && !hasMatchingManualDependency) {
        missing.push(inferredDependency);
      }
    }

    for (const dep of startMemo.deps ?? []) {
      if (matched.has(dep)) {
        continue;
      }
      extra.push(dep);
    }

    /**
     * Per docblock, we only consider dependencies as extraneous if
     * they are unused globals or reactive locals. Notably, this allows
     * non-reactive locals.
     */
    retainWhere(extra, dep => {
      return dep.root.kind === 'Global' || dep.root.value.reactive;
    });

    if (missing.length !== 0 || extra.length !== 0) {
      let suggestions: Array<CompilerSuggestion> | null = null;
      if (startMemo.depsLoc != null && typeof startMemo.depsLoc !== 'symbol') {

Review comment (suggestion):
Nice, this switches us back to special casing non-reactive deps allowed by `exhaustive-deps` rule.

This code should now bail out (right)?
```js
hook useHook() {
  const obj = {};
  return useMemo(() => obj, []);
}
```

Code after:
      CompilerError.simpleInvariant(inferredDependency.kind === 'Local', {
        reason: 'Unexpected function dependency',
        loc: value.loc,
      });
      let hasMatchingManualDependency = false;
      for (const manualDependency of manualDependencies) {
        if (
          manualDependency.root.kind === 'NamedLocal' &&
          manualDependency.root.value.identifier.id ===
            inferredDependency.identifier.id &&
          (areEqualPaths(manualDependency.path, inferredDependency.path) ||
            isSubPathIgnoringOptionals(
              manualDependency.path,
              inferredDependency.path,
            ))
        ) {
          hasMatchingManualDependency = true;
          matched.add(manualDependency);
        }
      }
      const isOptionalDependency =
        !reactive.has(inferredDependency.identifier.id) &&
        (isStableType(inferredDependency.identifier) ||
          isPrimitiveType(inferredDependency.identifier));
      if (hasMatchingManualDependency || isOptionalDependency) {
        continue;
      }
      missing.push(inferredDependency);
    }

    for (const dep of startMemo.deps ?? []) {
      if (matched.has(dep)) {
        continue;
      }
      if (dep.root.kind === 'NamedLocal' && dep.root.constant) {
        CompilerError.simpleInvariant(
          !dep.root.value.reactive &&
            isPrimitiveType(dep.root.value.identifier),
          {
            reason: 'Expected constant-folded dependency to be non-reactive',
            loc: dep.root.value.loc,
          },
        );
        /*
         * Constant primitives can get constant-folded, which means we won't
         * see a LoadLocal for the value within the memo function.
         */
        continue;
      }
      extra.push(dep);
    }