Skip to content

Conversation

@vladimir-ivanov
Copy link
Contributor

@vladimir-ivanov vladimir-ivanov commented Oct 5, 2025

Summary

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 #7682
closes #7499

@changeset-bot
Copy link

changeset-bot bot commented Oct 5, 2025

🦋 Changeset detected

Latest commit: 24d4595

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

@codspeed-hq
Copy link

codspeed-hq bot commented Oct 5, 2025

CodSpeed Performance Report

Merging #7684 will improve performances by 6.37%

Comparing vladimir-ivanov:feat/no_unused_private_class_members_dynamic_prop_access (24d4595) with main (6f5b876)

Summary

⚡ 1 improvement
✅ 57 untouched
⏩ 95 skipped1

Benchmarks breakdown

Benchmark BASE HEAD Change
js_analyzer[parser_13571644119461115204.ts] 99.8 ms 93.8 ms +6.37%

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.

@vladimir-ivanov vladimir-ivanov force-pushed the feat/no_unused_private_class_members_dynamic_prop_access branch from d7e33ef to 47b9b5b Compare October 12, 2025 14:08
@vladimir-ivanov vladimir-ivanov marked this pull request as ready for review October 12, 2025 14:09
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 12, 2025

Walkthrough

Reworks no_unused_private_class_members to be semantic-aware: introduces SemanticClassModel and NamedClassMember, swaps syntax-only traversal for semantic ClassMemberReferences and traverse_meaningful_read_members_usage so only meaningful reads count as usage (pure writes excluded), updates UnusedMemberAction/AnyMember APIs to accept semantic_class for name/range resolution, updates related lints (use_readonly_class_properties) to use named-member semantics, and adds/adjusts tests and changesets for dynamic this[...] access and meaningful-read semantics.

Possibly related PRs

Suggested reviewers

  • ematipico
  • Conaclos
  • dyc3

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: aligning the noUnusedPrivateClassMembers rule with semantic class analysis and improving dynamic property access detection.
Description check ✅ Passed The description is related to the changeset, providing concrete examples of reclassified valid/invalid cases and amendments to meaningful-reads logic.
Linked Issues check ✅ Passed The PR addresses both #7682 (dynamic property access refinement) and #7499 (rule alignment via semantic-class infrastructure). Code changes implement semantic-aware member tracking, computed property filtering by type narrowing, and unified ClassMemberReferences for consistency.
Out of Scope Changes check ✅ Passed All changes directly support the stated objectives: semantic-class infrastructure, meaningful-reads amendments, test case reclassification, and dynamic access type narrowing. Minor formatting adjustments in unrelated files are incidental.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 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 053b59c and 24d4595.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/services/semantic_class.rs (39 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_analyze/src/services/semantic_class.rs
⏰ 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: Check Dependencies
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Documentation
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: End-to-end tests
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_analyze)

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: 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/correctness/no_unused_private_class_members.rs (1)

807-823: Computed access misses union-typed locals

resolve_formal_param_type only handles formal parameters, so a union-typed local like const action: "add" | "remove" never registers as a meaningful read. With this change both #add and #remove would now be flagged as unused—previously the rule suppressed the warning. Please resolve the identifier’s type for any binding (variables, class fields, parameters) before extracting literals. A quick fix is to generalise the helper to accept JsIdentifierBinding (and friends) and look up their type annotation via the semantic model. Example to repro:

class Demo {
  #add() {}
  #remove() {}
  run() {
    const action: "add" | "remove" = "add";
    this[action]();
  }
}

This now fires, which is a regression.

🧹 Nitpick comments (2)
crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs (1)

145-184: Trim the clones; one pass is plenty.

We keep cloning constructor_params and writes for every candidate, so the work balloons with class size. Let’s reuse the original vectors and iterate them by reference instead:

-    let constructor_params: Vec<_> =
-        collect_non_readonly_constructor_parameters(&members, private_only);
-    let non_readonly_class_property_members =
-        collect_non_readonly_class_member_properties(&members, private_only);
-
-    constructor_params
-        .clone()
-        .into_iter()
-        .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()
-                })
-            }),
-        )
-        .filter_map(|prop_or_param| {
-            if writes.clone().into_iter().any(
-                |ClassMemberReference {
-                     name: class_member_ref_name,
-                     ..
-                 }| {
-                    if let Some(NamedClassMember {
-                        name: member_name, ..
-                    }) = ctx
-                        .semantic_class()
-                        .extract_named_member(&prop_or_param.clone())
-                    {
-                        return class_member_ref_name.eq(&member_name);
-                    }
-
-                    false
-                },
-            ) {
-                None
-            } else {
-                Some(prop_or_param.clone())
-            }
-        })
+    let constructor_params: Vec<_> =
+        collect_non_readonly_constructor_parameters(&members, private_only);
+    let constructor_param_texts: Vec<_> = constructor_params
+        .iter()
+        .map(|member| member.to_trimmed_text())
+        .collect();
+    let non_readonly_class_property_members =
+        collect_non_readonly_class_member_properties(&members, private_only);
+
+    constructor_params
+        .into_iter()
+        .chain(
+            non_readonly_class_property_members.filter(|class_property_member| {
+                !constructor_param_texts
+                    .iter()
+                    .any(|text| text == &class_property_member.to_trimmed_text())
+            }),
+        )
+        .filter_map(|prop_or_param| {
+            let Some(NamedClassMember {
+                name: member_name, ..
+            }) = ctx.semantic_class().extract_named_member(&prop_or_param)
+            else {
+                return Some(prop_or_param);
+            };
+
+            if writes.iter().any(
+                |ClassMemberReference {
+                     name: class_member_ref_name,
+                     ..
+                 }| class_member_ref_name == &member_name,
+            ) {
+                None
+            } else {
+                Some(prop_or_param)
+            }
+        })

