Skip to content

Conversation

@ematipico
Copy link
Member

Summary

Part of #6611

This PR improves the inference engine by updating the type of those bindings that are assigned to multiple values.

The implementation is quite naive for now, so if there more than one assignments, we create new types for each one of them. You can see it from the snapshots.

cc @arendjr

Test Plan

Added new tests

Docs

@changeset-bot
Copy link

changeset-bot bot commented Nov 16, 2025

🦋 Changeset detected

Latest commit: e0955f4

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

@ematipico ematipico requested review from a team and arendjr November 16, 2025 13:33
@github-actions github-actions bot added A-Project Area: project A-Linter Area: linter L-JavaScript Language: JavaScript and super languages A-Type-Inference Area: type inference labels Nov 16, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 16, 2025

Walkthrough

Refactors module graph collector to use binding data, detect writable references, and widen inferred types by unioning RHS assignment types. Adds tests exercising widening from single and multiple assignments and a TypeScript test ensuring no diagnostics for reassigned variables checked in conditions. Adds a public union_with method to the TypeResolver trait for building union types. Updates the noUnnecessaryConditions rule to be marked as "inspired" and to avoid flagging variables mutated inside a module. Several changeset files added for patch releases.

Suggested labels

A-Resolver

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: improving type inference for bindings assigned multiple values through modifications to the inference engine.
Description check ✅ Passed The description clearly relates to the changeset, explaining the motivation (improving inference for bindings assigned to multiple values) and noting the naive implementation approach.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/inference-assignments

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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

🧹 Nitpick comments (4)
crates/biome_js_type_info/src/resolver.rs (1)

725-731: union_with helper is fine, but note the “naive” union growth

The implementation matches optional and does what the PR describes: it always creates a fresh two‑member Union from current_type and ty and registers it.

That’s perfectly OK for the current “naive” widening, but it does mean repeated widening will keep allocating new union types without flattening or deduplication. If unions ever start to get large or performance becomes an issue, this might be a good place to introduce flattening/reuse.

.changeset/upset-cameras-walk.md (1)

5-5: Tiny wording tweak (optional)

Pure style, but this reads a bit smoother without the comma:

-Improved the type inference engine, by resolving types for variables that are assigned to multiple values.
+Improved the type inference engine by resolving types for variables that are assigned to multiple values.
crates/biome_module_graph/tests/spec_tests.rs (1)

2132-2163: New widening snapshot test is consistent and focused

This is a clean minimal case for “boolean literal + reassignment” widening; setup mirrors other snapshot tests and the relative "index.ts" path is used consistently, so behaviour should be stable.

If you want to align style with nearby tests, you could optionally write:

let snapshot = ModuleGraphSnapshot::new(module_graph.as_ref(), &fs).with_resolver(resolver.as_ref());

but the current &module_graph form is perfectly fine thanks to deref‑coercion.

crates/biome_module_graph/src/js_module_info/collector.rs (1)

609-648: Code duplication in the if-else branches.

Lines 613–616 and 643–648 perform identical lookups from typed_bindings. The else branch is redundant; you can simplify by initialising ty once (lines 613–616), applying the writable-reference union conditionally, then returning ty at the end.

Apply this refactor to eliminate duplication:

-                let ty = if let Some(typed_bindings) = decl
+                let mut ty = if let Some(typed_bindings) = decl
                     .as_js_variable_declaration()
                     .and_then(|decl| self.variable_declarations.get(decl.syntax()))
                 {
-                    let mut ty = typed_bindings
+                    typed_bindings
                         .iter()
                         .find_map(|(name, ty)| (name == binding_name).then(|| ty.clone()))
-                        .unwrap_or_default();
-
+                        .unwrap_or_default()
+                } else {
+                    let data = TypeData::from_any_js_declaration(self, scope_id, &decl);
+                    self.reference_to_owned_data(data)
+                };
+
                     if self.has_writable_reference(&binding) {
                         let references = self.get_writable_references(&binding);
                         for reference in references {
                             let Some(node) = self.binding_node_by_start.get(&reference.range_start)
                             else {
                                 continue;
                             };
                             for ancestor in node.ancestors().skip(1) {
                                 if let Some(assignment) =
                                     JsAssignmentExpression::cast_ref(&ancestor)
                                     && let Ok(right) = assignment.right()
                                 {
                                     let data =
                                         TypeData::from_any_js_expression(self, scope_id, &right);
                                     let assigned_type = self.reference_to_owned_data(data);
                                     ty = ResolvedTypeId::new(
                                         self.level(),
                                         self.union_with(ty.clone(), assigned_type).into(),
                                     )
                                     .into();
                                 }
                             }
                         }
-
-                        ty
-                    } else {
-                        typed_bindings
-                            .iter()
-                            .find_map(|(name, ty)| (name == binding_name).then(|| ty.clone()))
-                            .unwrap_or_default()
                     }
