From 290174c38fa6e8ed99d58f2c8c988c03b31d04e7 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Mon, 24 Nov 2025 12:19:53 -0800 Subject: [PATCH] [compiler] Adjustments to exhaustive deps messages, disable the lint rule Similar to ValidateHookUsage, we implement this check in the compiler for safety but (for now) continue to rely on the existing rule for actually reporting errors to users. --- .../babel-plugin-react-compiler/src/CompilerError.ts | 10 +++++++++- .../src/Entrypoint/Pipeline.ts | 8 +++++--- .../src/Validation/ValidateExhaustiveDependencies.ts | 4 ++-- .../compiler/error.invalid-exhaustive-deps.expect.md | 8 ++++---- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts index 352d31b2f897..c7c8d6a161e9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts @@ -1067,7 +1067,15 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule { name: 'memo-dependencies', description: 'Validates that useMemo() and useCallback() specify comprehensive dependencies without extraneous values. See [`useMemo()` docs](https://react.dev/reference/react/useMemo) for more information.', - preset: LintRulePreset.RecommendedLatest, + /** + * TODO: the "MemoDependencies" rule largely reimplements the "exhaustive-deps" non-compiler rule, + * allowing the compiler to ensure it does not regress change behavior due to different dependencies. + * We previously relied on the source having ESLint suppressions for any exhaustive-deps violations, + * but it's more reliable to verify it within the compiler. + * + * Long-term we should de-duplicate these implementations. + */ + preset: LintRulePreset.Off, }; } case ErrorCategory.IncompatibleLibrary: { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index 3d97d4ddf046..1b76dfb43e14 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -303,9 +303,11 @@ function runWithEnvironment( inferReactivePlaces(hir); log({kind: 'hir', name: 'InferReactivePlaces', value: hir}); - if (env.config.validateExhaustiveMemoizationDependencies) { - // NOTE: this relies on reactivity inference running first - validateExhaustiveDependencies(hir).unwrap(); + if (env.enableValidations) { + if (env.config.validateExhaustiveMemoizationDependencies) { + // NOTE: this relies on reactivity inference running first + validateExhaustiveDependencies(hir).unwrap(); + } } rewriteInstructionKindsBasedOnReassignment(hir); diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts index d5f9ab293235..b3a68fc0134b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts @@ -284,7 +284,7 @@ export function validateExhaustiveDependencies( if (missing.length !== 0) { const diagnostic = CompilerDiagnostic.create({ category: ErrorCategory.MemoDependencies, - reason: 'Found non-exhaustive dependencies', + reason: 'Found missing memoization dependencies', description: 'Missing dependencies can cause a value not to update when those inputs change, ' + 'resulting in stale UI', @@ -309,7 +309,7 @@ export function validateExhaustiveDependencies( reason: 'Found unnecessary memoization dependencies', description: 'Unnecessary dependencies can cause a value to update more often than necessary, ' + - 'which can cause effects to run more than expected', + 'causing performance regressions and effects to fire more often than expected', }); diagnostic.withDetails({ kind: 'error', diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-exhaustive-deps.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-exhaustive-deps.expect.md index bca95a84e1ec..ed317be118e1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-exhaustive-deps.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-exhaustive-deps.expect.md @@ -53,7 +53,7 @@ function Component({x, y, z}) { ``` Found 4 errors: -Error: Found non-exhaustive dependencies +Error: Found missing memoization dependencies Missing dependencies can cause a value not to update when those inputs change, resulting in stale UI. @@ -66,7 +66,7 @@ error.invalid-exhaustive-deps.ts:7:11 9 | }, [x?.y.z?.a.b]); 10 | const b = useMemo(() => { -Error: Found non-exhaustive dependencies +Error: Found missing memoization dependencies Missing dependencies can cause a value not to update when those inputs change, resulting in stale UI. @@ -81,7 +81,7 @@ error.invalid-exhaustive-deps.ts:15:11 Error: Found unnecessary memoization dependencies -Unnecessary dependencies can cause a value to update more often than necessary, which can cause effects to run more than expected. +Unnecessary dependencies can cause a value to update more often than necessary, causing performance regressions and effects to fire more often than expected. error.invalid-exhaustive-deps.ts:31:5 29 | return []; @@ -92,7 +92,7 @@ error.invalid-exhaustive-deps.ts:31:5 33 | const ref2 = useRef(null); 34 | const ref = z ? ref1 : ref2; -Error: Found non-exhaustive dependencies +Error: Found missing memoization dependencies Missing dependencies can cause a value not to update when those inputs change, resulting in stale UI.