Skip to content

Conversation

vladimir-ivanov
Copy link
Contributor

@vladimir-ivanov vladimir-ivanov commented Sep 28, 2025

Changed noUnusedPrivateClassMembers to align more fully with meaningful reads.

Amended slightly the meaningful reads logic to include some extra cases.

Reshuffled some previously valid and invalid cases like:

Invalid examples (previously valid)

class UsedMember {
  get #usedAccessor() {}
  set #usedAccessor(value) {}

  method() {
    // no return statement so no meaningful read
    this.#usedAccessor = 42;
  }
}

class UsedMember {
  #usedInInnerClass;

  method(a) {
    return class {
      // not really used, a is not reference to this scope
      foo = a.#usedInInnerClass;
    }
  }
}

class UsedMember {
  set #accessorUsedInMemberAccess(value) {} // <- unused

  method(a) {
    // there is no getter, so this is not a read at all
    [this.#accessorUsedInMemberAccess] = a;
  }
}

class UsedMember {
  #usedInInnerClass;

  method(a) {
    return class {
      foo = a.#usedInInnerClass;
    }
  }
}

class C {
  set #x(value) {
    doSomething(value);
  }

  foo() {
    // no retrn statement so not a meaningful read.
    this.#x = 1;
  }
}

Valid example (Previously invalid)

class Foo {
  #usedOnlyInWriteStatement = 5;
  method() {
    // this is both meaningful read and write.
    this.#usedOnlyInWriteStatement += 42;
  }
}

closes #7499

Copy link

changeset-bot bot commented Sep 28, 2025

🦋 Changeset detected

Latest commit: 38035b9

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 Sep 28, 2025
@vladimir-ivanov vladimir-ivanov force-pushed the feat/no_unused_private_class_members_semantic_class branch from 2577f8f to 19ee305 Compare September 28, 2025 21:24
@vladimir-ivanov vladimir-ivanov changed the title feat(biome_js_analyze): align no_unused_private_class_members_with_se… feat(biome_js_analyze): align no_unused_private_class_members_with_semantic_class Sep 28, 2025
Copy link

codspeed-hq bot commented Sep 28, 2025

CodSpeed Performance Report

Merging #7610 will degrade performances by 10.43%

Comparing vladimir-ivanov:feat/no_unused_private_class_members_semantic_class (38035b9) with main (efc9980)

Summary

❌ 2 regressions
✅ 51 untouched
⏩ 85 skipped1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Benchmarks breakdown

Benchmark BASE HEAD Change
js_analyzer[parser_13571644119461115204.ts] 98.1 ms 109.5 ms -10.43%
js_analyzer[router_17129688031671448157.ts] 26.6 ms 28.8 ms -7.62%

Footnotes

  1. 85 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.

@vladimir-ivanov vladimir-ivanov force-pushed the feat/no_unused_private_class_members_semantic_class branch from dae7a42 to 0b4b6be Compare September 29, 2025 19:59
@vladimir-ivanov vladimir-ivanov marked this pull request as ready for review September 29, 2025 20:00
Copy link
Contributor

coderabbitai bot commented Sep 29, 2025

Walkthrough

The PR integrates SemanticClassModel and named-member extraction into unused-private-member analysis. It replaces the old traversal with semantic-aware traversals (traverse_meaningful_read_members_usage and traverse_computed_members_usage), threads Arc through diagnostics/actions, extends AnyMember with semantic-aware APIs (member_range, matches_name), introduces AnyNamedClassMember/NamedClassMember, migrates use_readonly_class_properties to the named-member model, and updates tests and changelog to clarify the “meaningful read” definition.

Possibly related PRs

Suggested labels

A-Project

Suggested reviewers

  • ematipico
  • Conaclos

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly and concisely summarises the main change by indicating that the no_unused_private_class_members lint is being aligned with the semantic class model, matching the feature implementation in the pull request.
Linked Issues Check ✅ Passed The changes introduce a central SemanticClassModel and shared AnyNamedClassMember representation, update both noUnusedPrivateClassMembers and use_readonly_class_properties to use unified traversal logic and context classification via AccessKind and ClassMemberReferences, normalise TS and ES private members, and centralise read/write detection and diagnostic fix actions, fulfilling the objectives of issue #7499.
Out of Scope Changes Check ✅ Passed All modifications, including updates to the semantic_class service, lint rules for private members and readonly properties, changelog entries, and related tests, directly support the alignment of private member analysis and readonly suggestions as scoped by issue #7499.
Description Check ✅ Passed The pull request description accurately describes the updates to the noUnusedPrivateClassMembers rule, including changes to meaningful reads logic and example reclassifications, and it clearly references the related rule documentation and issue.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 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 34e433f and 38035b9.