</blockquote></details>
<details>
<summary>crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts (1)</summary><blockquote>

`44-72`: **Consider adding inline comments for consistency.**

The last three classes (`SampleYesNoString`, `SampleYesNoAny`, `SampleYesNoUnknown`) correctly test overly broad types (`string`, `any`, `unknown`) that should result in all private members being flagged as unused. For consistency with the earlier classes, consider adding inline comments to clarify expected behaviour, e.g., `// <- all unused`.



Apply this diff to improve test clarity:

```diff
 export class SampleYesNoString {
-	private yes: () => void;
-	private no: () => void;
-	private dontKnow: () => void;
+	private yes: () => void; // <- unused
+	private no: () => void; // <- unused
+	private dontKnow: () => void; // <- unused

	on(action: string): void {
		this[action]();
	}
}

export class SampleYesNoAny {
-	private yes: () => void;
-	private no: () => void;
-	private dontKnow: () => void;
+	private yes: () => void; // <- unused
+	private no: () => void; // <- unused
+	private dontKnow: () => void; // <- unused

	on(action: any): void {
		this[action]();
	}
}

export class SampleYesNoUnknown {
-	private yes: () => void;
-	private no: () => void;
-	private dontKnow: () => void;
+	private yes: () => void; // <- unused
+	private no: () => void; // <- unused
+	private dontKnow: () => void; // <- unused

	on(action: unknown): void {
		this[action]();
	}
}
📜 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 d6da4d5 and 47b9b5b.

⛔ Files ignored due to path filters (8)
  • 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 **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (14)
  • .changeset/no_unused_private_class_members_amended.md (1 hunks)
  • .changeset/no_unused_private_class_members_dynamic_usages.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 (12 hunks)
  • crates/biome_js_analyze/src/services/semantic_class.rs (40 hunks)
  • crates/biome_js_analyze/tests/quick_test.rs (1 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_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 (0 hunks)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts (2 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 (6)
.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_dynamic_usages.md
  • .changeset/no_unused_private_class_members_amended.md
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/quick_test.rs
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts
  • 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
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts
  • 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/quick_test.rs
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts
  • 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
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts
  • 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/quick_test.rs
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts
**/*.{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/tests/quick_test.rs
  • 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
  • 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/tests/quick_test.rs
  • 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
  • crates/biome_js_analyze/src/services/semantic_class.rs
🧠 Learnings (2)
📚 Learning: 2025-10-02T12:57:33.228Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-02T12:57:33.228Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/tests/quick_test.rs : Use tests/quick_test.rs for ad-hoc testing: un-ignore the test and set the rule filter (e.g., RuleFilter::Rule("nursery", "<ruleName>"))

Applied to files:

  • crates/biome_js_analyze/tests/quick_test.rs
📚 Learning: 2025-10-02T12:57:33.228Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-02T12:57:33.228Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/lint/nursery/*.rs : Place all new JavaScript lint rules in the nursery group under biome_js_analyze/lib/src/lint/nursery/<rule_name>.rs

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
🧬 Code graph analysis (5)
crates/biome_js_analyze/tests/quick_test.rs (1)
crates/biome_service/src/file_handlers/mod.rs (1)
  • rule_filter (1127-1127)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts (2)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts (2)
  • SampleAddRemove (3-14)
  • SampleYesNo (18-25)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (1)
  • action (194-252)
crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs (2)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (1)
  • diagnostic (160-192)
crates/biome_js_analyze/src/services/semantic_class.rs (3)
  • new (51-53)
  • new (94-96)
  • new (425-430)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (1)
crates/biome_js_analyze/src/services/semantic_class.rs (6)
  • semantic_class (36-38)
  • class_member_references (55-57)
  • class_member_references (280-328)
  • new (51-53)
  • new (94-96)
  • new (425-430)
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 (2)
  • SampleAddRemove (15-29)
  • SampleYesNo (34-42)
⏰ 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). (7)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Check Dependencies
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
🔇 Additional comments (3)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts (3)

1-13: Good baseline test case for untyped parameter.

The Sample class properly demonstrates the scenario where an untyped parameter in method(name) should be considered too broad to validate usage of member or #prop through this[name]. Both members should be flagged as unused, aligning with the PR objective to avoid false negatives when the key type is untyped.


15-29: Excellent coverage of string literal union constraint.

The SampleAddRemove class correctly demonstrates that a constrained union type "add" | "remove" should only validate usage of the matching private members, leaving append properly flagged as unused. This aligns precisely with the PR's goal to recognise only string literal unions as valid usages.


31-42: Well-crafted test for mixed union type filtering.

The YesNo type definition and SampleYesNo class nicely demonstrate that only string literals from a union should be extracted, ignoring the object type { ignored: number }. This ensures yes and no are considered used whilst dontKnow remains flagged as unused, as intended by the PR objectives.

@vladimir-ivanov vladimir-ivanov changed the title Feat/no unused private class members dynamic prop access feat(biome_js_analyze): align no_unused_private_class_members_with_semantic_class Oct 12, 2025
@vladimir-ivanov vladimir-ivanov changed the title feat(biome_js_analyze): align no_unused_private_class_members_with_semantic_class feat(biome_js_analyze): align no_unused_private_class_members_with_semantic_class and dynamic prop access Oct 12, 2025
@github-actions github-actions bot added the A-CLI Area: CLI label Oct 12, 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: 3

🧹 Nitpick comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)

1205-1217: Consider adding error context for type resolution failures.

The function resolve_formal_param_type returns None when it cannot resolve a parameter's type, but this could be due to several reasons (no binding, not a formal parameter, no type annotation). For debugging and telemetry, consider logging or tracking why resolution failed.

This is optional, as the current behaviour (silent fallback) is acceptable for production use.

📜 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 47b9b5b and e3b3fa3.

📒 Files selected for processing (7)
  • crates/biome_cli/tests/snapshots/main_commands_check/does_error_with_only_warnings.snap.new (1 hunks)
  • crates/biome_cli/tests/snapshots/main_commands_check/should_pass_if_there_are_only_warnings.snap.new (1 hunks)
  • crates/biome_cli/tests/snapshots/main_commands_ci/does_error_with_only_warnings.snap.new (1 hunks)
  • crates/biome_cli/tests/snapshots/main_commands_lint/does_error_with_only_warnings.snap.new (1 hunks)
  • crates/biome_cli/tests/snapshots/main_commands_lint/lint_syntax_rules.snap.new (1 hunks)
  • crates/biome_cli/tests/snapshots/main_commands_lint/should_pass_if_there_are_only_warnings.snap.new (1 hunks)
  • crates/biome_js_analyze/src/services/semantic_class.rs (40 hunks)
✅ Files skipped from review due to trivial changes (4)
  • crates/biome_cli/tests/snapshots/main_commands_ci/does_error_with_only_warnings.snap.new
  • crates/biome_cli/tests/snapshots/main_commands_lint/should_pass_if_there_are_only_warnings.snap.new
  • crates/biome_cli/tests/snapshots/main_commands_check/does_error_with_only_warnings.snap.new
  • crates/biome_cli/tests/snapshots/main_commands_check/should_pass_if_there_are_only_warnings.snap.new
🧰 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/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
  • crates/biome_cli/tests/snapshots/main_commands_lint/does_error_with_only_warnings.snap.new
  • crates/biome_cli/tests/snapshots/main_commands_lint/lint_syntax_rules.snap.new
**/*.{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
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_cli/tests/snapshots/main_commands_lint/does_error_with_only_warnings.snap.new
  • crates/biome_cli/tests/snapshots/main_commands_lint/lint_syntax_rules.snap.new
⏰ 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). (9)
  • GitHub Check: Test Node.js API
  • GitHub Check: Documentation
  • GitHub Check: autofix
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_js_formatter)
🔇 Additional comments (4)
crates/biome_cli/tests/snapshots/main_commands_lint/does_error_with_only_warnings.snap.new (1)

42-80: Confirm duplicate noClassAssign warnings
Please verify if the two identical noClassAssign warning blocks in crates/biome_cli/tests/snapshots/main_commands_lint/does_error_with_only_warnings.snap.new are intentional.

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

1731-1784: Good test coverage for extract_named_member.

The tests verify method, property, getter, setter, and index signature extraction. This provides solid coverage for the new named-member extraction API.


1260-1263: Fix duplicate text in panic message.

"but none was found" appears twice.

Apply this diff:

                     panic!(
-                        "Case '{}' failed: expected to find read '{}', but none was found in {:#?}",
+                        "Case '{}' failed: expected to find read '{}' in {:#?}",
                         description, expected_name, reads
                     )

Likely an incorrect or invalid review comment.


815-849: Add missing test coverage for dynamic member type-narrowing edge cases

Current tests only cover simple string-literal unions. Please add specs for:

  • type guards or as assertions
  • template literal types expanding to specific strings
  • enums that compile to string unions

@vladimir-ivanov vladimir-ivanov force-pushed the feat/no_unused_private_class_members_dynamic_prop_access branch 3 times, most recently from 01e5bc1 to 16c66aa Compare October 12, 2025 20:24
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_analyze/src/services/semantic_class.rs (2)

1186-1204: Refactor: extract literal string trimming to helper.

Lines 1195-1198 contain trim_matches(&['"', '\''][..]) logic that could be extracted to a small helper for clarity and reuse.

Apply this diff:

+/// Trims quotes from a string literal type's text representation
+fn trim_literal_quotes(text: &str) -> &str {
+    text.trim_matches(&['"', '\''][..])
+}
+
 fn extract_literal_types(union: &TsUnionType) -> Vec<Text> {
     extract_shallow_union_members(union)
         .iter()
         .filter_map(|item| {
             if let Some(literal_type) = TsStringLiteralType::cast(item.syntax().clone()) {
                 return Some(Text::new_owned(Box::from(
-                    literal_type
-                        .to_trimmed_text()
-                        .trim_matches(&['"', '\''][..]),
+                    trim_literal_quotes(&literal_type.to_trimmed_text()),
                 )));
             }

             None
         })
         .collect()
 }

824-860: extract_literal_types skips nested unions
As documented, nested union members are ignored—consider adding recursive extraction or clearly documenting this limitation.

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

98-106: Refactor: consider if struct variant adds value.

RemoveMember changed from RemoveMember(AnyMember) to RemoveMember { member: AnyMember }. Does the named field improve clarity for this single-field variant?

If the struct variant doesn't provide meaningful documentation benefit, consider reverting to the tuple variant for brevity:

 pub enum UnusedMemberAction {
-    RemoveMember {
-        member: AnyMember,
-    },
+    RemoveMember(AnyMember),
     RemovePrivateModifier {
         member: AnyMember,
         rename_with_underscore: bool,
     },
 }

Adjust the pattern matches accordingly:

-            Self::RemoveMember { member } => member.member_range(semantic_class),
+            Self::RemoveMember(member) => member.member_range(semantic_class),
crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs (1)

161-177: Consider simplifying the nested filter logic.

The logic is correct but quite verbose with multiple .clone() calls. Whilst cloning might be necessary for ownership, extracting the inner closure into a helper function could improve readability.

Example refactor:

fn has_write_reference(
    prop_or_param: &AnyNamedClassMember,
    writes: &[ClassMemberReference],
    semantic_class: &SemanticClassModel,
) -> bool {
    writes.iter().any(|ClassMemberReference { name: class_member_ref_name, .. }| {
        semantic_class
            .extract_named_member(prop_or_param)
            .is_some_and(|NamedClassMember { name: member_name, .. }| {
                class_member_ref_name.eq(&member_name)
            })
    })
}

Then use:

.filter_map(|prop_or_param| {
    if has_write_reference(&prop_or_param, &writes, ctx.semantic_class()) {
        None
    } else {
        Some(prop_or_param)
    }
})
📜 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 1e0aa10 and 16c66aa.

⛔ Files ignored due to path filters (8)
  • 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 **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (15)
  • .changeset/no_unused_private_class_members_amended.md (1 hunks)
  • .changeset/no_unused_private_class_members_dynamic_usages.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 (12 hunks)
  • crates/biome_js_analyze/src/services/semantic_class.rs (41 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_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 (0 hunks)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts (2 hunks)
  • crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid.ts.snap.new (1 hunks)
  • crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid_checkAllPropertiesTrue.ts.snap.new (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
✅ Files skipped from review due to trivial changes (1)
  • .changeset/no_unused_private_class_members_amended.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts
🧰 Additional context used
📓 Path-based instructions (6)
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/style/useReadonlyClassProperties/invalid_checkAllPropertiesTrue.ts.snap.new
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • 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/style/useReadonlyClassProperties/invalid.ts.snap.new
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid_checkAllPropertiesTrue.ts.snap.new
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • 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/style/useReadonlyClassProperties/invalid.ts.snap.new
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid_checkAllPropertiesTrue.ts.snap.new
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts
  • crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid.ts.snap.new
**/*.{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/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
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

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

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
.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_dynamic_usages.md
🧠 Learnings (1)
📚 Learning: 2025-10-02T12:57:33.228Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-02T12:57:33.228Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/lint/nursery/*.rs : Place all new JavaScript lint rules in the nursery group under biome_js_analyze/lib/src/lint/nursery/<rule_name>.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/lint/style/use_readonly_class_properties.rs (1)
crates/biome_js_analyze/src/services/semantic_class.rs (4)
  • semantic_class (35-37)
  • new (51-53)
  • new (101-103)
  • new (440-445)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (2)
crates/biome_js_analyze/src/services/semantic_class.rs (6)
  • semantic_class (35-37)
  • class_member_references (55-57)
  • class_member_references (293-341)
  • new (51-53)
  • new (101-103)
  • new (440-445)
crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs (3)
  • member (222-228)
  • ctx (133-133)
  • diagnostic (187-205)
⏰ 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: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Documentation
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: End-to-end tests
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Check Dependencies
  • GitHub Check: Test Node.js API
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: autofix
🔇 Additional comments (14)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts (1)

10-10: LGTM!

Typo corrected—unusedProperty now matches the test's intent.

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

22-26: LGTM!

NamedClassMember cleanly encapsulates member name and range for semantic-aware analysis.


44-53: Verify cache invalidation strategy.

SemanticClassModel introduces a RefCell<FxHashMap> cache for NamedClassMember lookups. Confirm that the cache doesn't retain stale entries if the AST is mutated during analysis phases.

Based on learnings


1216-1228: LGTM!

resolve_formal_param_type cleanly navigates from identifier → binding → formal parameter → type annotation.

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

255-282: LGTM!

traverse_meaningful_read_members_usage correctly filters out members with meaningful reads, returning only truly unused members.


400-418: Verify semantic_class cache hit rate.

Both member_range and matches_name call semantic_class.extract_named_member(), which hits the cache. Confirm the cache is effective during typical analysis runs.

Based on learnings

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

22-29: LGTM!

This test correctly validates that a setter-only accessor (without getter) in destructuring is not a meaningful read.

crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid.ts.snap.new (1)

1-356: LGTM!

Snapshot correctly documents diagnostics for the updated useReadonlyClassProperties rule using semantic-aware member extraction.

.changeset/no_unused_private_class_members_dynamic_usages.md (1)

15-29: Manually verify dontKnow is reported as unused with dynamic union access. Confirm Biome’s new semantic analysis correctly excludes dontKnow when calling this[action]() for action: YesNo.

crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid_checkAllPropertiesTrue.ts.snap.new (1)

1-279: LGTM! Comprehensive test snapshot.

The snapshot correctly validates the rule's behaviour with checkAllProperties: true, covering property fields, constructor parameters, and various visibility modifiers. Diagnostics and fixes are consistent with the semantic-class-based implementation.

crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs (4)

2-5: State type migration looks good.

The shift from AnyPropertyMember to AnyNamedClassMember aligns with the semantic-class integration. The new imports support semantic-aware member extraction and reference tracking.

Also applies to: 128-128


187-205: Diagnostic function correctly uses semantic class.

The signature change from _ to ctx is appropriate, and using semantic_class.extract_named_member aligns with the new semantic-aware approach. Returning None when extraction fails is sensible (no extractable name means no actionable diagnostic).


207-283: Action function handles both member types correctly.

The pattern matching on AnyNamedClassMember variants (lines 217-218 and 252-253) correctly handles both class property members and constructor parameters. The cloning of original_node is appropriate to avoid ownership issues.


285-393: Helper functions correctly updated for type migration.

The collection functions (collect_non_readonly_class_member_properties and collect_non_readonly_constructor_parameters) now consistently return AnyNamedClassMember, maintaining type safety throughout the refactor.

@vladimir-ivanov vladimir-ivanov force-pushed the feat/no_unused_private_class_members_dynamic_prop_access branch 3 times, most recently from f146954 to d951e0d Compare October 12, 2025 21:02
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: 2

♻️ Duplicate comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)

229-239: Typo in comment (already noted in past reviews).

Line 238: "claas" should be "class". This was flagged previously and marked as addressed, but appears to still be present in the code.

🧹 Nitpick comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)

1198-1255: Add tests for union type edge cases

Current specs only cover simple literal unions (e.g. YesNo). Add tests for:

  • generic type parameters involving unions
  • intersections containing unions (e.g. A & (B | C))
  • conditional types with union branches
  • mapped types over union keys

Place new cases under crates/biome_js_analyze/tests/specs/correctness.

📜 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 3383b75 and d951e0d.

⛔ Files ignored due to path filters (8)
  • 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 **
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (15)
  • .changeset/no_unused_private_class_members_amended.md (1 hunks)
  • .changeset/no_unused_private_class_members_dynamic_usages.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 (12 hunks)
  • crates/biome_js_analyze/src/services/semantic_class.rs (40 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_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 (0 hunks)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_aligned_with_semantic_class.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts (2 hunks)
  • crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid.ts.snap.new (1 hunks)
  • crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid_checkAllPropertiesTrue.ts.snap.new (1 hunks)
💤 Files with no reviewable changes (2)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid.js
✅ Files skipped from review due to trivial changes (1)
  • .changeset/no_unused_private_class_members_dynamic_usages.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_dynamic_access.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts
  • crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid.ts.snap.new
🧰 Additional context used
📓 Path-based instructions (6)
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/valid_aligned_with_semantic_class.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts
  • crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid_checkAllPropertiesTrue.ts.snap.new
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts
  • 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_issue_7101.ts
  • crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • 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/style/useReadonlyClassProperties/invalid_checkAllPropertiesTrue.ts.snap.new
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts
  • 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_issue_7101.ts
  • crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.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/valid_aligned_with_semantic_class.js
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid.ts
  • crates/biome_js_analyze/tests/specs/style/useReadonlyClassProperties/invalid_checkAllPropertiesTrue.ts.snap.new
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts
**/*.{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
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
  • crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.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
  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
  • crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs
.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
🧠 Learnings (1)
📚 Learning: 2025-10-02T12:57:33.228Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-02T12:57:33.228Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/lint/nursery/*.rs : Place all new JavaScript lint rules in the nursery group under biome_js_analyze/lib/src/lint/nursery/<rule_name>.rs

Applied to files:

  • crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs
🧬 Code graph analysis (4)
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 (2)
  • SampleAddRemove (15-29)
  • SampleYesNo (34-42)
crates/biome_js_analyze/src/lint/correctness/no_unused_private_class_members.rs (2)
crates/biome_js_analyze/src/services/semantic_class.rs (6)
  • semantic_class (38-40)
  • class_member_references (61-63)
  • class_member_references (301-349)
  • new (54-59)
  • new (109-111)
  • new (452-457)
crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs (3)
  • member (222-228)
  • ctx (133-133)
  • diagnostic (187-205)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_issue_7101.ts (1)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/invalid_aligned_with_semantic_class.ts (1)
  • foo (36-39)
crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs (1)
crates/biome_js_analyze/src/services/semantic_class.rs (4)
  • semantic_class (38-40)
  • new (54-59)
  • new (109-111)
  • new (452-457)
⏰ 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: Documentation
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_formatter)
🔇 Additional comments (8)
crates/biome_js_analyze/tests/specs/correctness/noUnusedPrivateClassMembers/valid_dynamic_access.ts (1)

3-25: Good call on literal unions

Nice to see the rule exercised with a narrowed union so we stop hand-waving every computed access. Keep them coming.

crates/biome_js_analyze/src/lint/style/use_readonly_class_properties.rs (2)

187-205: Good: Diagnostic now uses semantic context.

The diagnostic correctly derives member names via semantic_class.extract_named_member(), handling cases where extraction fails by returning None. This is more robust than the previous approach.


160-182: Semantic-aware filtering logic is correct. extract_named_member safely returns None for index signatures and the filter_map handles this case as intended.

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

258-282: Good: Semantic‑aware filtering improves accuracy.

The function now filters based on AccessKind::MeaningfulRead, which correctly distinguishes between writes, meaningful reads, and trivial reads. This aligns well with the PR objectives to reduce false negatives.


400-418: TsPropertyParameter is handled by extract_named_member
The extract_named_member match includes an arm for AnyNamedClassMember::TsPropertyParameter, so this variant is covered.

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

48-80: Good: Caching improves performance.

The SemanticClassModel now caches named member extraction results, which is essential given that extract_named_member is called repeatedly during analysis. The use of RefCell allows interior mutability without requiring &mut self.


1100-1196: Good: Comprehensive meaningful read detection.

The helper functions is_class_initializer_rhs, is_assignment_expression_context, and is_general_expression_context provide a robust way to determine whether a read is meaningful. This correctly handles:

  • Class property initialisers
  • Assignment RHS
  • Control flow contexts (if, switch, for)
  • Expression contexts (return, call args, etc.)

1752-1805: Good: Comprehensive test coverage for extract_named_member.

The tests verify name extraction for methods, properties, getters, setters, and correctly return None for index signatures. This provides confidence in the core extraction logic.

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 d951e0d and b446f6c.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/services/semantic_class.rs (40 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 (9264-9264)
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: Lint project (depot-windows-2022)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Documentation
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_parser)
🔇 Additional comments (29)
crates/biome_js_analyze/src/services/semantic_class.rs (29)

25-29: LGTM!

Clean struct definition for named class members.


31-45: LGTM!

Service struct properly exposes semantic data.


47-80: LGTM!

Caching logic is sound. RefCell is appropriate here as Biome's analyzer runs single-threaded within each file analysis.


82-96: LGTM!

Service initialization follows the established pattern.


104-130: LGTM!

Visitor wiring correctly propagates semantic data into the service bag.


132-152: LGTM!

Root discovery logic correctly handles the first class declaration.


173-180: LGTM!

Visitor construction properly initialized with root.


229-239: LGTM!

Union covers all named class member variants comprehensively.


249-292: LGTM!

Member name extraction handles all variants correctly, including the special case for index signatures.


301-349: LGTM!

Semantic model correctly propagated through all reference collection paths.


351-506: LGTM!

This-aliasing logic correctly handles scope inheritance, nested functions, and arrow functions. The skipping of nested classes prevents false positives.


508-543: LGTM!

Alias resolution correctly validates scope boundaries before confirming a reference.


682-702: LGTM!

Reference collection correctly initialised with semantic context.


710-759: LGTM!

Match statement improves readability over cascading if-let chains.


776-805: LGTM!

Boolean return value correctly signals whether the node was handled.


823-843: LGTM!

Handler return semantics are consistent with the refactored pattern.


845-881: Past concern: narrowed literal casts.

A previous review requested test coverage for narrowed literal casts via as (e.g., this[action as "add"]). The current implementation attempts to resolve the type but may not handle as casts explicitly.

Generate a test case to confirm narrowed casts are recognised:

class Example {
  #add() {}
  #remove() {}
  
  method(action: "add" | "remove") {
    // Should recognise #add as used via narrowing
    this[action as "add"]();
  }
}

Verify the dynamic access resolution correctly identifies #add as read when the type is narrowed via as.


1039-1059: LGTM!

Constructor reference collection correctly incorporates semantic context.


1067-1085: LGTM!

Static member read collection properly categorised by access kind.


1088-1107: LGTM!

Scope validation correctly handles shadowing by stopping at lexical boundaries.


1118-1129: LGTM!

Expression context detection comprehensively checks multiple ancestor contexts.


1131-1206: LGTM!

Helper predicates comprehensively identify meaningful read contexts. The logic correctly distinguishes class initialisers, assignment contexts, and general expression usage.


1207-1265: LGTM!

Type resolution utilities correctly extract string literal types from unions and resolve type aliases. The shallow union extraction aligns with the PR objective to handle string-literal unions without over-matching.


1266-1326: LGTM!

Test infrastructure is well structured and provides clear assertion failures.


1337-1456: LGTM!

Test coverage for object destructuring and static member access is thorough.


1458-1528: LGTM!

Assignment expression tests comprehensively cover compound assignments and destructuring patterns, including the return expression case.


1530-1616: LGTM!

Update expression tests verify correct read/write detection across various contexts.


1618-1759: LGTM!

Expression context tests are extensive and verify meaningful read detection across a wide range of language constructs.


1761-1814: LGTM!

Named member extraction tests verify correct behaviour for all member types, including the special index signature case.

Copy link
Contributor

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

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

By no means an in depth review, but at a glance it looks good. 👍

Comment on lines 9 to 47
- A *meaningful read* is any access that affects program behavior.
- For example, `this.#x += 1` both reads and writes `#x`, so it counts as usage.
- Pure writes without a read (e.g. `this.#x = 1` with no getter) are no longer treated as usage.

