Skip to content

Conversation

@mdevils
Copy link
Contributor

@mdevils mdevils commented Jan 14, 2026

Summary

A number of fixes (some of them cross-dependent) for useExhaustiveDependencies:

Fixed #7982:
useExhaustiveDependencies now properly handles callback expressions with type
assertions.

const callback = useCallback(
  (() => {
    return count * 2;
  }) as Function,
  [count], // count is now correctly detected
);

Fixed #3512:
useExhaustiveDependencies now properly handles nested destructuring patterns
from hook results.

const [[x, y], setXY] = useState([1, 2]);
useEffect(() => {
  console.log(x, y);
}, [x, y]); // x and y are now correctly recognized as unstable

Fixed #3685:
useExhaustiveDependencies now properly handles transparent expression
wrappers like non-null assertions and type assertions in dependency comparisons.

useMemo(() => Boolean(myObj!.x), [myObj!.x]); // No longer reports incorrect diagnostics
useMemo(() => myObj!.x?.y === true, [myObj!.x?.y]); // Now correctly matches dependencies

Fixed #5914:
useExhaustiveDependencies now properly handles variables declared in the same
statement.

const varA = Math.random(),
  varB = useMemo(() => varA, [varA]); // varA is now correctly recognized as needed

Fixed #8427:
useExhaustiveDependencies now properly resolves variable references to detect
captured dependencies.

const fe = fetchEntity;
useEffect(() => {
  fe(id);
}, [id, fe]); // fe is now correctly detected as needed

Fixed #8484:
useExhaustiveDependencies now properly handles member access on stable hook
results.

const stableObj = useStable();
useMemo(() => {
  return stableObj.stableValue; // stableObj.stableValue is now correctly recognized as stable
}, []);

Fixed #3080:
useExhaustiveDependencies now properly analyzes captures within referenced
functions passed to hooks.

function myEffect() {
  console.log(foo, bar);
}
useEffect(myEffect, [foo, bar]); // foo and bar are now correctly detected

Fixed #4248:
useExhaustiveDependencies now correctly handles function props passed as
callbacks.

const data = React.useMemo(getData, [getData]); // getData is now correctly recognized as needed

Test Plan

Tests are included.

Docs

Changeset is included.

@changeset-bot
Copy link

changeset-bot bot commented Jan 14, 2026

🦋 Changeset detected