📒 Files selected for processing (1)
  • .changeset/no_unused_private_class_members_amended.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/no_unused_private_class_members_amended.md
⏰ 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). (11)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Documentation
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix

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

@vladimir-ivanov vladimir-ivanov requested review from Conaclos, ematipico and togami2864 and removed request for Conaclos, ematipico and togami2864 September 29, 2025 20:06
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: 4

🧹 Nitpick comments (3)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js (1)

4-9: LGTM. Consider one more variant for coverage.

This nicely exercises a meaningful read via +=. Optional: add prefix/postfix increment and direct read/write variants to future‑proof the rule against similar patterns.

.changeset/no_unused_private_class_members_amended.md (1)

25-27: Grammar nit and duplicated example.

  • Comment grammar: “a is not reference to this scope” → “a is not a reference to this scope.”
  • The “UsedMember/#usedInInnerClass” block appears twice with the same point; consider dropping one to reduce noise.
-      // not really used, a is not reference to this scope
+      // Not really used; `a` is not a reference to this scope.
- class UsedMember {
-   #usedInInnerClass;
-
-   method(a) {
-     return class {
-       foo = a.#usedInInnerClass;
-     }
-   }
- }
+ // (Duplicate example removed for brevity.)

Also applies to: 44-48

crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts (1)

16-18: Small wording tweak for clarity.

“a is not reference to this scope” → “a is not a reference to this scope.”

-      // not really used, a is not reference to this scope
+      // Not really used; `a` is not a reference to this scope.
📜 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 2b9b9d8 and 0b50aad.

⛔ Files ignored due to path filters (7)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (10)
  • .changeset/no_unused_private_class_members_amended.md (1 hunks)
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (12 hunks)
  • crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs (11 hunks)
  • crates/biome_js_analyze/src/services/semantic_class.rs (13 hunks)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.js (0 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 (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 (0 hunks)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js (1 hunks)
💤 Files with no reviewable changes (2)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.js
🧰 Additional context used
📓 Path-based instructions (5)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js
  • crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs
  • crates/biome_js_analyze/src/services/semantic_class.rs
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js
  • crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs
  • crates/biome_js_analyze/src/services/semantic_class.rs
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts
.changeset/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

.changeset/*.md: In changeset files, only use #### or ##### headers; avoid other header levels
Changeset descriptions should use past tense for what you did (e.g., "Added...")
Describe current Biome behavior in present tense within changesets (e.g., "Biome now supports...")
For bug fixes in changesets, start with a link to the issue (e.g., "Fixed #1234: ...")
When referencing rules or assists in changesets, include links to their documentation pages
Include a minimal code block in the changeset when applicable to demonstrate the change
End every sentence in the changeset description with a period

Files:

  • .changeset/no_unused_private_class_members_amended.md
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs
  • crates/biome_js_analyze/src/services/semantic_class.rs
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
🧠 Learnings (5)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : If a rule returns a code action (implements `action`), add `fix_kind` in `declare_lint_rule!` and use `ctx.metadata().applicability()` when building the action

Applied to files:

  • crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
📚 Learning: 2025-08-11T11:48:52.001Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-08-11T11:48:52.001Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When a token is mandatory and present in the AST, use the AST-provided token (e.g., node.l_paren_token().format()) instead of hardcoded tokens

Applied to files:

  • crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/{lint,assist}/**/*.rs : When banning globals (e.g., `noConsoleLog`), check the semantic model to avoid false positives from locally shadowed bindings

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : Follow rule naming conventions: use `no<Concept>` to forbid and `use<Concept>` to mandate; prefer consistent prefixes (e.g., `noDuplicate<Concept>`, `useConsistent<Concept>`)

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : Define per-rule options types in `biome_rule_options` crate (one file per rule, e.g., `lib/use_my_rule.rs`)

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
🧬 Code graph analysis (2)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (1)
crates/biome_js_analyze/src/services/semantic_class.rs (5)
  • new (48-50)
  • new (426-431)
  • default (42-44)
  • class_member_references (52-54)
  • class_member_references (271-327)
⏰ 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). (18)
  • GitHub Check: Validate PR title
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
  • GitHub Check: Bench (biome_graphql_formatter)
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: Bench (biome_graphql_parser)
  • GitHub Check: Bench (biome_configuration)
  • GitHub Check: Bench (biome_html_parser)
  • GitHub Check: Bench (biome_json_formatter)
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Bench (biome_html_formatter)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_json_analyze)
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_json_parser)
  • GitHub Check: Bench (biome_css_parser)