-                } else {
-                    let data = TypeData::from_any_js_declaration(self, scope_id, &decl);
-                    self.reference_to_owned_data(data)
-                };
📜 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 47d940e and 06c85ff.

⛔ Files ignored due to path filters (2)
  • crates/biome_js_analyze/tests/specs/nursery/noUnnecessaryConditions/validAssignment.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_module_graph/tests/snapshots/test_widening_via_assignment.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (7)
  • .changeset/lovely-sloths-chew.md (1 hunks)
  • .changeset/upset-cameras-walk.md (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noUnnecessaryConditions/validAssignment.ts (1 hunks)
  • crates/biome_js_type_info/src/resolver.rs (1 hunks)
  • crates/biome_module_graph/src/js_module_info/collector.rs (2 hunks)
  • crates/biome_module_graph/tests/snapshots/test_widening_via_assignment_multiple_values.snap.new (1 hunks)
  • crates/biome_module_graph/tests/spec_tests.rs (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-20T16:24:59.781Z
Learnt from: arendjr
Repo: biomejs/biome PR: 7266
File: crates/biome_js_type_info/src/type.rs:94-102
Timestamp: 2025-08-20T16:24:59.781Z
Learning: In crates/biome_js_type_info/src/type.rs, the flattened_union_variants() method returns TypeReference instances that already have the correct module IDs applied to them. These references should be used directly with resolver.resolve_reference() without applying additional module ID transformations, as variant references may originate from nested unions in different modules.

Applied to files:

  • crates/biome_js_type_info/src/resolver.rs
  • crates/biome_module_graph/src/js_module_info/collector.rs
  • crates/biome_module_graph/tests/snapshots/test_widening_via_assignment_multiple_values.snap.new
🧬 Code graph analysis (2)
crates/biome_module_graph/tests/spec_tests.rs (3)
crates/biome_fs/src/fs/memory.rs (1)
  • default (37-49)
crates/biome_test_utils/src/lib.rs (1)
  • get_added_paths (169-190)
crates/biome_module_graph/src/js_module_info/module_resolver.rs (1)
  • for_module (76-91)
crates/biome_module_graph/src/js_module_info/collector.rs (3)
crates/biome_js_type_info/src/type_data.rs (8)
  • ty (545-550)
  • index (45-47)
  • index (1431-1433)
  • index (1472-1476)
  • reference (343-345)
  • new (39-43)
  • new (1425-1429)
  • new (1460-1470)
crates/biome_module_graph/src/js_module_info.rs (1)
  • binding (264-266)
crates/biome_js_type_info/src/local_inference.rs (3)
  • from_any_js_expression (425-602)
  • from_any_js_expression (2173-2180)
  • from_any_js_expression (2343-2425)
🪛 LanguageTool
.changeset/lovely-sloths-chew.md

[uncategorized] ~5-~5: Possible missing comma found.
Context: ... of the rule noUnnecessaryConditions. Now the rule doesn't isn't triggered for va...

(AI_HYDRA_LEO_MISSING_COMMA)


[grammar] ~5-~5: Two consecutive contractions are very uncommon. Did you maybe just mean “doesn't” or “isn't”?
Context: ...noUnnecessaryConditions. Now the rule doesn't isn't triggered for variables that are mutate...

(DON_T_AREN_T)

⏰ 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). (12)
  • GitHub Check: autofix
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: Test Node.js API
  • GitHub Check: Documentation
  • 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: Lint project (depot-windows-2022)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_parser)
🔇 Additional comments (7)
crates/biome_js_analyze/tests/specs/nursery/noUnnecessaryConditions/validAssignment.ts (1)