This change ensures that private members are only considered “used” when they are actually read in a way that influences execution.

***Invalid examples (previously valid)***

```ts
class UsedMember {
set #x(value) {
doSomething(value);
}

foo() {
// This assignment does not actually read #x, because there is no getter.
// Previously, this was considered a usage, but now it’s correctly flagged.
this.#x = 1;
}
}
```

***Valid example (Previously invalid)***

```js
class Foo {
#usedOnlyInWriteStatement = 5;

method() {
// This counts as a meaningful read because we both read and write the value.
this.#usedOnlyInWriteStatement += 42;
}
}
```

***Summary***
• Only accesses that read a value are considered meaningful for the purpose of this rule.
• Simple assignments to a setter without a corresponding getter no longer count as usage.
• Operations like +=, method calls returning a value, or reading the property for computation are considered meaningful reads.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I feel like this is just a little too verbose for what users actually care about. Maybe others feel differently.

@vladimir-ivanov
Copy link
Contributor Author

By no means an in depth review, but at a glance it looks good. 👍

needs a lot of work still, but thanks, experimenting with perf optimisation. Wont be ready for review until I make it green.

@vladimir-ivanov vladimir-ivanov force-pushed the feat/no_unused_private_class_members_dynamic_prop_access branch 2 times, most recently from 416d1ae to fba354c Compare October 14, 2025 20:27
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