🔇 Additional comments (4)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts (1)

10-10: Rename improves intent.

Switching to unusedProperty clarifies the case. Please ensure any snapshots/inline expectations referencing the old name are updated.

crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts (2)

2-6: Good clarification of the constructor case.

This cleanly isolates two private param properties as unused while keeping usedProperty public. Looks spot on for the regression.

Please confirm this still reproduces the intended diagnostics for issue 7101 with the new semantics.


14-17: Helpful inline note.

The comment crisply explains why this doesn’t count as a member read/write. No changes needed.

crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts (1)

1-9: Accessor write‑only case captured well.

This is a solid invalid example for “no meaningful read” with a setter. All good.

Ok(_) => {}
Err(node) => {
if ts_private_count != 0
&& let Some(computed_member) = AnyJsComputedMember::cast(node)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

will do another / final pr to address this area - I would suggest we either do:

  1. move to semantic_class and perhaps introduce another type of entity there - so far we have AccessKind { MeaningfulRead, TrivialRead, Write} - we can add another DynamicRead, or similar.
  2. keep the logic here and expand so we read the type of the argument AND check its definition -> if TS Union type, then we check the values and match against class members.
  3. any other ideas welcome :-)

Copy link
Member

Choose a reason for hiding this comment

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

Will you create a new task for that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thinking yes. maybe just option 2 as a start. will play with it tomorrow

Copy link
Contributor Author

Choose a reason for hiding this comment

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

#7682 ticket raised, will do a pr shortly, perhaps will wait for this one to get resolved or rejected first to avoid conflicts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

#7684 pr raised, but in draft mode as it stands, depends on this one

Copy link
Contributor

coderabbitai bot commented Sep 29, 2025

Note

Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

vladimir-ivanov and others added 5 commits September 29, 2025 21:15
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…eClassMembers/invalid_aligned_with_semantic_class.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…_class_members.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (2)

164-174: Unsafe fix: demoting TS parameter properties while this.<name> is used.

We always propose RemovePrivateModifier for TsPropertyParameter, but we don’t gate it on property reads/writes. If the class writes this.foo = ... (or reads it) anywhere, removing the private modifier drops the class field and breaks those sites.

Gate the fix on “no property reads and no property writes” and fall back to a diagnostic‑only path otherwise.

Suggested minimal change:

@@
-                AnyMember::TsPropertyParameter(ts_property_param) => {
-                    // Check if the parameter is also unused in constructor body using semantic analysis
-                    let should_rename =
-                        check_ts_property_parameter_usage(ctx, ts_property_param);
-                    results.push(UnusedMemberAction::RemovePrivateModifier {
-                        member,
-                        semantic_class: semantic_class.clone(),
-                        rename_with_underscore: should_rename,
-                    });
-                }
+                AnyMember::TsPropertyParameter(ts_property_param) => {
+                    let has_prop_refs =
+                        has_property_references(&semantic_class, &member, &class_member_references);
+                    if has_prop_refs {
+                        // Report only; avoid an unsafe demotion that would break `this.<name>` sites.
+                        results.push(UnusedMemberAction::RemoveMember {
+                            member,
+                            semantic_class: semantic_class.clone(),
+                        });
+                    } else {
+                        // Safe to demote to a plain parameter. Rename if the binding itself is unused.
+                        let should_rename =
+                            check_ts_property_parameter_usage(ctx, ts_property_param);
+                        results.push(UnusedMemberAction::RemovePrivateModifier {
+                            member,
+                            semantic_class: semantic_class.clone(),
+                            rename_with_underscore: should_rename,
+                        });
+                    }
+                }

…and add this helper (place near other helpers):

+fn has_property_references(
+    semantic_class: &SemanticClassModel,
+    member: &AnyMember,
+    refs: &ClassMemberReferences,
+) -> bool {
+    let matches = |r: &ClassMemberReference| member.matches_name(semantic_class, &r.name);
+    refs.reads.iter().any(matches) || refs.writes.iter().any(matches)
+}

This keeps the diagnostic while avoiding an unsound quick‑fix.


214-226: Avoid deleting constructor parameters for the diagnostic‑only path.

With the gating above, some TsPropertyParameter cases will use RemoveMember purely to carry the diagnostic. Make action a no‑op for that specific shape.

@@
-            UnusedMemberAction::RemoveMember { member, .. } => {
-                mutation.remove_node(member.clone());
+            UnusedMemberAction::RemoveMember { member, .. } => {
+                if matches!(member, AnyMember::TsPropertyParameter(_)) {
+                    // Diagnostic only; no safe fix for TS parameter properties with property uses.
+                    return None;
+                }
+                mutation.remove_node(member.clone());
                 Some(JsRuleAction::new(

This preserves existing fixes for regular class members.

🧹 Nitpick comments (2)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (2)

321-351: Computed‑access scan risks false negatives via nested classes.

syntax.descendants() will also traverse into inner classes; this[...] inside them doesn’t refer to the outer class. That can incorrectly mark TS privates as used.

Follow‑up suggestion:

  • Either restrict the walk to bodies belonging to the current class’ members, or
  • Leverage SemanticClassModel and tag such accesses as a separate AccessKind (e.g. DynamicRead) and handle them upstream, as discussed in the PR thread.

This is non‑blocking for this PR but worth tightening.


180-212: Message clarity for TS parameter properties.

Once the gating is in place:

  • When rename_with_underscore is true: consider “This parameter is never used.”.
  • When false (there are constructor‑binding references): “This parameter is never used outside of the constructor.”

Optional polish, but helps match the actual fix offered.

📜 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 a66a6c9 and b937419.

📒 Files selected for processing (3)
  • .changeset/no_unused_private_class_members_amended.md (1 hunks)
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (12 hunks)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/no_unused_private_class_members_amended.md
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
🧠 Learnings (5)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : If a rule returns a code action (implements `action`), add `fix_kind` in `declare_lint_rule!` and use `ctx.metadata().applicability()` when building the action

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/{lint,assist}/**/*.rs : When banning globals (e.g., `noConsoleLog`), check the semantic model to avoid false positives from locally shadowed bindings

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : Follow rule naming conventions: use `no<Concept>` to forbid and `use<Concept>` to mandate; prefer consistent prefixes (e.g., `noDuplicate<Concept>`, `useConsistent<Concept>`)

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : Define per-rule options types in `biome_rule_options` crate (one file per rule, e.g., `lib/use_my_rule.rs`)

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : Set the `language` field in `declare_lint_rule!` to the primary language (`js`, `jsx`, `ts`, or `tsx`) the rule targets

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (1)
crates/biome_js_analyze/src/services/semantic_class.rs (5)
  • new (48-50)
  • new (426-431)
  • default (42-44)
  • class_member_references (52-54)
  • class_member_references (271-327)
⏰ 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). (19)
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
  • GitHub Check: Bench (biome_html_formatter)
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: Bench (biome_json_formatter)
  • GitHub Check: Bench (biome_json_parser)
  • GitHub Check: Bench (biome_graphql_parser)
  • GitHub Check: Bench (biome_package)
  • GitHub Check: Bench (biome_html_parser)
  • GitHub Check: Bench (biome_configuration)
  • GitHub Check: Bench (biome_graphql_formatter)
  • GitHub Check: Bench (biome_json_analyze)
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_css_parser)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Validate PR title
🔇 Additional comments (6)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts (4)

1-9: Good coverage for accessor-only writes.

This clearly exercises “setter-only” paths; matches the updated meaningful‑read semantics. Nice.


14-20: Inner class “a.#field” examples hit the right edge case.

These confirm that an inner class not bound to the outer this shouldn’t count as usage. Solid negative tests.

Also applies to: 34-39


22-29: Setter‑only accessor used in member access is invalid (as intended).

Good to assert that [this.#accessorUsedInMemberAccess] = a doesn’t constitute a read without a getter.


41-50: Nit: keep comments consistent with other cases.

You already phrase it as “no meaningful read” elsewhere; this one matches now. All good.

crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (2)

148-154: Nice: no longer discarding the computed‑usage filter result.

Passing unused_members into traverse_meaningful_read_members_usage resolves the earlier bypass. Thanks for addressing it.


85-92: Good: fix kind + applicability wired.

fix_kind: FixKind::Unsafe and using ctx.metadata().applicability() in actions are in line with the project’s guidance. Based on learnings

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

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/services/semantic_class.rs (1)

579-617: LHS object destructuring is a write, not a read

collect_object_assignment_names marks ({ a: this.z } = obj) as a MeaningfulRead; it should be a Write (we assign to this.z). Current logic then feeds it into reads via the branch at Lines 841–851, which can incorrectly “use” write‑only members.

Apply this minimal fix:

-                        AccessKind::MeaningfulRead,
+                        AccessKind::Write,

No other branches need to change; the existing split on access_kind will now route to writes.

Also applies to: 841-851

🧹 Nitpick comments (6)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts.snap.new (1)

29-36: Snapshot intent matches “meaningful read” alignment

The cases capture setter-only writes, inner-class without outer this, and computed LHS without a getter. Looks good and reads crisply.

Consider adding a tiny positive case showing a compound assignment on a private accessor (e.g. this.#x += 1) to make the new “read+write” semantics explicit in snapshots.

Also applies to: 48-57

crates/biome_js_analyze/src/services/semantic_class.rs (5)

1003-1009: Missed meaningful-read contexts: variable initialisers

const x = this.foo is a genuine read, but is_used_in_expression_context doesn’t consider variable initialisers. Please extend to cover variable declarator initialiser contexts (or a general “initialiser clause” when parent isn’t a class property).

Suggested approach:

  • Treat JS_INITIALIZER_CLAUSE as meaningful unless its parent is JS_PROPERTY_CLASS_MEMBER (already handled by is_class_initializer_rhs).
  • Or include JS_VARIABLE_DECLARATOR with a check that you’re on the RHS.

Also applies to: 1065-1085


452-477: Alias detection only scans top-level statements; misses aliases in nested blocks

collect_local_this_references walks body.statements() and only variable statements, so if (...) { const alias = this } (then used inside the block) is missed. This can undercount reads/writes via aliases.

Consider a lightweight preorder over body.syntax() to catch const/let alias = this anywhere in the function body, not just top level. Optionally pick up simple alias = this assignments too.


772-782: Inconsistent ranges for member reads

Here you use the whole static member (this.foo) range; elsewhere you use the member token’s range. Normalise on the member token’s trimmed range for sharper diagnostics.

Apply:

-            range: static_member.syntax().text_trimmed_range(),
+            range: member.syntax().text_trimmed_range(),

Also applies to: 957-964


191-201: Tiny nit: comment typo and a TODO

The doc hints at adding accessor support; there’s a “claas” typo. Maybe add a tracking issue or TODO so it doesn’t drift.


211-262: Minor: prefer consistent range extraction in extract_named_member

For consistency with other sites and to avoid node‑specific range() quirks, prefer name_node.syntax().text_trimmed_range().

-                range: name_node.range(),
+                range: name_node.syntax().text_trimmed_range(),
📜 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 b937419 and adfd844.

📒 Files selected for processing (3)
  • crates/biome_js_analyze/src/services/semantic_class.rs (13 hunks)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts.snap.new (1 hunks)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts.snap.new (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts.snap.new
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts.snap.new
  • crates/biome_js_analyze/src/services/semantic_class.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts.snap.new
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts.snap.new
  • crates/biome_js_analyze/src/services/semantic_class.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts.snap.new
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts.snap.new
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
⏰ 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). (18)
  • GitHub Check: Test Node.js API
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Bench (biome_graphql_formatter)
  • GitHub Check: Bench (biome_graphql_parser)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_html_formatter)
  • GitHub Check: Check Dependencies
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Bench (biome_json_analyze)
  • GitHub Check: Bench (biome_json_parser)
  • GitHub Check: Bench (biome_html_parser)
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: Bench (biome_json_formatter)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_css_parser)
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: autofix
🔇 Additional comments (3)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts.snap.new (1)

8-20: LGTM: dynamic access doesn’t imply private name usage

The diagnostic and unsafe fix are appropriate for #prop when only this[name] is read.

Also applies to: 26-46

crates/biome_js_analyze/src/services/semantic_class.rs (2)

1290-1334: Test expectation questions: destructuring LHS counted as read

The test expects a meaningful read for ({ a: this.z } = obj). Per JS semantics, that’s a write to this.z (no prior value read). Recommend flipping this to a write to stay consistent with “setter‑only usage is not enough.”

If you agree, I can draft the test update after the code change above.


811-827: Extend compound assignment operator match
Current matches! only handles PIPE2EQ, AMP2EQ, SLASHEQ, STAREQ, PERCENTEQ, PLUSEQ, QUESTION2EQ. Add the missing variants for -= (MINUSEQ), ^= (CARETEQ), <<= (LTLTEQ), >>= (GTGTEQ), >>>= (GT2GTEQ) and the exponent‐assignment token for **=. Confirm the exact JsSyntaxKind names and update the matches! accordingly.

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 (2)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (2)

481-489: Diagnostic anchoring can still fail.

The past review comment on lines 481-490 remains unaddressed. member_range returns None when extract_named_member can't resolve, which could cause diagnostics to lose their anchor. Falling back to self.syntax().text_range() ensures diagnostics always have a range.

Apply this diff:

 fn member_range(&self, semantic_class: &SemanticClassModel) -> Option<TextRange> {
     if let Some(any_named_class_member) = AnyNamedClassMember::cast(self.syntax().clone())
         && let Some(prop_name) = semantic_class.extract_named_member(&any_named_class_member)
     {
         return Some(prop_name.range);
     }

-    None
+    Some(self.syntax().text_range())
 }

145-154: Critical: Still passing wrong vector to meaningful read traversal.

The past review comment on lines 146-154 remains unaddressed. Line 146 filters out computed-usage members into unused_members, but line 151 then passes the original private_members (not unused_members) to traverse_meaningful_read_members_usage, discarding the computed-usage filtering.

Apply this diff:

 let mut unused_members =
     traverse_computed_members_usage(node.syntax(), private_members.clone());

 if !unused_members.is_empty() {
     unused_members = traverse_meaningful_read_members_usage(
         &semantic_class,
-        private_members,
+        unused_members,
         &class_member_references,
     );
 }
🧹 Nitpick comments (3)
crates/biome_js_analyze/src/services/semantic_class.rs (2)

21-25: Consider adding PartialEq and Eq derives.

Since NamedClassMember contains Text and TextRange (both likely Eq), adding #[derive(PartialEq, Eq)] would enable direct equality checks and potentially allow use in hash-based collections for deduplication scenarios.


191-201: Address the TODO for accessor support.

The comment notes that accessor class members need to be added. This could affect completeness of the semantic analysis.

Do you want me to open an issue to track adding accessor support to AnyNamedClassMember?

crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (1)

331-346: Empty match arm may indicate incomplete refactoring.

Line 333's Ok(_) => {} suggests that AnyJsName nodes are intentionally skipped, but the empty block provides no clarity. Consider adding a comment explaining why names are excluded from computed-member checks, or removing the match if it serves no purpose.

📜 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 adfd844 and 0c5c7ae.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (2)
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (12 hunks)
  • crates/biome_js_analyze/src/services/semantic_class.rs (12 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format all Rust source files before committing (just f)

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
🧠 Learnings (2)
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/**/src/{lint,assist}/**/*.rs : If a rule returns a code action (implements `action`), add `fix_kind` in `declare_lint_rule!` and use `ctx.metadata().applicability()` when building the action

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
📚 Learning: 2025-09-10T08:05:22.867Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-09-10T08:05:22.867Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/{lint,assist}/**/*.rs : When banning globals (e.g., `noConsoleLog`), check the semantic model to avoid false positives from locally shadowed bindings

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
🧬 Code graph analysis (2)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (2)
crates/biome_js_syntax/src/expr_ext.rs (8)
  • member (1541-1546)
  • new (1400-1402)
  • name (108-110)
  • name (168-170)
  • name (1698-1712)
  • name (1743-1764)
  • name (1795-1815)
  • name (1872-1898)
crates/biome_js_analyze/src/services/semantic_class.rs (5)
  • new (48-50)
  • new (426-431)
  • default (42-44)
  • class_member_references (52-54)
  • class_member_references (271-327)
⏰ 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). (24)
  • GitHub Check: Bench (biome_package)
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: Bench (biome_configuration)
  • GitHub Check: Bench (biome_html_formatter)
  • GitHub Check: Bench (biome_html_parser)
  • GitHub Check: Bench (biome_graphql_formatter)
  • GitHub Check: Bench (biome_json_analyze)
  • GitHub Check: Bench (biome_json_formatter)
  • GitHub Check: Bench (biome_json_parser)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_graphql_parser)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: Bench (biome_css_parser)
  • GitHub Check: Documentation
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Test Node.js API
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: autofix
🔇 Additional comments (10)
crates/biome_js_analyze/src/services/semantic_class.rs (6)

41-50: LGTM!

The Default and constructor implementations follow Rust conventions correctly.


56-61: LGTM!

Clean delegation pattern. The public method provides a stable API while keeping implementation details private.


213-262: LGTM!

The extraction logic correctly handles each member type, with appropriate None return for index signatures and proper error propagation via ?.


845-850: LGTM!

The access kind discrimination properly routes references to either writes or reads based on AccessKind.


862-876: LGTM!

This correctly implements the meaningful read enhancement: assignments used in expression contexts (e.g., return this.#val = 3) are both writes and meaningful reads.


1011-1085: LGTM!

The three expression context helpers correctly identify meaningful reads:

  • is_class_initializer_rhs: RHS of property initialisers
  • is_assignment_expression_context: RHS and LHS object destructuring (both are reads)
  • is_general_expression_context: comprehensive coverage of expression contexts
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (4)

1-23: LGTM!

Imports properly support the semantic class integration with Arc for shared ownership.


100-110: LGTM!

Wrapping SemanticClassModel in Arc enables efficient shared ownership across multiple actions.


275-302: LGTM!

The filtering logic correctly retains only members without meaningful reads, using AccessKind::MeaningfulRead discrimination and semantic name matching.


491-499: LGTM!

matches_name correctly returns false when name extraction fails, providing safe fallback behaviour for comparison.

Copy link
Contributor

coderabbitai bot commented Oct 3, 2025

Note

Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

Ok(_) => {}
Err(node) => {
if ts_private_count != 0
&& let Some(computed_member) = AnyJsComputedMember::cast(node)
Copy link
Member

Choose a reason for hiding this comment

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

Will you create a new task for that?

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 (2)
crates/biome_js_analyze/src/services/semantic_class.rs (2)

41-45: Is the Default implementation necessary?

You've added an explicit new() constructor. The Default trait implementation delegates to new(), which is fine, but consider whether callers actually need Default::default() or if they can just call new() directly. If Default isn't required by a trait bound elsewhere, this might be unnecessary indirection.


1107-1138: Consider restoring AccessKind checks in test assertions.

The updated assertions at lines 1107–1117 and 1125–1137 now ignore the AccessKind (second element of the tuple). Whilst this simplifies the tests, it also means you won't catch bugs where the wrong AccessKind is returned (e.g., TrivialRead instead of MeaningfulRead). If the distinction matters for correctness, consider restoring those checks.

📜 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 0c5c7ae and 84c91b3.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/services/semantic_class.rs (15 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Before committing, format Rust and TOML files (e.g., via just f/just format)

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Document rules, assists, and options via inline rustdoc in Rust source

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/services/semantic_class.rs (2)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • TextRange (9205-9205)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
⏰ 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). (11)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Test Node.js API
  • 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: autofix
  • GitHub Check: Documentation
  • GitHub Check: Check Dependencies
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
🔇 Additional comments (4)
crates/biome_js_analyze/src/services/semantic_class.rs (4)

1036-1059: Verify object destructuring on LHS logic.

Lines 1051–1056 treat the LHS of an assignment as a read when it's an object assignment pattern. Typically, the LHS of an assignment is the write target, not a read. For example, in ({ a: this.z } = obj), this.z is being written to, not read.

However, if the intent is to detect reads from obj.a being assigned into this.z, that's handled by the RHS check (lines 1045–1049). Please verify this logic is correct, as it might flag false positives.


1564-1566: Test case expectation may be incomplete.

Line 1564 has the code for(this.i = 0; this.i < 10; this.i++) {}, which references this.i three times. The expected result only lists ("this.i", true) once. If the test helper parse_this_member_nodes_from_code returns one node per reference, you'd expect three entries. Please verify this is intentional and that the test is checking what you intend.


1004-1085: Nice refactor of is_used_in_expression_context.

Breaking out the three helper functions (is_class_initializer_rhs, is_assignment_expression_context, is_general_expression_context) makes the logic much clearer and easier to extend. The new test coverage is comprehensive, too. Well done.


211-262: extract_named_member implementation looks correct.

The match arms correctly extract the name and range for each member type, and you're returning None for index signatures, which makes sense. The new tests in extract_named_member_tests confirm the behaviour. Good work.

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_js_analyze/src/services/semantic_class.rs (1)

1107-1138: Consider enhancing test assertions.

Lines 1107 and 1126 now ignore the AccessKind component of expected tuples (changed from (expected_name, expected_access_kind) to (expected_name, _)). This simplifies the assertions but reduces test coverage—correctness of AccessKind (MeaningfulRead vs TrivialRead) is no longer verified.

If AccessKind correctness is critical, restore the checks:

-    for (expected_name, _) in expected {
+    for (expected_name, expected_kind) in expected {
         reads
             .iter()
-            .find(|r| r.name.clone().text() == *expected_name)
+            .find(|r| r.name.clone().text() == *expected_name && &r.access_kind == expected_kind)
             .unwrap_or_else(|| {
                 panic!(
-                    "Case '{}' failed: expected to find read '{}', but none was found in {:#?}",
+                    "Case '{}' failed: expected to find read '{}' with {:?}, but none was found in {:#?}",
-                    description, expected_name, reads
+                    description, expected_name, expected_kind, reads
                 )
             });
     }

Apply similar changes to assert_writes at lines 1125-1137.

📜 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 84c91b3 and f31bbc2.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/services/semantic_class.rs (15 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Before committing, format Rust and TOML files (e.g., via just f/just format)

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Document rules, assists, and options via inline rustdoc in Rust source

Files:

  • crates/biome_js_analyze/src/services/semantic_class.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
⏰ 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). (10)
  • GitHub Check: Test Node.js API
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Documentation
  • GitHub Check: Check Dependencies
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_parser)
🔇 Additional comments (10)
crates/biome_js_analyze/src/services/semantic_class.rs (10)

21-25: LGTM!

The NamedClassMember struct is well-structured for holding a class member's name and range.


41-50: LGTM!

Standard Rust idiom for providing a default constructor via Default trait.


56-61: LGTM!

Clean delegation to the private extraction logic.


192-201: LGTM with a note.

The union covers the expected named member types. The inline TODO on line 200 regarding accessor is noted for future work.


204-205: LGTM!

Adding JsStaticMemberAssignment to the union aligns with the new extraction capabilities.


213-262: LGTM!

The extraction logic correctly handles each member variant, with index signatures appropriately returning None.


845-876: LGTM!

The changes correctly distinguish between writes and reads in destructuring patterns (lines 845-850) and appropriately propagate MeaningfulRead when an assignment is used in an expression context (lines 862-875).


1003-1009: LGTM!

Refactoring into smaller helper functions improves readability and maintainability.


1020-1085: LGTM!

The three new helper functions correctly identify meaningful read contexts:

  • is_class_initializer_rhs detects RHS of class property initialisers.
  • is_assignment_expression_context detects RHS and LHS object destructuring.
  • is_general_expression_context covers a comprehensive list of expression contexts.

1211-1637: LGTM!

The test additions and modifications correctly reflect the new behaviour:

  • Test cases updated to align with new read/write semantics (lines 1211-1350).
  • New test case (lines 1314-1319) validates assignment-in-expression as a MeaningfulRead.
  • is_used_in_expression_context_tests module (lines 1441-1582) provides comprehensive coverage of expression contexts.
  • extract_named_member_tests module (lines 1584-1637) validates extraction logic for all member variants and edge cases.

@vladimir-ivanov vladimir-ivanov force-pushed the feat/no_unused_private_class_members_semantic_class branch from f31bbc2 to dcdad70 Compare October 3, 2025 20:20
@biomejs biomejs deleted a comment from coderabbitai bot Oct 4, 2025
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 dcdad70 and 34e433f.

📒 Files selected for processing (1)
  • .changeset/no_unused_private_class_members_amended.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/**/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

.changeset/**/*.md: Create changesets using the just new-changeset command; do not author them manually
In changeset markdown, only use headers #### or #####
Changeset descriptions must end every sentence with a full stop (.)
For bug fixes, start the changeset description with a linked issue reference like “Fixed #1234
Prefer past tense for what was done and present tense for current behavior in changesets

Files:

  • .changeset/no_unused_private_class_members_amended.md
⏰ 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). (11)
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Documentation
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_js_formatter)

@vladimir-ivanov
Copy link
Contributor Author

are we getting this reviewed anytime soon please? will be ready to do another pr soon, which will handle the dynamic this access. And then want to add additional checks to read return types of fns (e.g. parse esnext.d.ts files from typescript, and use the types in ts queries to deal with Object.clone return type (this) and others inside semantic_class.
Thank you

@ematipico
Copy link
Member

@vladimir-ivanov not sure I'll be able to review the PR soon, but have you looked at the performance degradation?

@vladimir-ivanov
Copy link
Contributor Author

thanks, in that case will merge the follow up pr into this one. handleDynamicAcees e.g. this[action]

@vladimir-ivanov
Copy link
Contributor Author

closing in favour of #7684

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.

Align no_unused_private_class_members and use_readonly_class_properties behaviour

2 participants