1-9: Targeted regression fixture looks good

This neatly captures the “mutated in the same module before the condition” case for noUnnecessaryConditions. No changes needed from my side.

crates/biome_module_graph/tests/spec_tests.rs (1)

2165-2200: Good coverage for multi‑value widening

Nice to see a dedicated case where a binding starts as undefined and is then assigned both a string and a number in separate functions; that should exercise the “multiple assignments → union” path nicely. Snapshot wiring matches the previous test and looks correct.

Nothing I’d change here.

crates/biome_module_graph/tests/snapshots/test_widening_via_assignment_multiple_values.snap.new (1)

1-99: Snapshot looks good!

The progressive type widening from undefinedundefined | stringundefined | string | number correctly reflects the multiple assignment behavior. The structure is clear and validates the new inference logic.

crates/biome_module_graph/src/js_module_info/collector.rs (4)

6-9: LGTM!

Import changes appropriately include JsAssignmentExpression for the new assignment-tracking logic.


578-578: Clone is acceptable here.

Cloning the binding provides access to metadata needed for the improved inference. Since this occurs once per binding during collection, the performance cost is reasonable.


584-598: Helper methods look solid.

Clean, focused implementations for detecting and collecting writable references. These support the new type-widening logic nicely.


654-654: Early return is appropriate.

Returning after resolving the type from variable declarations prevents unnecessary traversal of further ancestors and maintains the existing control-flow pattern.

@codspeed-hq
Copy link

codspeed-hq bot commented Nov 16, 2025

CodSpeed Performance Report

Merging #8119 will not alter performance

Comparing fix/inference-assignments (e0955f4) with main (0eb08e8)

Summary

✅ 58 untouched
⏩ 95 skipped1

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

📜 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 06c85ff and 7b39a52.

⛔ Files ignored due to path filters (1)
  • crates/biome_module_graph/tests/snapshots/test_widening_via_assignment_multiple_values.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (2)
  • .changeset/lovely-sloths-chew.md (1 hunks)
  • crates/biome_module_graph/src/js_module_info/collector.rs (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: arendjr
Repo: biomejs/biome PR: 7266
File: crates/biome_js_type_info/src/type.rs:94-102
Timestamp: 2025-08-20T16:24:59.781Z
Learning: In crates/biome_js_type_info/src/type.rs, the flattened_union_variants() method returns TypeReference instances that already have the correct module IDs applied to them. These references should be used directly with resolver.resolve_reference() without applying additional module ID transformations, as variant references may originate from nested unions in different modules.
📚 Learning: 2025-08-20T16:24:59.781Z
Learnt from: arendjr
Repo: biomejs/biome PR: 7266
File: crates/biome_js_type_info/src/type.rs:94-102
Timestamp: 2025-08-20T16:24:59.781Z
Learning: In crates/biome_js_type_info/src/type.rs, the flattened_union_variants() method returns TypeReference instances that already have the correct module IDs applied to them. These references should be used directly with resolver.resolve_reference() without applying additional module ID transformations, as variant references may originate from nested unions in different modules.

Applied to files:

  • crates/biome_module_graph/src/js_module_info/collector.rs
📚 Learning: 2025-08-05T14:43:29.581Z
Learnt from: dyc3
Repo: biomejs/biome PR: 7081
File: packages/@biomejs/biome/configuration_schema.json:7765-7781
Timestamp: 2025-08-05T14:43:29.581Z
Learning: The file `packages/biomejs/biome/configuration_schema.json` is auto-generated and should not be manually edited or reviewed for schema issues; any changes should be made at the code generation source.

Applied to files:

  • .changeset/lovely-sloths-chew.md
🧬 Code graph analysis (1)
crates/biome_module_graph/src/js_module_info/collector.rs (2)
crates/biome_module_graph/src/js_module_info.rs (1)
  • binding (264-266)
crates/biome_js_type_info/src/local_inference.rs (3)
  • from_any_js_expression (425-602)
  • from_any_js_expression (2173-2180)
  • from_any_js_expression (2343-2425)
🪛 LanguageTool
.changeset/lovely-sloths-chew.md

[uncategorized] ~5-~5: Possible missing comma found.
Context: ... of the rule noUnnecessaryConditions. Now the rule isn't triggered for variables ...

(AI_HYDRA_LEO_MISSING_COMMA)

⏰ 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). (6)
  • GitHub Check: Test Node.js API
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: autofix
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_formatter)
🔇 Additional comments (2)
crates/biome_module_graph/src/js_module_info/collector.rs (2)

584-598: Clean helper methods.

These helper methods are well-structured and serve their purpose clearly.


618-641: The union_with method exists and is correctly used; however, scope handling requires architectural clarification.

The call to self.union_with(ty.clone(), assigned_type) on line 635 is valid—the method exists on the TypeResolver trait that JsModuleInfoCollector implements.

However, the scope concern remains: line 631 uses the binding's declaration scope (scope_id) to infer the assignment's RHS type, even when the assignment occurs in a nested scope (e.g., inside a function). Since scope_by_range is available elsewhere in the collector, consider whether you should look up the assignment's actual scope rather than using the binding's declaration scope. This matters especially for resolving identifiers referenced in complex RHS expressions within different scopes.

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: 0

♻️ Duplicate comments (1)
crates/biome_module_graph/src/js_module_info/collector.rs (1)

584-599: Assignment-based type widening: fix scope usage and tidy up

Nice idea to widen binding types from write references. A few points worth tightening up:

  1. Scope for RHS inference can be wrong (potentially widening to unknown)

    In the assignment loop you currently do:

let data = TypeData::from_any_js_expression(self, scope_id, &right);
let assigned_type = self.reference_to_owned_data(data);
ty = ResolvedTypeId::new(
    self.level(),
    self.union_with(ty.clone(), assigned_type),
).into();

Here scope_id is the scope of the declaration (binding.range), not necessarily the scope where the assignment lives. From the from_any_js_expression implementation in local_inference.rs, identifier expressions rely on the provided scope_id to resolve bindings. That means something like:

let x = 1;
function f(y: string) {
    x = y;
}

may fail to see y (declared in the inner scope) and treat the RHS as unknown, so x gets widened from number to unknown instead of number | string.

You can reuse the already computed expression type from the first pass and avoid the scope mismatch entirely by going through reference_to_resolved_expression:

-                                if let Some(assignment) =
-                                    JsAssignmentExpression::cast_ref(&ancestor)
-                                    && let Ok(right) = assignment.right()
-                                {
-                                    let data =
-                                        TypeData::from_any_js_expression(self, scope_id, &right);
-                                    let assigned_type = self.reference_to_owned_data(data);
-                                    ty = ResolvedTypeId::new(
-                                        self.level(),
-                                        self.union_with(ty.clone(), assigned_type),
-                                    )
-                                    .into();
-                                }
+                                if let Some(assignment) =
+                                    JsAssignmentExpression::cast_ref(&ancestor)
+                                    && let Ok(right) = assignment.right()
+                                {
+                                    let assigned_type =
+                                        self.reference_to_resolved_expression(scope_id, &right);
+                                    ty = ResolvedTypeId::new(
+                                        self.level(),
+                                        self.union_with(ty.clone(), assigned_type),
+                                    )
+                                    .into();
+                                }

This should pick up the RHS type as it was inferred with the correct scope during the initial traversal, and avoids recomputing it. I’d also consider adding a spec that covers a nested-function assignment like the example above to guard this behaviour.

  1. Drop the redundant else branch

    The if self.has_writable_reference(&binding) arm returns ty in both branches:

if self.has_writable_reference(&binding) {
    // ... possibly update ty ...
    ty
} else {
    ty
}

You can simply return ty once after the if block and avoid repeating yourself. This is exactly what was mentioned in the previous review, and it’s still an easy cleanup.

  1. Minor tidy-ups

    • let binding_name = &binding.name.clone(); can just be let binding_name = &binding.name; – no need to allocate a fresh Text.
    • get_writable_references allocates a Vec only to iterate over it immediately. You could inline the iterator (for reference in binding.references.iter().filter(|r| r.is_write())) and drop the helper, unless you particularly like the separation.

None of these are blockers, but they’ll make this bit of logic a touch leaner and clearer.

Also applies to: 600-645

🧹 Nitpick comments (1)
crates/biome_module_graph/src/js_module_info/collector.rs (1)