♻️ Duplicate comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)

887-896: Missing compound assignment operators.

The compound assignment detection at lines 887-896 only covers:

  • ||=, &&=, /=, *=, %=, +=, ??=

It's missing:

  • -= (MINUSEQ)
  • **= (exponentiation assignment)
  • <<=, >>=, >>>= (bitwise shift assignments)
  • &=, |=, ^= (bitwise assignments)

All compound assignments read the old value before writing, so they should all trigger meaningful reads.

As per the past review comment from dyc3, extend the matches! to include all compound operators:

             && matches!(
                 operator.kind(),
                 JsSyntaxKind::PIPE2EQ
                     | JsSyntaxKind::AMP2EQ
                     | JsSyntaxKind::SLASHEQ
                     | JsSyntaxKind::STAREQ
                     | JsSyntaxKind::PERCENTEQ
                     | JsSyntaxKind::PLUSEQ
                     | JsSyntaxKind::QUESTION2EQ
+                    | JsSyntaxKind::MINUSEQ          // -=
+                    | JsSyntaxKind::STARSTAREQ       // **= (check exact name)
+                    | JsSyntaxKind::LT2EQ            // <<=
+                    | JsSyntaxKind::GT2EQ            // >>=
+                    | JsSyntaxKind::GT3EQ            // >>>=
+                    | JsSyntaxKind::AMPEQ            // &=
+                    | JsSyntaxKind::PIPEEQ           // |=
+                    | JsSyntaxKind::CARETEQ          // ^=
             )

Note: Verify the exact JsSyntaxKind variant names for **= and the bitwise operators in your codebase.

📜 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 209a878 and fba354c.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/services/semantic_class.rs (40 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)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • TextRange (9264-9264)
⏰ 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: Documentation
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: End-to-end tests
  • GitHub Check: Check Dependencies
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Test Node.js API
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: autofix
  • GitHub Check: Bench (biome_js_formatter)
🔇 Additional comments (7)
crates/biome_js_analyze/src/services/semantic_class.rs (7)

25-29: Well-defined struct for named members.

The NamedClassMember structure clearly separates the name from its source range, which is essential for accurate diagnostics.


47-80: Caching strategy looks sound.

Using RefCell<FxHashMap<SyntaxNodePtr, Option<NamedClassMember>>> for caching named members is appropriate for interior mutability in a query-based system. The extract_named_member method correctly checks the cache before computing.


223-233: Clear documentation with examples.

The inline comments for each variant of AnyNamedClassMember are helpful. Note: Line 232 still contains the typo "claas" → should be "class" (though marked as addressed in a previous commit).


243-286: extract_named_member handles all member types correctly.

The function properly returns None for index signatures (which lack a traditional name) and extracts names for all other member types. The use of to_named helper reduces duplication.


1091-1165: Expression context helpers are comprehensive.

The is_class_initializer_rhs, is_assignment_expression_context, and is_general_expression_context functions cover a wide range of cases where a read is meaningful. The logic correctly distinguishes between RHS (reads) and LHS contexts.


1167-1224: Type resolution logic is well-structured.

The functions extract_literal_types, resolve_formal_param_type, and resolve_reference_to_union properly handle:

  • Extracting string literals from union types
  • Resolving formal parameter type annotations
  • Following type aliases to their underlying unions

This aligns with the PR objective to handle string-literal unions.


1226-1774: Comprehensive test coverage.

The test suite covers:

  • Object binding patterns
  • Static member expressions
  • Assignment expressions (including compound assignments)
  • Pre/post update expressions
  • Expression context detection
  • Named member extraction

Good use of TestCase structs and helper functions.

@vladimir-ivanov
Copy link
Contributor Author

big win, no more perf issues, interesting how small optimisations make big difference.

…ndle dynamic access class members based on their ts type
…ndle dynamic access class members based on their ts type
…ndle dynamic access class members based on their ts type
@vladimir-ivanov vladimir-ivanov force-pushed the feat/no_unused_private_class_members_dynamic_prop_access branch from 59dfee8 to 2c83ce7 Compare November 13, 2025 18:41
Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

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

Great work. @vladimir-ivanov

…ndle dynamic access class members based on their ts type
@vladimir-ivanov vladimir-ivanov force-pushed the feat/no_unused_private_class_members_dynamic_prop_access branch from 0c57f9c to e5f2c63 Compare November 14, 2025 20:25
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

♻️ Duplicate comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)

879-888: Address the missing compound assignment operators.

As noted in the previous review, this matches clause is still missing several compound assignment operators (-=, **=, <<=, >>=, >>>=, &=, |=, ^=). All compound assignments should read the old value before writing.

Apply this diff to include all compound operators:

             && matches!(
                 operator.kind(),
                 JsSyntaxKind::PIPE2EQ
                     | JsSyntaxKind::AMP2EQ
                     | JsSyntaxKind::SLASHEQ
                     | JsSyntaxKind::STAREQ
                     | JsSyntaxKind::PERCENTEQ
                     | JsSyntaxKind::PLUSEQ
+                    | JsSyntaxKind::MINUSEQ
+                    | JsSyntaxKind::STAR2EQ
+                    | JsSyntaxKind::LT2EQ
+                    | JsSyntaxKind::RT2EQ
+                    | JsSyntaxKind::RT3EQ
+                    | JsSyntaxKind::AMPEQ
+                    | JsSyntaxKind::PIPEEQ
+                    | JsSyntaxKind::CARETEQ
                     | JsSyntaxKind::QUESTION2EQ
             )
📜 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 0c57f9c and e5f2c63.

📒 Files selected for processing (2)
  • crates/biome_js_analyze/src/lint/suspicious/no_class_assign.rs (1 hunks)
  • crates/biome_js_analyze/src/services/semantic_class.rs (39 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
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.
📚 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_analyze/src/services/semantic_class.rs
⏰ 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: 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: Check Dependencies
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Test Node.js API
  • GitHub Check: autofix
🔇 Additional comments (7)
crates/biome_js_analyze/src/services/semantic_class.rs (7)

22-42: Well-structured public API.

The NamedClassMember struct and SemanticClassServices updates provide a clean interface for semantic-aware class member analysis.


816-848: Dynamic member handling correctly implements PR objectives.

This function properly resolves formal parameter types to string-literal unions and records reads only for resolved literals, which addresses the main goal of avoiding false positives with broad types like string or any.


101-140: Solid semantic model integration.

The visitor correctly wires up the semantic event extraction and model building, enabling semantic-aware analysis throughout the class member reference collection.


44-77: Efficient caching strategy.

The RefCell<FxHashMap> cache for named members is a sensible optimisation, avoiding repeated AST traversal for member name extraction.


1159-1216: Type resolution logic looks correct.

The functions properly extract string literals from union types and resolve type aliases, which enables precise tracking of dynamic member access.


1061-1157: Comprehensive expression context detection.

These helpers correctly distinguish meaningful reads (used in logic/computation) from trivial ones (standalone statements), which is crucial for the rule's accuracy.


1218-1767: Thorough test coverage.

The test suite comprehensively verifies read/write detection, this-aliasing, and the distinction between meaningful and trivial reads across various contexts.

…ndle dynamic access class members based on their ts type
@vladimir-ivanov vladimir-ivanov force-pushed the feat/no_unused_private_class_members_dynamic_prop_access branch from 244a10e to e7ea8c4 Compare November 14, 2025 20:37
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)
.changeset/no_unused_private_class_members_amended.md (1)

1-42: Consider condensing examples per prior feedback on verbosity.

Previous reviewers flagged that the changelog was too verbose and suggested removing detailed examples. The current entry still includes two full code blocks and expanded explanations. Whilst the content is accurate and technically sound, consider whether end users need this level of detail in the changelog, or whether a shorter summary with a link to migration documentation would better serve the audience.

A condensed version might look like:

  • One-line summary of the change
  • Bullet point of key behaviour shifts
  • Link to updated documentation for migration guidance

This would align with the principle that changelogs should highlight what changed, whilst detailed examples belong in guides or rule documentation.

📜 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 e7ea8c4 and 053b59c.

📒 Files selected for processing (1)
  • .changeset/no_unused_private_class_members_amended.md (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
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.
⏰ 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: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: End-to-end tests
  • 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: Validate rules documentation
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: autofix

@vladimir-ivanov vladimir-ivanov merged commit f4433b3 into biomejs:main Nov 14, 2025
18 checks passed
@vladimir-ivanov vladimir-ivanov deleted the feat/no_unused_private_class_members_dynamic_prop_access branch November 14, 2025 21:14
@github-actions github-actions bot mentioned this pull request Nov 14, 2025
ematipico added a commit to hamirmahal/biome that referenced this pull request Nov 19, 2025
…mantic_class and dynamic prop access (biomejs#7684)

Co-authored-by: Emanuele Stoppa <[email protected]>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
@ematipico ematipico mentioned this pull request Nov 19, 2025
github-merge-queue bot pushed a commit that referenced this pull request Nov 21, 2025
ryan-m-walker pushed a commit to ryan-m-walker/biome that referenced this pull request Nov 23, 2025
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 L-JavaScript Language: JavaScript and super languages

Projects

None yet

3 participants