Latest commit: 96a5437

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Linter Area: linter A-Parser Area: parser L-JavaScript Language: JavaScript and super languages labels Jan 14, 2026
@mdevils mdevils changed the title Fixes for useExhaustiveDependencies Jan 14, 2026
@mdevils mdevils changed the title fix(linter): useExhaustiveDependencies multiple fixes (closes #3080, closes #4248, closes #5914, closes #8427, closes #8484, closes #3685, closes #3512, closes #7982) Jan 14, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

Walkthrough

Refactors the useExhaustiveDependencies lint rule to improve dependency detection. Adds an expression-candidate abstraction, deeper expression unwrapping, and stability-checking helpers with recursion limits. Introduces functions to collect candidate expressions and resolve relevant captures (including from inlined closures and identifiers), replaces capture-based logic with candidate-driven analysis, and updates react hook stability APIs. Adds tests and changelog entries; several internal helpers, enums and public-ish utilities were added or made public to support the new analysis.

Possibly related PRs

Suggested reviewers

  • dyc3
  • arendjr
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main change: multiple fixes to the useExhaustiveDependencies linter rule, with specific issue references.
Description check ✅ Passed The PR description comprehensively documents all fixes with code examples and issue links, clearly explaining the problems and solutions.
Linked Issues check ✅ Passed All code changes directly address the linked issues: type assertions (#7982), nested destructuring (#3512), transparent wrappers (#3685), same-statement variables (#5914), variable references (#8427), stable hook results (#8484), function captures (#3080), and function props (#4248).
Out of Scope Changes check ✅ Passed All changes are within scope: core rule refactoring in use_exhaustive_dependencies.rs, public API updates in hooks.rs and expr_ext.rs, new test cases, and changelog entries.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 338e476 and 96a5437.

📒 Files selected for processing (10)
  • .changeset/famous-ads-dance.md
  • .changeset/forty-sloths-begin.md
  • .changeset/public-doodles-cut.md
  • .changeset/puny-eagles-hang.md
  • .changeset/rare-things-spend.md
  • .changeset/rude-spies-feel.md
  • .changeset/short-chefs-switch.md
  • .changeset/tiny-owls-show.md
  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
  • crates/biome_js_analyze/src/react/hooks.rs
✅ Files skipped from review due to trivial changes (5)
  • .changeset/rare-things-spend.md
  • .changeset/short-chefs-switch.md
  • .changeset/forty-sloths-begin.md
  • .changeset/public-doodles-cut.md
  • .changeset/puny-eagles-hang.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/rude-spies-feel.md
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use inline rustdoc documentation for rules, assists, and their options
Use the dbg!() macro for debugging output in Rust tests and code
Use doc tests (doctest) format with code blocks in rustdoc comments; ensure assertions pass in tests

Files:

  • crates/biome_js_analyze/src/react/hooks.rs
  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
🧠 Learnings (20)
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Avoid string allocations by using `&str` or `TokenText` instead of `to_string()`

Applied to files:

  • crates/biome_js_analyze/src/react/hooks.rs
📚 Learning: 2025-10-03T12:28:56.788Z
Learnt from: ematipico
Repo: biomejs/biome PR: 7670
File: crates/biome_service/src/file_handlers/html.rs:744-748
Timestamp: 2025-10-03T12:28:56.788Z
Learning: In Biome's codebase, when creating tokens with new text content, use the factory pattern with functions like `ident(text)` from the respective `*_factory` crates (e.g., `biome_html_factory::make::ident`). There is no `.with_text()` method on tokens. The `ident()` function creates a new detached token with the IDENT kind, which is the standard approach for token construction.

Applied to files:

  • crates/biome_js_analyze/src/react/hooks.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When formatting AST nodes, use mandatory tokens from the AST instead of hardcoding token strings (e.g., use `node.l_paren_token().format()` instead of `token("(")`)

Applied to files:

  • crates/biome_js_analyze/src/react/hooks.rs
  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Import the `FormatNode` trait and implement it for your Node when creating formatters in biome_js_formatter

Applied to files:

  • crates/biome_js_analyze/src/react/hooks.rs
  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Do not attempt to 'fix' the code; if a token/node is known to be mandatory but is missing, return `None` instead

Applied to files:

  • crates/biome_js_analyze/src/react/hooks.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : For tokens that are not mandatory, use helper functions instead of hardcoding

Applied to files:

  • crates/biome_js_analyze/src/react/hooks.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Use the `Semantic<T>` query type to access semantic information about bindings, references, and scope within a rule

Applied to files:

  • crates/biome_js_analyze/src/react/hooks.rs
  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ConditionalParsedSyntax` for syntax that is only valid in specific contexts (e.g., strict mode, file types, language versions) and call `or_invalid_to_bogus()` to convert to a bogus node if not supported

Applied to files:

  • crates/biome_js_analyze/src/react/hooks.rs
  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Use `declare_node_union!` macro to query multiple node types together to avoid redundant traversal passes

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Check if a variable is global using the semantic model before reporting diagnostics for rules that ban global functions or variables

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2025-12-22T09:26:56.943Z
Learnt from: ematipico
Repo: biomejs/biome PR: 8537
File: crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs:167-210
Timestamp: 2025-12-22T09:26:56.943Z
Learning: When defining lint rules (declare_lint_rule!), only specify fix_kind if the rule implements an action(...) function. Rules that only emit diagnostics without a code fix should omit fix_kind. This applies to all Rust lint rule definitions under crates/.../src/lint (e.g., crates/biome_js_analyze/src/lint/...).

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Deprecated rules must include a `deprecated` field in the `declare_lint_rule!` macro with an explanation of what rule to use instead

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Implement custom `Queryable` and `Visitor` types for rules that require deep inspection of child nodes to avoid inefficient traversals

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/local_inference.rs : Implement local inference in dedicated modules to derive type definitions from expressions without context of surrounding scopes

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Implement the `action` function and add `fix_kind` metadata to the rule macro if the rule provides code actions

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Rule names should use the `use` prefix when the rule's sole intention is to mandate a single concept

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/flattening.rs : Implement type flattening to simplify `TypeofExpression` variants once all component types are resolved

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Define `FormatHtmlSyntaxNode` struct in a `cst.rs` file implementing `FormatRule<HtmlSyntaxNode>`, `AsFormat<HtmlFormatContext>`, and `IntoFormat<HtmlFormatContext>` traits using the provided boilerplate code

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Use `Markup!` macro for diagnostic messages and code action descriptions to ensure proper formatting

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/biome_rule_options/lib/**/*.rs : Rule option types must derive `Deserializable`, `Serialize`, `Deserialize`, and optionally `JsonSchema` traits

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/react/hooks.rs (2)
crates/biome_js_syntax/src/expr_ext.rs (3)
  • member (1526-1531)
  • callee (33-38)
  • new (1385-1387)
crates/biome_js_syntax/src/file_source.rs (1)
  • js_module (170-172)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: Parser conformance
  • GitHub Check: Test Node.js API
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: End-to-end tests
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: Documentation
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: autofix
🔇 Additional comments (18)
.changeset/tiny-owls-show.md (1)

1-11: LGTM!

Clear and concise changelog entry with a helpful code example demonstrating the fix for issue #4248.

crates/biome_js_analyze/src/react/hooks.rs (5)

28-35: LGTM!

Making closure public is appropriate as it's now used externally for capture analysis in the dependency rule.


53-64: LGTM!

The capture filtering logic is sound—correctly identifying variables declared outside the closure but referenced within it.


235-240: LGTM!

Good use of TokenText to avoid string allocations. Based on learnings, this is the preferred approach.


256-291: LGTM!

The stability-checking logic is well-structured. The match on (config.result, member) cleanly handles all the relevant combinations.


335-404: LGTM!

Good test coverage for both named and namespace import patterns. The tests properly exercise the updated API.

crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs (11)

31-32: LGTM!

Sensible recursion guard. A depth of 10 should handle any reasonable hook pattern whilst preventing runaway recursion.


528-530: LGTM!

Appropriate use of declare_node_union! for the candidate abstraction.


532-582: LGTM!

Well-structured traversal logic that correctly builds expression candidate chains whilst handling edge cases like sequence expressions and computed member indices.


584-631: LGTM!

The refactored logic correctly determines dependency requirements by checking all candidate expressions for stability.


633-763: LGTM!

Comprehensive stability analysis covering all binding declaration types. The depth guard and pattern-member handling are well implemented.


765-877: LGTM!

The expression stability analysis handles all major expression types correctly. The self-reference check at lines 852-860 is a nice touch to prevent false positives.


879-970: LGTM!

Thorough pattern-member extraction with proper handling of array indices, object keys, and deeply nested patterns. The index calculation accounting for separators is correct.


1095-1159: LGTM!

This is the heart of the fix for #3080 and #4248. The logic correctly resolves captures from referenced functions and handles function props as dependencies.


1057-1061: LGTM!

Using inner_expression() ensures transparent wrappers don't break member chain comparison—key for fix #3685.


1203-1223: LGTM!

Clean integration of the new capture resolution pipeline. Using the longest expression candidate ensures accurate dependency matching.


10-26: LGTM!

All new imports are properly utilised by the refactored code.

.changeset/famous-ads-dance.md (1)

1-14: LGTM!

Clear changelog entry with a good example demonstrating nested destructuring handling for issue #3512.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codspeed-hq
Copy link

codspeed-hq bot commented Jan 14, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 95 skipped benchmarks1


Comparing mdevils:fixes/use-exhaustive-dependencies (96a5437) with main (f244526)2

Open in CodSpeed

Footnotes

  1. 95 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (39eb545) during the generation of this report, so f244526 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Copy link
Contributor

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very comprehensive! Just a couple notes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter A-Parser Area: parser L-JavaScript Language: JavaScript and super languages

2 participants