573-579: Avoid cloning JsBindingData and JsSyntaxNode on the hot path

infer_all_types only reads from binding and node, so you can pass them by reference and drop the clone() calls if you ever want to trim a bit of overhead here:

-            let binding = &self.bindings[index];
-            if let Some(node) = self.binding_node_by_start.get(&binding.range.start()) {
+            let binding = &self.bindings[index];
+            if let Some(node) = self.binding_node_by_start.get(&binding.range.start()) {
                 let scope_id = scope_id_for_range(scope_by_range, binding.range);
-                let ty = self.infer_type(&node.clone(), binding.clone(), scope_id);
+                let ty = self.infer_type(node, binding, scope_id);

That would, of course, require infer_type to take &JsBindingData instead of an owned value.

📜 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 7b39a52 and fa78da4.

📒 Files selected for processing (1)
  • crates/biome_module_graph/src/js_module_info/collector.rs (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: arendjr
Repo: biomejs/biome PR: 7266
File: crates/biome_js_type_info/src/type.rs:94-102
Timestamp: 2025-08-20T16:24:59.781Z
Learning: In crates/biome_js_type_info/src/type.rs, the flattened_union_variants() method returns TypeReference instances that already have the correct module IDs applied to them. These references should be used directly with resolver.resolve_reference() without applying additional module ID transformations, as variant references may originate from nested unions in different modules.
📚 Learning: 2025-08-20T16:24:59.781Z
Learnt from: arendjr
Repo: biomejs/biome PR: 7266
File: crates/biome_js_type_info/src/type.rs:94-102
Timestamp: 2025-08-20T16:24:59.781Z
Learning: In crates/biome_js_type_info/src/type.rs, the flattened_union_variants() method returns TypeReference instances that already have the correct module IDs applied to them. These references should be used directly with resolver.resolve_reference() without applying additional module ID transformations, as variant references may originate from nested unions in different modules.

Applied to files:

  • crates/biome_module_graph/src/js_module_info/collector.rs
🧬 Code graph analysis (1)
crates/biome_module_graph/src/js_module_info/collector.rs (3)
crates/biome_module_graph/src/js_module_info.rs (1)
  • binding (264-266)
crates/biome_js_formatter/src/utils/assignment_like.rs (1)
  • right (323-353)
crates/biome_js_type_info/src/local_inference.rs (3)
  • from_any_js_expression (425-602)
  • from_any_js_expression (2173-2180)
  • from_any_js_expression (2343-2425)
⏰ 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: autofix
  • GitHub Check: Documentation
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: End-to-end tests
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Check Dependencies
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Test Node.js API
  • GitHub Check: Bench (biome_module_graph)
🔇 Additional comments (1)
crates/biome_module_graph/src/js_module_info/collector.rs (1)

4-10: New imports look spot on

All the newly imported syntax nodes (AnyJsImportClause, JsAssignmentExpression, identifiers, inner_string_text, etc.) are used below and match their call‑sites. Nothing to fix here.

Copy link
Contributor

@arendjr arendjr left a comment

Choose a reason for hiding this comment

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

Nice start! I wouldn't consider the requested change to be a blocker personally. Better to be a bit more lenient than too strict, as is currently the case. We can always tighten it again later (although then it might be good to acknowledge this in the changelog).

@ematipico ematipico force-pushed the fix/inference-assignments branch from a34d385 to b700ad7 Compare November 19, 2025 20:49
@ematipico ematipico requested a review from dyc3 November 19, 2025 20:49
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: 0

🧹 Nitpick comments (1)
crates/biome_module_graph/src/js_module_info/collector.rs (1)

578-578: Consider performance impact of cloning bindings.

Cloning the entire JsBindingData structure (including its references Vec) for every binding during type inference could be expensive for modules with many bindings or heavily-referenced bindings. Consider passing a reference or extracting only the required fields.

📜 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 fa78da4 and 3d8fc1f.

📒 Files selected for processing (3)
  • .changeset/lovely-sloths-chew.md (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_unnecessary_conditions.rs (2 hunks)
  • crates/biome_module_graph/src/js_module_info/collector.rs (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-20T16:24:59.781Z
Learnt from: arendjr
Repo: biomejs/biome PR: 7266
File: crates/biome_js_type_info/src/type.rs:94-102
Timestamp: 2025-08-20T16:24:59.781Z
Learning: In crates/biome_js_type_info/src/type.rs, the flattened_union_variants() method returns TypeReference instances that already have the correct module IDs applied to them. These references should be used directly with resolver.resolve_reference() without applying additional module ID transformations, as variant references may originate from nested unions in different modules.

Applied to files:

  • crates/biome_module_graph/src/js_module_info/collector.rs
📚 Learning: 2025-08-05T14:43:29.581Z
Learnt from: dyc3
Repo: biomejs/biome PR: 7081
File: packages/@biomejs/biome/configuration_schema.json:7765-7781
Timestamp: 2025-08-05T14:43:29.581Z
Learning: The file `packages/biomejs/biome/configuration_schema.json` is auto-generated and should not be manually edited or reviewed for schema issues; any changes should be made at the code generation source.

Applied to files:

  • .changeset/lovely-sloths-chew.md
🧬 Code graph analysis (2)
crates/biome_js_analyze/src/lint/nursery/no_unnecessary_conditions.rs (1)
crates/biome_analyze/src/rule.rs (2)
  • sources (617-620)
  • inspired (254-259)
crates/biome_module_graph/src/js_module_info/collector.rs (2)
crates/biome_module_graph/src/js_module_info.rs (1)
  • binding (264-266)
crates/biome_js_type_info/src/local_inference.rs (3)
  • from_any_js_expression (425-602)
  • from_any_js_expression (2173-2180)
  • from_any_js_expression (2343-2425)
🪛 LanguageTool
.changeset/lovely-sloths-chew.md

[uncategorized] ~5-~5: Possible missing comma found.
Context: ... of the rule noUnnecessaryConditions. Now the rule isn't triggered for variables ...

(AI_HYDRA_LEO_MISSING_COMMA)

⏰ 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). (6)
  • GitHub Check: Test Node.js API
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: autofix
🔇 Additional comments (7)
.changeset/lovely-sloths-chew.md (1)

5-5: Grammar issue resolved.

The previously flagged "doesn't isn't" grammar glitch has been corrected. The sentence now reads smoothly.

crates/biome_js_analyze/src/lint/nursery/no_unnecessary_conditions.rs (2)

47-61: Clear documentation of the rule deviation.

The added documentation effectively explains why the rule doesn't trigger for bindings with multiple assignments. The example is illustrative and directly supports the explanation.


90-90: Correct source attribution.

Marking the rule as inspired() appropriately reflects the deviation in behaviour from the source rule, as discussed in previous reviews.

crates/biome_module_graph/src/js_module_info/collector.rs (4)

584-598: Clean helper methods for write detection.

The helper methods are straightforward and correctly identify writable references using the is_write() predicate.


609-628: Verify widening scope is intentional.

Type widening based on writable references is only applied to variable declarations (lines 609-622), but not to function parameters, type parameters, or for-variable declarations. Is this the intended behaviour, or should widening also apply to reassigned parameters?


668-697: Type widening logic looks sound.

The method correctly identifies write references, locates assignment expressions, infers RHS types, and builds union types. The approach of iterating through writable references and unioning assigned types achieves the PR objective of widening types for multiply-assigned bindings.


6-9: Necessary import addition.

Adding JsAssignmentExpression supports the new widening logic that inspects assignment expressions.

@github-actions github-actions bot added the A-CLI Area: CLI label Nov 19, 2025
@ematipico ematipico force-pushed the fix/inference-assignments branch from 576b3d6 to e0955f4 Compare November 19, 2025 21:05
@ematipico
Copy link
Member Author

ematipico commented Nov 19, 2025

There's a tiny regression 😅

@ematipico ematipico added this pull request to the merge queue Nov 19, 2025
Merged via the queue into main with commit 8d64655 Nov 19, 2025
19 checks passed
@ematipico ematipico deleted the fix/inference-assignments branch November 19, 2025 21:30
@github-actions github-actions bot mentioned this pull request Nov 19, 2025
ryan-m-walker pushed a commit to ryan-m-walker/biome that referenced this pull request Nov 23, 2025
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-CLI Area: CLI A-Linter Area: linter A-Project Area: project A-Type-Inference Area: type inference L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants