-
-
Notifications
You must be signed in to change notification settings - Fork 794
fix: revert #7684 #8173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: revert #7684 #8173
Conversation
…_with_se…" This reverts commit f4433b3.
🦋 Changeset detectedLatest commit: 1cbd0e4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
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 |
CodSpeed Performance ReportMerging #8173 will not alter performanceComparing Summary
Footnotes
|
|
Caution Review failedThe pull request is closed. WalkthroughThis pull request reverts an internal refactor of the Possibly related PRs
Suggested reviewers
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (8)
📒 Files selected for processing (13)
Comment |
WalkthroughThis patch release reverts an internal refactor that introduced a regression in the noUnusedPrivateClassMembers rule, where private class members accessed exclusively from constructors were incorrectly flagged as unused. The fix involves restructuring the semantic_class service to shift from SemanticClass-based queries to more generic Semantic types, refactoring member traversal logic to use new FunctionThisReferences tracking, and updating related lint rules. Test files have been adjusted to align with the new semantics. Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. Comment |
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs (1)
126-148: Fixctx.modelaccess: should bectx.model()method callLine 141 uses
ctx.model.class_member_references(…)as a field access, butRuleContextexposes model as a method. Across all other rules (e.g.no_class_assign.rs,use_static_response_methods.rs,no_var.rs), the pattern isctx.model()followed by the method call. Change line 141 to:- let ClassMemberReferences { writes, .. } = ctx.model.class_member_references(&members); + let ClassMemberReferences { writes, .. } = ctx.model().class_member_references(&members);This will not compile as-is.
🧹 Nitpick comments (3)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts (1)
9-13: Constructor parameter name looks like a typo.
nusedPropertyon Line 10 reads like a slippedufromunusedProperty. The test still does its job, but consider renaming for readability.crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.js (1)
41-46: NewFoocase neatly covers compound write‑only usage.
this.#usedOnlyInWriteStatement += 42;is a good extra invalid scenario where the private field is only written and the expression value is ignored, complementing the simple assignment case above. If you want clearer test names, you could rename this secondFoo, but it is not strictly necessary.crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs (1)
149-179: Avoid cloningwritesandAnyPropertyMemberin the hot pathThe filtering pipeline currently does quite a bit of cloning:
- Each
prop_or_paramis cloned several times.writes.clone().into_iter()re‑clones the entireFxHashSetfor every candidate.Functionally this works, but it’s unnecessarily expensive for larger classes.
You can keep the behaviour while reducing allocations by iterating by reference and pattern‑matching directly on
AnyPropertyMember, for example:- constructor_params - .clone() - .into_iter() + constructor_params + .iter() + .cloned() .chain( - non_readonly_class_property_members.filter(|class_property_member| { - !constructor_params.clone().into_iter().any(|node| { - node.to_trimmed_text() == class_property_member.to_trimmed_text() - }) - }), + non_readonly_class_property_members.filter(|class_property_member| { + !constructor_params.iter().any(|node| { + node.to_trimmed_text() == class_property_member.to_trimmed_text() + }) + }), ) - .filter_map(|prop_or_param| { - if writes - .clone() - .into_iter() - .any(|ClassMemberReference { name, .. }| { - if let Some(TextAndRange { text, .. }) = - extract_property_or_param_range_and_text(&prop_or_param.clone()) - { - return name.eq(&text); - } - false - }) - { - None - } else { - Some(prop_or_param.clone()) - } - }) + .filter_map(|prop_or_param| { + let Some(TextAndRange { text, .. }) = + extract_property_or_param_range_and_text(&prop_or_param) + else { + return None; + }; + + if writes.iter().any(|ClassMemberReference { name, .. }| name == &text) { + None + } else { + Some(prop_or_param) + } + })Similarly,
extract_property_or_param_range_and_textcan be simplified to match directly on&AnyPropertyMemberinstead of casting from a cloned syntax node:-fn extract_property_or_param_range_and_text( - property_or_param: &AnyPropertyMember, -) -> Option<TextAndRange> { - if let Some(AnyPropertyMember::JsPropertyClassMember(member)) = - AnyPropertyMember::cast(property_or_param.clone().into()) - { +fn extract_property_or_param_range_and_text( + property_or_param: &AnyPropertyMember, +) -> Option<TextAndRange> { + if let AnyPropertyMember::JsPropertyClassMember(member) = property_or_param { if let Ok(member_name) = member.name() { return Some(TextAndRange { text: member_name.to_trimmed_text(), range: member_name.range(), }); } return None; } - - if let Some(AnyPropertyMember::TsPropertyParameter(parameter)) = - AnyPropertyMember::cast(property_or_param.clone().into()) - { + if let AnyPropertyMember::TsPropertyParameter(parameter) = property_or_param { let name = parameter .formal_parameter() .ok()? .as_js_formal_parameter()? .binding() .ok()?; return Some(TextAndRange { text: name.to_trimmed_text(), range: name.range(), }); } None }This keeps the logic as‑is while tightening up allocations and making the intent a bit clearer.
Please sanity‑check that the refactor preserves behaviour for both class properties and TS property parameters, especially around
to_trimmed_text()equality.Also applies to: 285-288, 333-335, 349-354, 398-431
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (8)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.js.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid.js.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts.snapis excluded by!**/*.snapand included by**
📒 Files selected for processing (13)
.changeset/stale-jokes-turn.md(1 hunks)crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs(14 hunks)crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs(13 hunks)crates/biome_js_analyze/src/lint/suspicious/no_class_assign.rs(1 hunks)crates/biome_js_analyze/src/services/semantic_class.rs(38 hunks)crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.js(1 hunks)crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts(1 hunks)crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts(0 hunks)crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts(1 hunks)crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts(1 hunks)crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid.js(4 hunks)crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js(0 hunks)crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts(1 hunks)
💤 Files with no reviewable changes (2)
- crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js
- crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-25T12:32:59.003Z
Learnt from: arendjr
Repo: biomejs/biome PR: 7593
File: crates/biome_service/src/workspace/server.rs:1306-1306
Timestamp: 2025-09-25T12:32:59.003Z
Learning: In the biomejs/biome project, do not flag compilation errors during code review as they are handled by the existing test infrastructure and CI. Focus on other code quality aspects instead.
Applied to files:
.changeset/stale-jokes-turn.md
🧬 Code graph analysis (2)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts (1)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts (1)
Sample(1-13)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (3)
crates/biome_rowan/src/ast/mod.rs (1)
try_cast(182-188)crates/biome_rowan/src/syntax/node.rs (1)
grand_parent(254-256)crates/biome_js_formatter/src/utils/assignment_like.rs (1)
left(355-374)
⏰ 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: Documentation
- GitHub Check: End-to-end tests
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
- GitHub Check: Check Dependencies
- GitHub Check: Test Node.js API
- GitHub Check: autofix
- GitHub Check: Validate rules documentation
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Bench (biome_js_formatter)
🔇 Additional comments (9)
.changeset/stale-jokes-turn.md (1)
1-5: Changelog entry accurately describes the regression fix.Clear that this is a patch for
@biomejs/biome, tied to #8138 and explicitly namingnoUnusedPrivateClassMembers; reads well for release notes.crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts (1)
3-13: Dynamic access test still exercises both private members correctly.This keeps
addinitialised andremovecaptured as a private ctor parameter, both invoked viathis[action](), which is a solid “no diagnostics expected” case for dynamic access.crates/biome_js_analyze/src/lint/suspicious/no_class_assign.rs (1)
101-122: Diagnostic builder matches the collectedReferencestate.Using the write
reference.syntax().text_trimmed_range()as the primary span and the class name binding as a detail is consistent with the rule docs and therunimplementation, and the?chain keeps it safe for odd class forms.crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts (1)
2-12: Constructor rewrite focuses the test on the unused private properties.Both
unusedPropertyandanotherUnusedPropertyare now private ctor properties with defaults and no subsequent usage, which cleanly exercises the intended “two unused private ctor members” scenario for this issue. Any TS‑level diagnostics can stay in the remit of the existing CI.Based on learnings.
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid.js (1)
22-29: New valid patterns nicely broaden “used private member” coverage.These added classes cover several nuanced usages—accessors updated via
+=, setter‑only members appearing in destructuring targets, private fields read through instances captured in inner classes, and simple setter calls from methods—so they collectively help ensurenoUnusedPrivateClassMembersdoes not mis‑fire on legitimate patterns.Also applies to: 134-140, 154-162, 173-181
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts (1)
2-12: Dynamic-access fixture still looks soundThe minimal
Sampleshape (private field,#prop, constructor writes, andthis[name]lookup) still covers the dynamic-access case without adding noise. Nothing here appears to contradict the intended “invalid” scenario for the rule.crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (2)
244-303: Constructor‑only usages vs write‑only/update detectionThe new
traverse_members_usagelogic removes members from the “unused” set only when it sees a name match that is not:
- a write‑only assignment (as defined by
is_write_only), or- part of an update expression whose result isn’t used (
is_in_update_expression).This is deliberate for cases like:
this.#usedOnlyInWrite = 212;(still considered unused), andthis.count++;as a bare statement.For the regression in #8138, the key bit is that constructor‑only reads (e.g.
console.log(this.#field)orthis.#method()from the constructor) should still cause the corresponding member to be dropped fromprivate_members, while constructor‑only writes should keep the current “write‑only” behaviour.The current logic doesn’t treat constructors specially, so constructor reads should be handled the same as method reads; only pure writes and update‑only uses are filtered out.
Could you re‑run the linked reproducer from issue #8138 (private field and private method only used from the constructor) to confirm that:
- constructor reads clear the member from
unused_members, and- constructor‑only write‑only patterns are still intentionally reported?
If that expectation differs, we may need to relaxis_write_onlyfor constructor contexts.Also applies to: 377-430
97-113: Option passed to RuleDiagnostic::new—needs verificationThe review comment correctly identifies a type mismatch:
property_range()returnsOption<TextRange>, butdiagnostic()passes it directly toRuleDiagnostic::new()in three places (lines ~150–181). The code clearly shows property_range can legitimately returnNonefor bogus or metavariable parameters.I've examined the source code and confirmed the pattern described in the review. However, I cannot run
cargo checkin the sandbox to definitively confirm whether this causes a compilation error or relies on implicit conversion.The suggested fix (early return with
?operator) is idiomatic Rust and ensures diagnostics only emit when a valid range is available. Please run a build locally to confirm the type error and apply the fix if needed.crates/biome_js_analyze/src/services/semantic_class.rs (1)
20-38: Semantic-class service wiring and constructor handling look consistentThe new
SemanticClassServices/SemanticClassModelwrapper plus the two visitors give you a cheap, syntax‑onlyclass_member_referencesAPI that’s then reused byuseReadonlyClassProperties. The wayclass_member_referencesdelegates constructor analysis tocollect_references_from_constructor—which only looks at nested function bodies, not the top‑level constructor body—nicely matches the rule’s intent: treat direct constructor writes as initialisation, but still consider writes in escaping closures as mutations.The only micro‑nit is that you build an intermediate
Vec<ClassMemberReferences>and then fold it; this could be folded directly into the twoFxHashSets, but it’s not worth bikeshedding unless this shows up in profiles.It’s worth re‑running the
useReadonlyClassPropertiessuite for classes that mutate fields only:
- directly in the constructor;
- in arrow functions created in the constructor; and
- in regular methods,
to make sure the newclass_member_referencessemantics match the documented rule behaviour.Also applies to: 185-242, 819-837
Reverts #7684
Closes #8138