Skip to content

fix: jsx dependency detection in useexhaustivedependencies (#8917)#8930

Merged
ematipico merged 17 commits intobiomejs:mainfrom
ANKANJAGTAP:main
Feb 5, 2026
Merged

fix: jsx dependency detection in useexhaustivedependencies (#8917)#8930
ematipico merged 17 commits intobiomejs:mainfrom
ANKANJAGTAP:main

Conversation

@ANKANJAGTAP
Copy link
Contributor

Summary
Fix lint/correctness/useExhaustiveDependencies not detecting JSX component identifiers (e.g. ) as dependencies inside React hooks.
Previously, JSX component bindings were ignored, which could lead to missing dependency warnings and stale closures.

Root Cause:
get_expression_candidates did not handle JsxReferenceIdentifier nodes.

Changes:
Added JsxReferenceIdentifier to AnyExpressionCandidate.
Updated get_expression_candidates to return JSX identifiers.
Updated capture_needs_to_be_in_the_dependency_list to check JSX bindings via the semantic model.

Result:
Locally defined components used in JSX inside hooks are now correctly flagged as dependencies.

Test Plan
Added test where inside useCallback without [Sub] triggers a warning.
Verified no warning when [Sub] is included.
Confirmed no regressions in existing dependency checks.

Docs
Updated rule docs with JSX dependency example.

@changeset-bot
Copy link

changeset-bot bot commented Feb 1, 2026

🦋 Changeset detected

Latest commit: 6c0cdf2

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 L-JavaScript Language: JavaScript and super languages labels Feb 1, 2026
@ANKANJAGTAP ANKANJAGTAP changed the title Fixed-#8917 fix: JSX dependency detection in useExhaustiveDependencies (#8917) Feb 1, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 1, 2026

Walkthrough

Adds JSX identifier handling to the use_exhaustive_dependencies lint: JsxReferenceIdentifier is added to AnyExpressionCandidate; expression candidate extraction and dependency-capture logic now recognise JSX refs and consult the semantic model (defaulting to captured if binding is missing); stability checks for constant expressions are tightened; autofix construction transforms JSX reference identifiers into identifier expressions; multiple JSX-focused tests and a changeset were added.

Possibly related PRs

Suggested reviewers

  • ematipico
  • mdevils
  • dyc3
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarises the main change: fixing JSX component identifier detection in useExhaustiveDependencies lint rule.
Description check ✅ Passed The description provides clear, contextual information about the bug fix, root cause, changes made, testing approach, and expected results.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs`:
- Around line 668-681: The AddDependency autofix currently only handles
JsReferenceIdentifier/AnyJsExpression and ignores
AnyExpressionCandidate::JsxReferenceIdentifier, causing no-op fixes for JSX
captures; update the Fix::AddDependency handling to add a branch for
AnyExpressionCandidate::JsxReferenceIdentifier that extracts the JSX
identifier's name token (e.g., reference_identifier.name_token or equivalent),
converts it into a plain identifier expression (same shape used for
JsReferenceIdentifier/AnyJsExpression), and insert that converted identifier
into the dependency list; apply the same change to the duplicate handling block
around the second occurrence (lines referenced 1575-1589) so both places convert
JSX name tokens before creating the AddDependency fix.

Comment on lines +668 to +681
AnyExpressionCandidate::JsxReferenceIdentifier(reference_identifier) => {
if let Some(binding) = model.binding(reference_identifier) {
is_stable_binding(
&binding.tree(),
None,
component_function_range,
model,
options,
0,
)
} else {
true
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Autofix won’t add JSX dependencies.

With JsxReferenceIdentifier now producing missing-dependency diagnostics, the Fix::AddDependency action still only converts JsReferenceIdentifier/AnyJsExpression. JSX captures will therefore produce a no‑op fix. Please add a JSX branch that converts the JSX identifier’s name token into a normal identifier expression before insertion.

Also applies to: 1575-1589

🤖 Prompt for AI Agents
In `@crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs`
around lines 668 - 681, The AddDependency autofix currently only handles
JsReferenceIdentifier/AnyJsExpression and ignores
AnyExpressionCandidate::JsxReferenceIdentifier, causing no-op fixes for JSX
captures; update the Fix::AddDependency handling to add a branch for
AnyExpressionCandidate::JsxReferenceIdentifier that extracts the JSX
identifier's name token (e.g., reference_identifier.name_token or equivalent),
converts it into a plain identifier expression (same shape used for
JsReferenceIdentifier/AnyJsExpression), and insert that converted identifier
into the dependency list; apply the same change to the duplicate handling block
around the second occurrence (lines referenced 1575-1589) so both places convert
JSX name tokens before creating the AddDependency fix.

@ANKANJAGTAP ANKANJAGTAP changed the title fix: JSX dependency detection in useExhaustiveDependencies (#8917) fix: jsx dependency detection in useexhaustivedependencies (#8917) Feb 1, 2026
@codspeed-hq
Copy link

codspeed-hq bot commented Feb 1, 2026

CodSpeed Performance Report

Merging this PR will not alter performance

Comparing ANKANJAGTAP:main (6c0cdf2) with main (db0da5c)

Summary

✅ 58 untouched benchmarks
⏩ 95 skipped benchmarks1

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs`:
- Around line 545-554: Remove the panic call in get_expression_candidates that
triggers on JsxReferenceIdentifier (the panic!("DEBUG: Found likely
JsxReferenceIdentifier: {:?}", jsx_ref)); instead drop the panic and either log
non-fatal debug information with dbg!(jsx_ref) if you need runtime debug output
or simply proceed to push the candidate as originally intended by calling
result.push(AnyExpressionCandidate::JsxReferenceIdentifier(jsx_ref.clone())) and
returning result; ensure no panics remain in get_expression_candidates or other
lint paths so JSX identifiers do not crash the linter.

@mdevils mdevils self-requested a review February 3, 2026 09:49
Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

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

Please add a changeset @ANKANJAGTAP

@ANKANJAGTAP ANKANJAGTAP requested a review from ematipico February 3, 2026 12:42
"@biomejs/biome": patch
---

Fix `useExhaustiveDependencies` so JSX component identifiers (e.g. `<Sub />`) are detected as hook dependencies and included in autofix suggestions.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Fix `useExhaustiveDependencies` so JSX component identifiers (e.g. `<Sub />`) are detected as hook dependencies and included in autofix suggestions.
Fixed [#8917](https://github.com/biomejs/biome/issues/8917), [`useExhaustiveDependencies`](https://biomejs.dev/linter/rules/use-exhaustive-dependencies/) now correctly detects JSX component identifiers as hook dependencies.

perhaps?

@Netail
Copy link
Member

Netail commented Feb 3, 2026

Tests seem to fail

@ematipico
Copy link
Member

@ANKANJAGTAP please run the tests yourself and confirm everything passes

@ANKANJAGTAP
Copy link
Contributor Author

I’ve run cargo test -p biome_js_analyze locally on nightly and all tests pass. I also ran the full test suite and didn’t see any failures (only expected ignored tests).

@mdevils
Copy link
Contributor

mdevils commented Feb 4, 2026

@ANKANJAGTAP please don't forget checking our CONTRIBUTION guide. Specifically this: https://github.com/biomejs/biome/blob/main/crates/biome_analyze/CONTRIBUTING.md

Testing a rule: just test-lintrule myRuleName

In your case it would be just test-lintrule useExhaustiveDependencies

@ANKANJAGTAP
Copy link
Contributor Author

I ran cargo test -p biome_js_analyze and the full test suite locally on nightly and all tests pass.

I attempted to run just test-lintrule useExhaustiveDependencies, but the repo’s justfile uses the working-directory attribute which is not supported by the latest released just (v1.46.0).

Happy to rerun if there’s a recommended just version, but analyzer and snapshot tests pass locally.

@ematipico
Copy link
Member

but the repo’s justfile uses the working-directory attribute which is not supported by the latest released just (v1.46.0).

Din't know that. We will have to fix it

Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

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

Please create valid tests that make sure that adding the unsafe fix result in the rule not being triggered. To be more clear, the new diagnostics show, as unsafe fix, to add the JSX component to the array, hence we need to create fixtures with that code already applied and make sure no diagnostics are emitted. If they are, it means the fix isn't correct.

Add // should not generate diagnostics to the new file

@@ -0,0 +1,29 @@
import { getSubComponent } from "@external"
Copy link
Member

Choose a reason for hiding this comment

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

Please add // should generate diagnostics at the top of the file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a fixture covering the post-fix state and approved the snapshot.
All tests pass locally.

Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

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

Looks good. Waiting for @mdevils review

@mdevils
Copy link
Contributor

mdevils commented Feb 4, 2026

@ANKANJAGTAP thank you for making all the fixes. Could you please also add tests with a member-access in the JSX tag? I.e. <MyNS.MyComponent ... />

@ANKANJAGTAP
Copy link
Contributor Author

@ANKANJAGTAP thank you for making all the fixes. Could you please also add tests with a member-access in the JSX tag? I.e. <MyNS.MyComponent ... />

Added tests covering JSX member access (<MyNS.MyComponent />), including both diagnostic and no-diagnostic cases. All tests pass locally.

@ematipico
Copy link
Member

@ANKANJAGTAP tests are failing

@ANKANJAGTAP
Copy link
Contributor Author

Please give me some time; I will fix it.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/jsxMemberDependenciesFixed.jsx`:
- Line 10: The dependency array for the useCallback that defines render is
incorrect: update the useCallback call (function name render) so the dependency
array includes the namespace MyNS (i.e., change the empty array to include MyNS)
so the exhaustive-deps rule is satisfied for the JSX expression
<MyNS.MyComponent /> referenced inside the callback; ensure the identifier
referenced matches MyNS used in the JSXMember expression.

};

export function Component() {
const render = useCallback(() => <MyNS.MyComponent />, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Dependency array appears incorrect for a "fixed" test case.

The comment on line 1 states this should not generate diagnostics, yet the dependency array is empty []. According to the PR objectives and comparing with jsxMemberDependencies.jsx (which is identical and should trigger a warning), the fixed version ought to include [MyNS] to satisfy the exhaustive dependencies rule.

🐛 Proposed fix
-  const render = useCallback(() => <MyNS.MyComponent />, []);
+  const render = useCallback(() => <MyNS.MyComponent />, [MyNS]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const render = useCallback(() => <MyNS.MyComponent />, []);
const render = useCallback(() => <MyNS.MyComponent />, [MyNS]);
🤖 Prompt for AI Agents
In
`@crates/biome_js_analyze/tests/specs/correctness/useExhaustiveDependencies/jsxMemberDependenciesFixed.jsx`
at line 10, The dependency array for the useCallback that defines render is
incorrect: update the useCallback call (function name render) so the dependency
array includes the namespace MyNS (i.e., change the empty array to include MyNS)
so the exhaustive-deps rule is satisfied for the JSX expression
<MyNS.MyComponent /> referenced inside the callback; ensure the identifier
referenced matches MyNS used in the JSXMember expression.

@ANKANJAGTAP
Copy link
Contributor Author

All requested changes have been addressed and tests are passing locally.
Happy to make any further adjustments if needed. Thanks!

@ematipico ematipico merged commit 51c158e into biomejs:main Feb 5, 2026
18 checks passed
@github-actions github-actions bot mentioned this pull request Feb 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants