-
-
Notifications
You must be signed in to change notification settings - Fork 794
feat(biome-js-analyze): add is_meaningful_read to semantic class reads #7543
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(biome-js-analyze): add is_meaningful_read to semantic class reads #7543
Conversation
|
WalkthroughAdds a public Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
18-18: Redundant import:std::option::Optionis unnecessary.The
Optiontype is already in the prelude and doesn't need explicit importing.-use std::option::Option;
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(30 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧬 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). (23)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: autofix
- GitHub Check: Check Dependencies
- GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
- GitHub Check: Documentation
- GitHub Check: Test Node.js API
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_js_formatter)
🔇 Additional comments (14)
crates/biome_js_analyze/src/services/semantic_class.rs (14)
5-13: LGTM: Clean import additions for the new functionality.The new imports for
AnyJsObjectBindingPatternMember,JsPostUpdateExpression, andJsPreUpdateExpressionare appropriately added to support the meaningful read tracking feature.
133-133: Well-designed extension to the data model.The addition of
is_meaningful_read: Option<bool>toClassMemberReferenceprovides clear semantics:Some(true)for meaningful reads,Some(false)for non-meaningful reads, andNonefor writes. This is a solid design choice.
146-152: Excellent type system design with union types.The introduction of
MeaningfulReadNodeandAnyJsUpdateExpressionunions provides a clean abstraction for nodes that can participate in meaningful read analysis. The type composition is logical and well-structured.
362-364: Correct meaningful read classification for variable initialisers.Marking the right-hand side of variable statements as meaningful reads (
Some(true)) is appropriate since these expressions are being evaluated for their values.
420-420: Consistent write reference handling.The systematic use of
Noneforis_meaningful_readacross all write operations (array assignments, object assignments, destructuring patterns) maintains consistent semantics where writes are not considered meaningful reads.Also applies to: 441-443, 458-460, 488-490, 505-507
721-723: Clear meaningful read semantics in assignments.The distinction between compound assignments (
Some(true)for reads) and regular assignments (Nonefor writes) correctly reflects the semantic difference between operations that consume values versus those that only store them.Also applies to: 749-751
772-802: Sophisticated handling of update expressions.The implementation correctly models update expressions as producing both a read and a write reference, with the read being contextually determined by
is_meaningful_read. The separation into distinctClassMemberReferenceinstances is the right approach.
872-874: Clean public API design.The public
is_meaningful_readfunction provides a clean interface for external consumers whilst encapsulating the internal logic inis_used_in_expression_context.
876-907: Robust expression context analysis with proper safeguards.The implementation correctly handles parentheses skipping and includes appropriate loop limits to prevent infinite recursion. The expression context detection covers the key scenarios where reads are meaningful.
919-921: Excellent test structure enhancement.The updated test case structure with
(name, is_meaningful_read)tuples provides comprehensive validation of the new functionality whilst maintaining clarity.
1003-1003: Verify test expectations for object binding patterns.The tests expect
Some(false)for object binding pattern reads, but these might actually be meaningful in contexts likeconst { foo } = this; return foo;. Consider whether these should beSome(true)instead.Based on the web search results, object destructuring in JavaScript "makes it possible to unpack values from arrays, or properties from objects, into distinct variables" and is primarily used for "extracting property values and assigning them to a variable".
The key insight is that destructuring is fundamentally about extracting and binding values to variables, not necessarily about consuming those values in a meaningful expression context. In
const { foo } = this;, thefoois being extracted and bound to a new variable, which is different from usingfooin an expression likereturn fooorconsole.log(foo).The test expectations of
Some(false)for object binding patterns appear to be correct, as the destructuring operation itself is not a "meaningful read" in the expression evaluation sense—it's a binding operation that creates variables for later use.Also applies to: 1016-1016, 1053-1053, 1067-1067
1184-1195: Excellent context-aware meaningful read detection in tests.The test cases brilliantly demonstrate the nuanced meaningful read detection: update expressions in simple statements are
Some(false), but the same expressions in conditional or return contexts correctly showSome(true). This validates theis_used_in_expression_contextlogic.Also applies to: 1211-1220
1254-1371: Comprehensive test coverage for the new API.The dedicated test module for
is_meaningful_readprovides thorough validation across various expression contexts. The test structure efficiently extracts meaningful nodes and validates the expected behaviour patterns.
637-640: No action needed—declare_node_union!providesFrom<AnyJsObjectBindingPatternMember>forMeaningfulReadNode, sodeclarator.into()correctly converts the node foris_meaningful_read.
CodSpeed Performance ReportMerging #7543 will not alter performanceComparing Summary
|
|
also also thinking of alternative names, if we want this functionality at all of course: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
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/services/semantic_class.rs (2)
129-134: Addingis_meaningful_readto a Hash/Eq type changes set semantics; dedupe will now treat the same reference with different flags as different entries.This will produce duplicate reads for the same member/range when observed in different contexts (true/false). Recommend excluding
is_meaningful_readfrom identity and hashing.-#[derive(Debug, Clone, Hash, Eq, PartialEq)] +#[derive(Debug, Clone)] pub struct ClassMemberReference { pub name: Text, pub range: TextRange, pub is_meaningful_read: Option<bool>, } + +impl PartialEq for ClassMemberReference { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.range == other.range + } +} + +impl Eq for ClassMemberReference {} + +impl Hash for ClassMemberReference { + fn hash<H: Hasher>(&self, state: &mut H) { + self.name.hash(state); + self.range.hash(state); + } +}
706-724: Expand compound-assignment operator coverage
The current match only handles|=,&=,/=,*=,%=,+=and??=; you must also include the remaining assignment operators (-=,<<=,>>=,>>>=,^=,**=,||=,&&=) or replace thematches!with a helper likeis_compound_assignment_operator(kind).
🧹 Nitpick comments (7)
crates/biome_js_analyze/src/services/semantic_class.rs (7)
5-13: Tidy imports; remove redundant Option, add Hasher for custom Eq/Hash.
Optionis in prelude; importing it is unnecessary. You’ll also needHasherif you adopt the custom Eq/Hash suggestion below.- use std::option::Option; + use std::hash::{Hash, Hasher};Also applies to: 18-18
362-364: Don’t mark aliasingthisas a meaningful read.These entries feed alias tracking, not class-member reads. Mark as
Noneto avoid confusion if they ever leak.- // right hand side of a js variable statement is meaningful read - is_meaningful_read: Some(true), + // Alias registration only; not a class-member read. + is_meaningful_read: None,
636-641: Object binding: extract the actual property name and a stable range.Using
to_trimmed_text()and the node’s full range can capture “foo: bar” rather than just “foo” and will vary across shapes, hurting dedupe and UX. Prefer extracting the property key token and its range (and handle shorthand/aliased/rest cases explicitly).Would you like me to draft a helper to normalise “object binding member -> (property_name, range)”, covering
property,shorthand, andrest?
772-803: LGTM on update read/write split; tiny nit on duplication.You rebuild two
ClassMemberReferences fromname; consider insertingname.clone()directly for the write to avoid recomputing fields.
876-886:skip_parentheseswalks down; it should walk up.As written, it rarely strips anything. Walk ancestors while the node is a parenthesised expr.
-fn skip_parentheses(mut node: JsSyntaxNode) -> JsSyntaxNode { - while let Some(child) = node.first_child() { - if child.kind() == JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION { - node = child; // move ownership - } else { - break; - } - } - node -} +fn skip_parentheses(mut node: JsSyntaxNode) -> JsSyntaxNode { + // Move up past any ParenthesizedExpression wrappers. + while node.kind() == JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION { + if let Some(parent) = node.parent() { + node = parent; + } else { + break; + } + } + node +}
887-907: Broaden meaningful-context detection; handle RHS of=, variable/field initialisers, and unary contexts.This fixes cases like
a = this.x,field = this.x, andif (!this.x).-fn is_used_in_expression_context(node: &MeaningfulReadNode) -> Option<bool> { - let mut current: JsSyntaxNode = node.syntax().clone(); - - // Limit the number of parent traversals to avoid deep recursion - for _ in 0..8 { - if let Some(parent) = current.parent() { - let parent = skip_parentheses(parent); // strip parentheses - match parent.kind() { - JsSyntaxKind::JS_RETURN_STATEMENT - | JsSyntaxKind::JS_CALL_ARGUMENTS - | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION - | JsSyntaxKind::JS_LOGICAL_EXPRESSION - | JsSyntaxKind::JS_BINARY_EXPRESSION => return Some(true), - _ => current = parent, - } - } else { - break; - } - } - Some(false) -} +fn is_used_in_expression_context(node: &MeaningfulReadNode) -> Option<bool> { + let mut current: JsSyntaxNode = node.syntax().clone(); + let mut steps: u8 = 0; + while let Some(mut parent) = current.parent() { + steps = steps.saturating_add(1); + if steps > 64 { + break; + } + parent = skip_parentheses(parent); + match parent.kind() { + JsSyntaxKind::JS_RETURN_STATEMENT + | JsSyntaxKind::JS_CALL_ARGUMENTS + | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION + | JsSyntaxKind::JS_LOGICAL_EXPRESSION + | JsSyntaxKind::JS_BINARY_EXPRESSION + | JsSyntaxKind::JS_UNARY_EXPRESSION => return Some(true), + JsSyntaxKind::JS_ASSIGNMENT_EXPRESSION => { + if let Some(assign) = JsAssignmentExpression::cast_ref(&parent) { + if let Ok(rhs) = assign.right() { + if rhs.syntax().text_range().contains_range(current.text_range()) { + return Some(true); + } + } + } + current = parent; + } + JsSyntaxKind::JS_VARIABLE_DECLARATOR => { + // `const x = this.y;` alone isn’t meaningful (usage unknown). + return Some(false); + } + JsSyntaxKind::JS_PROPERTY_CLASS_MEMBER => { + // `field = this.y;` in a class field initialiser is meaningful. + return Some(true); + } + _ => current = parent, + } + } + Some(false) +}
1254-1372: Add a few focused tests to pin semantics.
class C { m(){ const a = this.x; } }→this.xshould be not meaningful (false).class C { m(){ a = this.x; } }→ meaningful (true).class C { m(){ if (!this.x){} } }→ meaningful (true).class C { field = this.x; }→ meaningful (true).I can push these cases into
is_meaningful_read_testsand the body collectors.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(30 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧬 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). (16)
- GitHub Check: Test Node.js API
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: autofix
🔇 Additional comments (2)
crates/biome_js_analyze/src/services/semantic_class.rs (2)
146-152: Public API surface: confirm visibility of new unions.If these are meant only for analyse internals, consider narrowing to
pub(crate); if they’re part of the public API for rule authors,pubis fine. Please confirm intended audience.
592-599: Nice: explicit handling of pre/post updates.Good call to split read/write and annotate meaning on the read side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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/services/semantic_class.rs (2)
631-640: Object binding reads: use the property key’s span and name (not the whole member)For
{ key: alias } = this, we should recordkey, and the range of the key token, not the entire binding member text. Current code will store"key: alias"and its span, which breaks identity and downstream matching.- reads.insert(ClassMemberReference { - name: declarator.clone().to_trimmed_text(), - range: declarator.clone().syntax().text_trimmed_range(), - is_meaningful_read: is_meaningful_read(&declarator.clone().into()), - }); + if let Some((name, range)) = extract_object_binding_member_key(&declarator) { + reads.insert(ClassMemberReference { + name, + range, + is_meaningful_read: is_meaningful_read( + &MeaningfulReadNode::from(declarator.clone()) + ), + }); + }Helper to add (outside this hunk):
fn extract_object_binding_member_key( member: &AnyJsObjectBindingPatternMember, ) -> Option<(Text, TextRange)> { if let Some(prop) = member.as_js_object_binding_pattern_property() { let key = prop.key().ok()?; // identifier/literal member name return Some((key.to_trimmed_text(), key.syntax().text_trimmed_range())); } if let Some(shorthand) = member.as_js_object_binding_pattern_shorthand_property() { let ident = shorthand.identifier().ok()?; // accessor may differ; use the key token return Some((ident.to_trimmed_text(), ident.syntax().text_trimmed_range())); } None // ignore rest }
705-723: Compound assignment detection misses operators (e.g. -=, <<=, >>>=, ^=, |=, &&=, ||=, ??=)Rather than whitelisting, treat any assignment with an operator other than
=as a read+write.- if let Ok(operator) = assignment.operator_token() - && let Some(operand) = left.as_any_js_assignment() - && matches!( - operator.kind(), - JsSyntaxKind::PIPE2EQ - | JsSyntaxKind::AMP2EQ - | JsSyntaxKind::SLASHEQ - | JsSyntaxKind::STAREQ - | JsSyntaxKind::PERCENTEQ - | JsSyntaxKind::PLUSEQ - | JsSyntaxKind::QUESTION2EQ - ) + if let Ok(operator) = assignment.operator_token() + && let Some(operand) = left.as_any_js_assignment() + && operator.kind() != JsSyntaxKind::EQ && let Some(name) = ThisPatternResolver::extract_this_member_reference( operand.as_js_static_member_assignment(), scoped_this_references, // nodes inside assignment expressions are considered meaningful reads e.g. this.x += 1; Some(true), )
🧹 Nitpick comments (5)
crates/biome_js_analyze/src/services/semantic_class.rs (5)
18-18: Drop redundant import of Option
Optionis in the prelude; the explicit import is noise.-use std::option::Option;
129-134: Clarify tri‑state with an enum rather than Option
Nonemeans “not a read”,Some(true|false)are semantics. An explicit enum (e.g.,ReadMeaningfulness::{Meaningful, NotMeaningful}) improves readability and avoids Boolean traps. If changing the type is too wide for this PR, at least add a doc‑comment documenting the tri‑state.
889-906: Make context climb unbounded (with a sane cap) and include awaited/yielded usesEight levels can be shallow in real code; also, awaited/yielded reads are meaningful.
- // Limit the number of parent traversals to avoid deep recursion - for _ in 0..8 { - if let Some(parent) = current.parent() { - let parent = skip_parentheses(parent); // strip parentheses - match parent.kind() { - JsSyntaxKind::JS_RETURN_STATEMENT - | JsSyntaxKind::JS_CALL_ARGUMENTS - | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION - | JsSyntaxKind::JS_LOGICAL_EXPRESSION - | JsSyntaxKind::JS_BINARY_EXPRESSION => return Some(true), - _ => current = parent, - } - } else { - break; - } - } + let mut hops = 0usize; + while let Some(parent) = current.parent() { + let parent = skip_parentheses(parent); // strip parentheses + match parent.kind() { + JsSyntaxKind::JS_RETURN_STATEMENT + | JsSyntaxKind::JS_CALL_ARGUMENTS + | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION + | JsSyntaxKind::JS_LOGICAL_EXPRESSION + | JsSyntaxKind::JS_BINARY_EXPRESSION + | JsSyntaxKind::JS_AWAIT_EXPRESSION + | JsSyntaxKind::JS_YIELD_EXPRESSION => return Some(true), + _ => current = parent, + } + hops += 1; + if hops > 32 { break; } + } Some(false)
1099-1162: Add coverage for other compound ops and aliasing in object bindingsPlease add cases for
-=,*=,**=,<<=,>>=,>>>=,|=,^=,&&=,||=,??=and for{ key: alias } = thisto ensure we recordkeywith the key’s span.I can draft the tests if you like.
871-874: Minor API naming nitIf you keep the boolean, consider
is_effective_readoris_semantically_read(as per PR description) – both read more naturally than “meaningful”.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(30 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧬 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). (9)
- GitHub Check: Documentation
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- 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)
🔇 Additional comments (4)
crates/biome_js_analyze/src/services/semantic_class.rs (4)
146-152: Public unions look goodThe
MeaningfulReadNodeandAnyJsUpdateExpressionunions are a sensible surface for the helper.
671-675: Use the member’s range, not the wholethis.foospanreads.insert(ClassMemberReference { name: member.to_trimmed_text(), - range: static_member.syntax().text_trimmed_range(), + range: member.syntax().text_trimmed_range(), is_meaningful_read: is_meaningful_read(&static_member.into()), });
839-844: Same normalisation for property initialisersRecord the member token span, not the entire static member expression.
reads.insert(ClassMemberReference { name, - range: static_member.syntax().text_trimmed_range(), + range: member.syntax().text_trimmed_range(), is_meaningful_read: is_meaningful_read(&MeaningfulReadNode::from( static_member.clone(), )), });
361-363: LGTM: initialiser reads marked meaningfulTreating RHS of
const alias = this;as meaningful is aligned with the intended signal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
705-723: Compound-assignment detection is incomplete — treat any non-'=' operator as compoundExplicit-kind matching omits several compound operators (e.g. '-=', '^=', '<<=', '>>=', '>>>='). Replace the matches list with a generic check: operator.text_trimmed().text() != "=".
- if let Ok(operator) = assignment.operator_token() - && let Some(operand) = left.as_any_js_assignment() - && matches!( - operator.kind(), - JsSyntaxKind::PIPE2EQ - | JsSyntaxKind::AMP2EQ - | JsSyntaxKind::SLASHEQ - | JsSyntaxKind::STAREQ - | JsSyntaxKind::PERCENTEQ - | JsSyntaxKind::PLUSEQ - | JsSyntaxKind::QUESTION2EQ - ) + if let Ok(operator) = assignment.operator_token() + && let Some(operand) = left.as_any_js_assignment() + // any assignment operator that isn't "=" is compound + && operator.text_trimmed().text() != "=" && let Some(name) = ThisPatternResolver::extract_this_member_reference( operand.as_js_static_member_assignment(), scoped_this_references, // nodes inside assignment expressions are considered meaningful reads e.g. this.x += 1; Some(true), )Add a quick test for
-=/|=/^=/<<=/>>=/>>>=?
🧹 Nitpick comments (5)
crates/biome_js_analyze/src/services/semantic_class.rs (5)
18-18: Drop the redundant import
Optionis in the prelude; thisuseadds noise.-use std::option::Option;
146-152: Scope the new unions conservativelyUnless you intend to stabilise these types, prefer
pub(crate)to avoid leaking API surface.-declare_node_union! { - pub MeaningfulReadNode = AnyJsUpdateExpression | AnyJsObjectBindingPatternMember | JsStaticMemberExpression -} +declare_node_union! { + pub(crate) MeaningfulReadNode = AnyJsUpdateExpression | AnyJsObjectBindingPatternMember | JsStaticMemberExpression +} -declare_node_union! { - pub AnyJsUpdateExpression = JsPreUpdateExpression | JsPostUpdateExpression -} +declare_node_union! { + pub(crate) AnyJsUpdateExpression = JsPreUpdateExpression | JsPostUpdateExpression +}
361-363: Alias capture doesn’t need a meaningful-read flagThese entries model
thisaliases, not class‑member reads; settingSome(true)is confusing and unused downstream.- // right hand side of a js variable statement is meaningful read - is_meaningful_read: Some(true), + // alias capture; not a class-member read + is_meaningful_read: None,
635-640: Use the property token’s range for object bindingsRecording the whole binding member span can make identities unstable (defaults, renames). Use the identifier/private-name token range instead.
- reads.insert(ClassMemberReference { - name: declarator.clone().to_trimmed_text(), - range: declarator.clone().syntax().text_trimmed_range(), - is_meaningful_read: is_meaningful_read(&declarator.clone().into()), - }); + if let Some(prop) = declarator.clone().as_js_object_binding_pattern_property() { + if let Ok(name) = prop.name() { + reads.insert(ClassMemberReference { + name: name.to_trimmed_text(), + range: name.syntax().text_trimmed_range(), + is_meaningful_read: is_meaningful_read(&declarator.clone().into()), + }); + } + }
887-907: Unbounded, context walk; drop the arbitrary depth capEight levels is brittle. Walk ancestors until a statement boundary or root; keep your current context checks.
- // Limit the number of parent traversals to avoid deep recursion - for _ in 0..8 { - if let Some(parent) = current.parent() { - let parent = skip_parentheses(parent); // strip parentheses + // Walk until a statement boundary or root + while let Some(parent0) = current.parent() { + let parent = skip_parentheses(parent0); // strip enclosing (...) match parent.kind() { JsSyntaxKind::JS_RETURN_STATEMENT | JsSyntaxKind::JS_CALL_ARGUMENTS | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION | JsSyntaxKind::JS_LOGICAL_EXPRESSION | JsSyntaxKind::JS_BINARY_EXPRESSION => return Some(true), _ => current = parent, } - } else { - break; - } - } + } Some(false)Consider also treating contexts like
await,yield, unary ops, and template expressions as meaningful. Happy to sketch tests.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(30 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧬 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). (24)
- GitHub Check: Documentation
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_package)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Test Node.js API
- GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Check Dependencies
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: autofix
🔇 Additional comments (5)
crates/biome_js_analyze/src/services/semantic_class.rs (5)
129-134: Confirm Hash/Eq semantics with the new fieldIncluding
is_meaningful_readinEq/Hashmeans the same name/range may exist twice (true vs false). If that’s intentional (per‑site classification), all good; if not, exclude it from equality or normalise before set insertion.
771-801: Update-expression handling looks goodRead/write split and meaningfulness via context are handled neatly.
671-675: Normalise to the member’s token range (notthis.foospan)This keeps read identities stable across contexts.
reads.insert(ClassMemberReference { name: member.to_trimmed_text(), - range: static_member.syntax().text_trimmed_range(), + range: member.syntax().text_trimmed_range(), is_meaningful_read: is_meaningful_read(&static_member.into()), });
839-845: Same range normalisation for property initialisersUse the member token’s range.
if let Ok(member) = static_member.member() { let name = member.to_trimmed_text(); reads.insert(ClassMemberReference { name, - range: static_member.syntax().text_trimmed_range(), + range: member.syntax().text_trimmed_range(), is_meaningful_read: is_meaningful_read(&MeaningfulReadNode::from( static_member.clone(), )), }); }
875-885: Parenthesis skipping walks the wrong wayYou need to walk up through enclosing
(...), not descend.-/// If the node is a parenthesized expression, return the inner expression, -/// otherwise return the node itself. -fn skip_parentheses(node: JsSyntaxNode) -> JsSyntaxNode { - if node.kind() == JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION { - // Assume the first child is the expression inside parentheses - if let Some(inner) = node.first_child() { - return inner; - } - } - node -} +/// Walk up past chains of enclosing parenthesised expressions. +fn skip_parentheses(mut node: JsSyntaxNode) -> JsSyntaxNode { + while node.kind() == JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION { + if let Some(parent) = node.parent() { + node = parent; + } else { + break; + } + } + node +}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
887-907: Context detection looks reasonable but could be more robust.The
is_used_in_expression_contextfunction covers key expression contexts. However, the arbitrary limit of 8 parent traversals and the specific node kinds might miss some edge cases.Consider expanding the meaningful expression contexts to include:
- Array/object literal contexts
- Template literal expressions
- Spread syntax contexts
JsSyntaxKind::JS_RETURN_STATEMENT | JsSyntaxKind::JS_CALL_ARGUMENTS | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION | JsSyntaxKind::JS_LOGICAL_EXPRESSION - | JsSyntaxKind::JS_BINARY_EXPRESSION => return Some(true), + | JsSyntaxKind::JS_BINARY_EXPRESSION + | JsSyntaxKind::JS_ARRAY_EXPRESSION + | JsSyntaxKind::JS_OBJECT_EXPRESSION => return Some(true),
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(30 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧬 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). (24)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_package)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Documentation
- GitHub Check: Check Dependencies
- GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: autofix
- GitHub Check: Test Node.js API
🔇 Additional comments (9)
crates/biome_js_analyze/src/services/semantic_class.rs (9)
133-134: Feature looks solid, but note the field addition could affect serialisation.The additional
is_meaningful_readfield onClassMemberReferenceextends the public API. If this struct is used in serialised contexts (like language server protocol), ensure backwards compatibility is maintained.
146-153: Clean union type definitions for the meaningful read feature.Well-structured type unions that properly distinguish update expressions and member accesses. The type system extension looks appropriate for the semantic analysis needs.
591-598: Streamlined pattern matching for update expressions.Replacing the previous verbose pattern matching with the new
AnyJsUpdateExpressionunion simplifies the code whilst maintaining the same functionality.
635-640: Mind the range inconsistency here.Using the full declarator syntax range instead of just the member's range conflicts with the established pattern elsewhere in the codebase.
Apply this diff to align with the range normalisation pattern:
reads.insert(ClassMemberReference { - name: declarator.clone().to_trimmed_text(), - range: declarator.clone().syntax().text_trimmed_range(), + name: declarator.clone().to_trimmed_text(), + range: declarator.clone().to_trimmed_text().text_range(), is_meaningful_read: is_meaningful_read(&declarator.clone().into()), });
672-676: Range normalisation needed here as flagged before.Apply this diff to fix the range:
reads.insert(ClassMemberReference { name: member.to_trimmed_text(), - range: static_member.syntax().text_trimmed_range(), + range: member.syntax().text_trimmed_range(), is_meaningful_read: is_meaningful_read(&static_member.into()), });
789-801: Excellent refactor for update expressions.The restructuring properly creates distinct read and write references for increment/decrement operations, with the read getting meaningful-read analysis and the write being marked as
None(since writes aren't meaningful reads by definition).
841-844: Same range issue in property initialisers.Apply this diff:
reads.insert(ClassMemberReference { name, - range: static_member.syntax().text_trimmed_range(), + range: static_member.member()?.syntax().text_trimmed_range(), is_meaningful_read: is_meaningful_read(&MeaningfulReadNode::from( static_member.clone(), )), });
871-885: Parenthesis skipping goes the wrong way.The function should walk up the tree, not down:
-fn skip_parentheses(node: JsSyntaxNode) -> JsSyntaxNode { - if node.kind() == JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION { - // Assume the first child is the expression inside parentheses - if let Some(inner) = node.first_child() { - return inner; - } - } - node -} +fn skip_parentheses(mut node: JsSyntaxNode) -> JsSyntaxNode { + while let Some(parent) = node.parent() { + if parent.kind() == JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION { + node = parent; + } else { + break; + } + } + node +}
919-922: Test structure accommodates the new meaningful-read flag nicely.The expanded test structure with
Option<bool>tuples for expected reads and writes provides comprehensive coverage for the new functionality.
ematipico
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR fails to explain the business logic of the code, which means it makes it impossible to review it. I addressed some coding problems, however the whole "meaningful reads" must be explained in the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
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)
129-134: Document and double‑check hash/eq semantics foris_meaningful_readPublic field lacks docs and now participates in
Hash/Eq, so the same member at the same range can appear twice inreadswith different flags. If that’s intentional, document it; if not, exclude the flag from equality or normalise before insert.#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct ClassMemberReference { pub name: Text, pub range: TextRange, - pub is_meaningful_read: Option<bool>, + /// Whether this reference is a meaningful read (Some(true)), + /// a non‑meaningful read (Some(false)), or N/A for writes (None). + /// Note: This field participates in Hash/Eq; identical name/range with different + /// values will be treated as distinct set entries. + pub is_meaningful_read: Option<bool>, }
🧹 Nitpick comments (5)
crates/biome_js_analyze/src/services/semantic_class.rs (5)
18-18: Drop redundantOptionimport
Optionis in the prelude; importingstd::option::Optionis noise.-use std::option::Option;
361-363: Don’t tagthisaliases as “meaningful reads”These entries model aliasing, not class member reads. Set to
Noneto avoid leaking semantics into alias tracking.- // right hand side of a js variable statement is meaningful read - is_meaningful_read: Some(true), + // N/A for aliasing; this is not a class member read + is_meaningful_read: None,
419-420: Explain “Only applicable to writes.”Add a short rationale (reads come from RHS/context; assignment patterns represent LHS writes).
- /// Only applicable to writes. + /// Only applicable to writes: array/object assignment patterns contribute LHS targets, + /// which are write sites for class members.Also applies to: 468-470
772-801: Update expressions: mark read/write cleanly, but avoid cloning the whole nodeLooks correct semantically. Minor nit: you can pass
&js_update_expressionwithout.clone()when building theMeaningfulReadNode.- is_meaningful_read: is_meaningful_read(&MeaningfulReadNode::from( - js_update_expression.clone(), - )), + is_meaningful_read: is_meaningful_read(&MeaningfulReadNode::from(js_update_expression)),
1016-1017: Tests: great coverage; please add callee/new/await/yield/throw casesGiven the expanded semantics, add cases for meaningful reads as call callees,
new,await,yield,throw, and template expressions.Also applies to: 1067-1067, 1114-1116, 1131-1132, 1184-1196, 1211-1221
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(30 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧬 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). (23)
- GitHub Check: Test Node.js API
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
- GitHub Check: Documentation
- GitHub Check: Check Dependencies
- GitHub Check: autofix
🔇 Additional comments (6)
crates/biome_js_analyze/src/services/semantic_class.rs (6)
591-598: Nice: update‑expression coverage wired into the walkHook looks correct and low‑cost. 👍
672-675: Range normalisation: good fixUsing the member token’s range keeps identities stable across contexts. Thanks for adjusting.
839-844: Use member token range, not the wholethis.foospan (again)This still uses the full static member span. Use the member token’s trimmed range for consistency with other sites.
- reads.insert(ClassMemberReference { - name, - range: static_member.syntax().text_trimmed_range(), - is_meaningful_read: is_meaningful_read(&MeaningfulReadNode::from( - static_member.clone(), - )), - }); + if let Ok(member) = static_member.member() { + reads.insert(ClassMemberReference { + name, + range: member.syntax().text_trimmed_range(), + is_meaningful_read: is_meaningful_read(&MeaningfulReadNode::from(static_member.clone())), + }); + }
636-639: Use property key (not binding name) for object-pattern readsFile: crates/biome_js_analyze/src/services/semantic_class.rs (lines 636–639)
to_trimmed_text() returns the binding (alias) in patterns like { foo: alias }; extract the property key (handle computed keys) so recorded reads reflect the source property. Suggested diff:
- reads.insert(ClassMemberReference { - name: declarator.clone().to_trimmed_text(), - range: declarator.clone().syntax().text_trimmed_range(), - is_meaningful_read: is_meaningful_read(&declarator.clone().into()), - }); + if let Some(prop) = declarator.clone().as_js_object_binding_pattern_property() { + let key = prop.key().ok()?; + let name = key.to_trimmed_text(); + reads.insert(ClassMemberReference { + name, + range: key.syntax().text_trimmed_range(), + is_meaningful_read: is_meaningful_read(&MeaningfulReadNode::from(declarator.clone())), + }); + }Add unit tests for
{ foo: alias } = thisand computed-key cases like{ ["f"+"oo"]: bar } = this.
146-152: Rename union to AnyMeaningfulReadNode — verify no name collisionsFile: crates/biome_js_analyze/src/services/semantic_class.rs (lines 146–152). The ripgrep you ran returned no matches; absence of matches isn't proof — confirm there are no existing AnyJsUpdateExpression or MeaningfulReadNode declarations in the repo before renaming.
-declare_node_union! { - pub MeaningfulReadNode = AnyJsUpdateExpression | AnyJsObjectBindingPatternMember | JsStaticMemberExpression -} +declare_node_union! { + pub AnyMeaningfulReadNode = AnyJsUpdateExpression | AnyJsObjectBindingPatternMember | JsStaticMemberExpression +}
705-722: Complete compound assignment operator list
The currentmatches!only covers||=,&&=,/=,*=,%=,+=,?=but omits-=,|=,^=,<<=,>>=,>>>=. Expand to includeJsSyntaxKind::MINUSEQ,PIPEEQ,CARETEQ,LTLTEQ,GTGTEQ,GTGTGTEQ, or switch to matching the semantic binary‐operator enum.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
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)
838-846: Use the member’s range here as well (normalisation bug)This still uses the whole
this.foospan. For consistency with other paths, use the member token’s range.Apply:
- if let Ok(member) = static_member.member() { - let name = member.to_trimmed_text(); - reads.insert(ClassMemberReference { - name, - range: static_member.syntax().text_trimmed_range(), - is_meaningful_read: is_meaningful_read(&AnyMeaningfulReadNode::from( - static_member.clone(), - )), - }); - } + if let Ok(member) = static_member.member() { + let name = member.to_trimmed_text(); + reads.insert(ClassMemberReference { + name, + range: member.syntax().text_trimmed_range(), + is_meaningful_read: is_meaningful_read(&AnyMeaningfulReadNode::from( + static_member.clone(), + )), + }); + }
🧹 Nitpick comments (4)
crates/biome_js_analyze/src/services/semantic_class.rs (4)
18-18: Drop the explicitOptionimport
Optionis in the prelude; the explicituse std::option::Option;is redundant.Apply:
- use std::option::Option;
133-135: Document the tri‑state clearly (reads vs writes)Good addition. Please make the contract explicit: “Some(true/false) only for reads; None for writes.” Also add a brief note on “meaningful” with 2–3 examples to avoid ambiguity.
638-641: Avoid repeated clones; compute onceMinor nit: avoid multiple
.clone()calls ondeclarator.Apply:
- reads.insert(ClassMemberReference { - name: declarator.clone().to_trimmed_text(), - range: declarator.clone().syntax().text_trimmed_range(), - is_meaningful_read: is_meaningful_read(&declarator.clone().into()), - }); + let decl = declarator.clone(); + reads.insert(ClassMemberReference { + name: decl.to_trimmed_text(), + range: decl.syntax().text_trimmed_range(), + is_meaningful_read: is_meaningful_read(&decl.into()), + });
1309-1364: Add missing meaningful‑context testsPlease extend with cases for callee/new/await/throw/unary/template/switch/for and parenthesised forms. This will guard the widened matcher.
Happy to draft the extra cases if you prefer me to push a patch.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(31 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧠 Learnings (1)
📚 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/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)
🪛 GitHub Actions: Benchmarks
crates/biome_js_analyze/src/services/semantic_class.rs
[error] 878-878: Rust compiler error: the trait bound "&SyntaxNode: Into<SyntaxNode>" is not satisfied in biome_js_analyze::semantic_class at line 878. Code uses 'AnyJsExpression::cast(node.syntax().into())', which requires an Into<SyntaxNode> implementation for '&SyntaxNode'.
🪛 GitHub Actions: autofix.ci
crates/biome_js_analyze/src/services/semantic_class.rs
[error] 878-878: the trait bound &SyntaxNode<JsLanguage>: Into<SyntaxNode<JsLanguage>> is not satisfied. Could not compile biome_js_analyze. The compiler error indicates a type mismatch in semantic_class.rs at the specified line.
🪛 GitHub Actions: Pull request Node.js
crates/biome_js_analyze/src/services/semantic_class.rs
[error] 878-878: the trait bound &SyntaxNode<JsLanguage>: Into<SyntaxNode<JsLanguage>> is not satisfied
🔇 Additional comments (6)
crates/biome_js_analyze/src/services/semantic_class.rs (6)
148-154: Type naming: align with internal conventionEarlier feedback suggested
AnyMeaningfulNode. Consider renamingAnyMeaningfulReadNodeaccordingly before release to avoid public API churn.
673-677: Nice: range now uses the member tokenSwitching to
member.syntax().text_trimmed_range()fixes identity stability for reads. Thanks for addressing this.
774-803: Update/read split for ++/-- looks correctReads get meaningful flag via the surrounding context, writes are
None. LGTM.
1165-1188: Tests:++/--context expectations look rightGood coverage across plain, conditional and return contexts.
884-900: Remove arbitrary parent limit; widen contexts and skip parentheses upwardsThe 8‑level cap can cause false negatives; also contexts miss
throw/await/yield/unary/template/callee/newand control‑flow conditions. Walk until the root and check more kinds.Apply:
- // Limit the number of parent traversals to avoid deep recursion - for _ in 0..8 { - if let Some(parent) = current.parent() { - match parent.kind() { - JsSyntaxKind::JS_RETURN_STATEMENT - | JsSyntaxKind::JS_CALL_ARGUMENTS - | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION - | JsSyntaxKind::JS_LOGICAL_EXPRESSION - | JsSyntaxKind::JS_BINARY_EXPRESSION => return Some(true), - _ => current = parent, - } - } else { - break; - } - } - Some(false) + while let Some(parent) = current.parent() { + // Walk past chains of parenthesised expressions + let mut p = parent; + while p.kind() == JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION { + if let Some(pp) = p.parent() { p = pp; } else { break; } + } + match p.kind() { + // Value is consumed in these positions + JsSyntaxKind::JS_RETURN_STATEMENT + | JsSyntaxKind::JS_THROW_STATEMENT + | JsSyntaxKind::JS_AWAIT_EXPRESSION + | JsSyntaxKind::JS_YIELD_EXPRESSION + | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION + | JsSyntaxKind::JS_LOGICAL_EXPRESSION + | JsSyntaxKind::JS_BINARY_EXPRESSION + | JsSyntaxKind::JS_UNARY_EXPRESSION + | JsSyntaxKind::JS_TEMPLATE_EXPRESSION + | JsSyntaxKind::JS_CALL_ARGUMENTS + | JsSyntaxKind::JS_CALL_EXPRESSION // callee: this.fn() + | JsSyntaxKind::JS_NEW_EXPRESSION + | JsSyntaxKind::JS_IF_STATEMENT + | JsSyntaxKind::JS_SWITCH_STATEMENT + | JsSyntaxKind::JS_FOR_STATEMENT + | JsSyntaxKind::JS_FOR_IN_STATEMENT + | JsSyntaxKind::JS_FOR_OF_STATEMENT => return Some(true), + _ => current = p, + } + } + Some(false)Please add tests for:
this.fn()(callee),new (this.Ctor)()await this.x,throw this.x,-(this.x),`${this.x}`switch (this.x) {},for (; this.x;) {}, and parenthesised forms likereturn (this.x).
704-725: Compound-assignment detection incomplete — widen the operator checkCurrent match only covers: PIPE2EQ, AMP2EQ, SLASHEQ, STAREQ, PERCENTEQ, PLUSEQ, QUESTION2EQ. Add the missing '-=' and shift-assignment kinds from the JsSyntaxKind enum (confirm exact names in the JS syntax crate) or replace the match with a generic check (e.g. operator.kind() != JsSyntaxKind::EQ or an is_compound_assignment() helper).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
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)
707-724: Compound assignments: cover all operators (or detect “not =”).The current match omits several operators (e.g.,
-=,|=,^=,<<=,>>=,>>>=,**=). This will miss legitimate reads (false negatives).- if let Ok(operator) = assignment.operator_token() - && let Some(operand) = left.as_any_js_assignment() - && matches!( - operator.kind(), - JsSyntaxKind::PIPE2EQ - | JsSyntaxKind::AMP2EQ - | JsSyntaxKind::SLASHEQ - | JsSyntaxKind::STAREQ - | JsSyntaxKind::PERCENTEQ - | JsSyntaxKind::PLUSEQ - | JsSyntaxKind::QUESTION2EQ - ) + if let Ok(operator) = assignment.operator_token() + && operator.kind() != JsSyntaxKind::EQ // treat any non-`=` assignment as a read + && let Some(operand) = left.as_any_js_assignment()
🧹 Nitpick comments (3)
crates/biome_js_analyze/src/services/semantic_class.rs (3)
5-13: Drop explicitOptionimport (in prelude).
use std::option::Option;is redundant. Keeps imports lean.- use std::option::Option;Also applies to: 18-18
877-912: Meaningful-context detector: remove traversal cap, peel parentheses, add while/do‑while.The 8‑level cap can miss deep nests; prefer an unbounded parent walk. Also include
JS_WHILE_STATEMENTandJS_DO_WHILE_STATEMENT. Peeling parentheses viaomit_parentheses()avoids extra hops.-fn is_used_in_expression_context(node: &AnyMeaningfulReadNode) -> Option<bool> { - let mut current: JsSyntaxNode = if let Some(expression) = AnyJsExpression::cast(node.syntax().clone()) { - expression.syntax().clone() // get JsSyntaxNode - } else { - node.syntax().clone() // fallback to the node itself - }; - - // Limit the number of parent traversals to avoid deep recursion - for _ in 0..8 { - if let Some(parent) = current.parent() { - match parent.kind() { +fn is_used_in_expression_context(node: &AnyMeaningfulReadNode) -> Option<bool> { + let mut current = if let Some(expr) = AnyJsExpression::cast_ref(node.syntax()) { + expr.omit_parentheses().syntax().clone() + } else { + node.syntax().clone() + }; + + while let Some(parent) = current.parent() { + match parent.kind() { JsSyntaxKind::JS_RETURN_STATEMENT | JsSyntaxKind::JS_CALL_ARGUMENTS | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION | JsSyntaxKind::JS_LOGICAL_EXPRESSION | JsSyntaxKind::JS_THROW_STATEMENT | JsSyntaxKind::JS_AWAIT_EXPRESSION | JsSyntaxKind::JS_YIELD_EXPRESSION | JsSyntaxKind::JS_UNARY_EXPRESSION | JsSyntaxKind::JS_TEMPLATE_EXPRESSION | JsSyntaxKind::JS_CALL_EXPRESSION // (callee) | JsSyntaxKind::JS_NEW_EXPRESSION | JsSyntaxKind::JS_IF_STATEMENT | JsSyntaxKind::JS_SWITCH_STATEMENT | JsSyntaxKind::JS_FOR_STATEMENT | JsSyntaxKind::JS_FOR_IN_STATEMENT | JsSyntaxKind::JS_FOR_OF_STATEMENT + | JsSyntaxKind::JS_WHILE_STATEMENT + | JsSyntaxKind::JS_DO_WHILE_STATEMENT | JsSyntaxKind::JS_BINARY_EXPRESSION => return Some(true), - _ => current = parent, - } - } else { - break; - } - } - Some(false) + _ => current = parent, + } + } + Some(false) }Question: should initialisers be considered meaningful? e.g.,
const t = this.x;If yes, includeJS_VARIABLE_DECLARATOR/JS_INITIALIZER_CLAUSEtoo.
235-235: Nit: “Cannot”, not “Can not”.Micro copy tweak.
-// Can not implement `Visitor` directly because it requires a new ctx that can not be created here +// Cannot implement `Visitor` directly because it requires a new ctx that cannot be created here
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(31 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧠 Learnings (1)
📚 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/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). (8)
- GitHub Check: Documentation
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Check Dependencies
- GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
- GitHub Check: Test Node.js API
- GitHub Check: autofix
🔇 Additional comments (4)
crates/biome_js_analyze/src/services/semantic_class.rs (4)
148-154: Type name: confirm convention (“AnyMeaningfulNode” vs “AnyMeaningfulReadNode”).Previous feedback suggested “AnyMeaningfulNode” as the internal convention. If we’re intentionally diverging, fine—just confirm.
774-803: Pre/post updates: good split into read + write.Capturing both edges and deferring meaningfulness to the update node context is spot on.
841-846: Use the member token’s range (not the wholethis.foo) for reads.Keeps identity stable and consistent with other paths.
- reads.insert(ClassMemberReference { - name, - range: static_member.syntax().text_trimmed_range(), - is_meaningful_read: is_meaningful_read(&AnyMeaningfulReadNode::from( - static_member.clone(), - )), - }); + if let Ok(member) = static_member.member() { + reads.insert(ClassMemberReference { + name, + range: member.syntax().text_trimmed_range(), + is_meaningful_read: is_meaningful_read(&AnyMeaningfulReadNode::from( + static_member.clone(), + )), + }); + }
1340-1417: Tests: add coverage for missed contexts and operators.
- While/do‑while:
while (this.c) {}anddo { } while (this.d).- Compound ops:
-=,|=,^=,<<=,>>=,>>>=,**=.- Object binding aliasing:
{a: b} = thisshould record read ofa.
1f81ab0 to
cb241c8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
crates/biome_js_analyze/src/services/semantic_class.rs (2)
235-235: Remove the misleading commentThis visitor doesn't implement the
Visitortrait directly because it creates contexts manually, not due to recursion concerns.-// Can not implement `Visitor` directly because it requires a new ctx that can not be created here +// Manually implements visitor pattern for direct control over traversal context
885-887: Clarify traversal limit commentThis is iterative parent traversal, not recursion. The limit prevents potential infinite loops in malformed syntax trees.
- // Limit the number of parent traversals to avoid deep recursion + // Limit parent traversals to prevent potential infinite loops
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(31 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧠 Learnings (1)
📚 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/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). (5)
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Test Node.js API
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Bench (biome_js_parser)
🔇 Additional comments (13)
crates/biome_js_analyze/src/services/semantic_class.rs (13)
5-13: Imports and namespace clean-upNice addition of the meaningful-read types, all looks correct.
133-136: Good documentation clarificationThe documentation now clearly explains what meaningful reads are and when
Noneapplies.
148-155: Consistent naming with BiomJS conventionsPerfect, the
AnyMeaningfulReadNodefollows the established naming pattern and theAnyJsUpdateExpressionunion is clean.
363-365: Consistent meaningful-read applicationGood application of meaningful reads to variable initialisers.
421-461: Clear documentation for write-only operationsThe comments correctly explain that array assignment patterns only apply to writes.
638-641: Object binding normalisation issue persistsUsing
declarator.to_trimmed_text()for object bindings like{a: b}yields"a: b"instead of just"a". Need to extract the property key.
675-677: Range normalisation looks goodUsing
member.syntax().text_trimmed_range()correctly captures just the property identifier.
722-724: Meaningful read logic for compound assignmentsCorrect implementation—compound assignments like
+=do indeed involve meaningful reads.
773-803: Update expression handling improvementsThe refactored approach using the union type is much cleaner. The meaningful-read logic correctly distinguishes between the read and write portions.
873-914: Expression context detection expandedGood expansion of expression contexts, covering more meaningful-read scenarios. The limit of 8 parent traversals is reasonable for preventing infinite loops.
878-883: Fix the cast issueThe current attempt to cast via
Intowill fail. Usecast_refor clone the syntax node directly.- let mut current: JsSyntaxNode = - if let Some(expression) = AnyJsExpression::cast(node.syntax().clone()) { - expression.syntax().clone() // get JsSyntaxNode - } else { - node.syntax().clone() // fallback to the node itself - }; + let mut current = if let Some(expression) = AnyJsExpression::cast(node.syntax().clone()) { + expression.omit_parentheses().syntax().clone() + } else { + node.syntax().clone() + };
925-975: Test infrastructure improvementsThe new helper functions with meaningful-read expectations are well-structured and comprehensive.
1260-1423: Comprehensive meaningful-read test coverageExcellent test coverage across all expression contexts. The test structure is clear and validates both node names and meaningful-read flags correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
crates/biome_js_analyze/src/services/semantic_class.rs (2)
148-155: Union types follow naming conventions correctly.The nodes follow Biome's naming conventions and serve the intended purpose. However, the
AnyCandidateForUsedInExpressionNodename feels unwieldy—could consider shortening toAnyMeaningfulReadCandidatein future.
876-877: Minor documentation polish needed.The comment has a typo: "NOt limited" should be "Not limited".
-/// NOt limited to `this` references. Can be used for any node. +/// Not limited to `this` references. Can be used for any node.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(31 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧠 Learnings (1)
📚 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/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 (2)
cast_ref(142-151)to_trimmed_text(223-225)
⏰ 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). (22)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Test Node.js API
- GitHub Check: autofix
- 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: Documentation
- GitHub Check: Check Dependencies
🔇 Additional comments (12)
crates/biome_js_analyze/src/services/semantic_class.rs (12)
133-136: Clear, well-documented addition to the API.The field and its documentation are clear and correctly indicate the different values:
Some(true)for meaningful reads,Some(false)for non-meaningful reads, andNonefor writes.
421-443: Consistent meaningful read annotation for writes.All write references correctly set
is_meaningful_readtoNone. The consistency is appreciated.Also applies to: 471-490, 505-507, 526-526, 538-538, 546-546, 725-725, 752-753, 790-790
593-600: Correct handling of update expressions.The refactor to use
AnyJsUpdateExpressionis correct and the cast_ref approach is proper. Good job addressing the past review feedback.
637-643: Object binding pattern still uses wrong range source.The range is still taken from the entire declarator rather than extracting the property key. This creates inconsistency in ClassMemberReference ranges.
Apply the fix suggested in the past review to extract the property key's range instead of the full declarator range.
677-679: Range now correctly uses member, not entire static expression.Good fix addressing the past review feedback. The range now correctly points to just the member identifier.
775-805: Update expression handling is thorough and correct.The logic correctly creates both read and write references for update expressions, with appropriate
is_meaningful_readvalues. The use ofAnyCandidateForUsedInExpressionNodeis consistent with other parts of the code.
842-848: Property initialiser range should use member token.This still uses the entire static member expression range instead of just the member token range, creating inconsistency.
Apply the suggested fix from past reviews to use the member's range instead of the full static member expression range.
875-915: Expression context detection is comprehensive.The function now covers all the meaningful expression contexts mentioned in the PR objectives. The traversal limit of 8 levels seems reasonable to prevent performance issues. Good work expanding the syntax kinds to include all meaningful contexts.
880-885: AnyJsExpression cast approach is correct.The fix for the compile error using
clone()instead of.into()is appropriate and follows Rust idioms correctly.
926-1260: Comprehensive test coverage for meaningful reads.The tests thoroughly cover all scenarios with clear expectations for both reads and writes. The helper functions
assert_readsandassert_writesare well-structured. The test cases validate the core functionality of meaningful read detection.
1262-1442: Thorough testing of expression contexts.The
is_used_in_expression_context_testsmodule provides excellent coverage of all meaningful expression contexts. The test structure withextract_all_nodesandrun_test_casesis clean and maintainable.
362-365: Resolved — callers handle is_meaningful_read correctlyAll usages are local to crates/biome_js_analyze/src/services/semantic_class.rs and treat is_meaningful_read as Option (Some(...) or None); unit tests assert the expected Option values, so no change required.
5760170 to
fde4f87
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
362-365: Simplify the meaningful read calculation.The cast to
AnyCandidateForUsedInExpressionNodeand then callingis_used_in_expression_contextseems overly complex for a simple boolean return.- is_meaningful_read: Some(is_used_in_expression_context( - &AnyCandidateForUsedInExpressionNode::from(id), - )), + is_meaningful_read: Some(is_used_in_expression_context( + &id.into(), + )),
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(31 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧠 Learnings (1)
📚 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/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). (22)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Documentation
- GitHub Check: Check Dependencies
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Test Node.js API
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: autofix
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_js_analyze)
🔇 Additional comments (13)
crates/biome_js_analyze/src/services/semantic_class.rs (13)
5-13: LGTM! Comprehensive imports added for expanded functionality.The new imports logically support the meaningful read detection and expanded node type handling introduced in this PR.
133-136: Clear documentation of is_meaningful_read field.The field documentation effectively explains the purpose and applicability. The
Option<bool>type makes sense:Some(true/false)for reads indicating meaningful usage,Nonefor writes where the concept doesn't apply.
148-155: Node union declarations follow Biome conventions well.These public unions properly implement the established naming pattern (
Any*) and expose the necessary AST node types for the meaningful read detection system.
421-444: Good documentation for write-only contexts.The comments clearly indicate these functions handle write references where meaningful read doesn't apply. The consistent use of
Noneforis_meaningful_readis correct.Also applies to: 471-491, 506-508
593-600: Clean handling of update expressions with proper type safety.The unified handling of pre/post update expressions through the
AnyJsUpdateExpressionunion is well-structured and maintains type safety.
678-679: Verify the range consistency fix.The use of
member.syntax().text_trimmed_range()instead of the full static member expression looks correct based on past review comments, ensuring consistent property identifier ranges.
724-726: Correct meaningful read annotation for compound assignments.Compound assignment reads (like
this.x += 1) are correctly marked as meaningful since the property value is consumed by the operation.
775-805: Clean separation of read/write semantics in updates.The update expression handling properly creates separate read and write references with appropriate meaningful read annotations. The write gets
None, the read gets the context-based evaluation.
875-915: Well-documented context detection with appropriate syntax kinds.The function documentation clearly explains the purpose, and the expanded set of syntax kinds covers the meaningful contexts outlined in the PR description. The traversal depth limit prevents excessive recursion.
880-885: Fix the syntax casting issue.The
AnyJsExpression::cast(node.syntax().clone())approach correctly handles the type conversion after the previous compile error fix.
927-977: Comprehensive test helpers for meaningful read validation.The test helper functions properly validate both the property names and their meaningful read annotations, providing clear error messages for debugging test failures.
1000-1046: Thorough test coverage for all reference types.The test cases comprehensively cover object binding patterns, static member expressions, assignment expressions, and update expressions, with proper validation of meaningful read flags. The test cases include both direct
thisusage and alias scenarios.Also applies to: 1048-1106, 1108-1172, 1174-1261
1262-1452: Excellent comprehensive testing of expression contexts.The dedicated test module for
is_used_in_expression_contextcovers all the meaningful expression contexts mentioned in the PR description, including edge cases like nested parentheses and complex logical expressions.
| // Limit the number of parent traversals to avoid deep recursion | ||
| for _ in 0..8 { | ||
| if let Some(parent) = current.parent() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it's just a loop. That's what you usually do to avoid a recursion. I still don't understand the limit. Ascending a tree is way cheaper than descending it, and we don't put limits when we descend a tree e.g. when we call preorder(). So it's fine removing it.
Co-authored-by: Emanuele Stoppa <[email protected]>
Co-authored-by: Emanuele Stoppa <[email protected]>
8ddcb1d to
c7d4cec
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
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)
1203-1239: Update remaining test expectations.The pre/post update test cases also need AccessKind conversion.
Apply this diff:
expected_reads: vec![ - ("count", Some(false)), - ("total", Some(false)), - ("inIfCondition", Some(true)), - ("inReturn", Some(true)), + ("count", AccessKind::TrivialRead), + ("total", AccessKind::TrivialRead), + ("inIfCondition", AccessKind::MeaningfulRead), + ("inReturn", AccessKind::MeaningfulRead), ], expected_writes: vec![ - ("count", None), - ("total", None), - ("inIfCondition", None), - ("inReturn", None), + ("count", AccessKind::Write), + ("total", AccessKind::Write), + ("inIfCondition", AccessKind::Write), + ("inReturn", AccessKind::Write), ], expected_reads: vec![ - ("count", Some(false)), - ("total", Some(false)), - ("inReturnIncrement", Some(true)), + ("count", AccessKind::TrivialRead), + ("total", AccessKind::TrivialRead), + ("inReturnIncrement", AccessKind::MeaningfulRead), ], expected_writes: vec![ - ("count", None), - ("total", None), - ("inReturnIncrement", None), + ("count", AccessKind::Write), + ("total", AccessKind::Write), + ("inReturnIncrement", AccessKind::Write), ],
🧹 Nitpick comments (3)
crates/biome_js_analyze/src/services/semantic_class.rs (3)
129-152: Add docs explaining when each variant applies.The enum definition is comprehensive, but missing essential context about when each variant is used. From past review feedback, this needs clarification.
Apply this diff to improve the documentation:
/// Represents how a class member is accessed within the code. -/// Variants: -/// -/// - `Write`: -/// The member is being assigned to or mutated. -/// Example: `this.count = 10;` -/// This indicates the member's value/state changes at this point. -/// -/// - `MeaningfulRead`: -/// The member's value is retrieved and used in a way that affects program logic. -/// Example: `if (this.enabled) { ... }` or `let x = this.value + 1;` -/// These reads influence control flow or computation. -/// -/// - `TrivialRead`: -/// The member is accessed, but its value is not used in a way that -/// meaningfully affects logic. -/// Example: `this.value;` as a standalone expression, or a read that is optimized away. -/// This is mostly for distinguishing "dead reads" from truly meaningful ones. +/// This classification helps lint rules like `no_unused_class_properties` identify +/// whether a property is genuinely unused or serves a meaningful purpose. +/// +/// ## When variants apply: +/// - **Write**: Any assignment, compound assignment, or mutation (e.g., `this.x = 1`, `this.y += 2`, `this.z++`) +/// - **MeaningfulRead**: Property access where the value affects control flow or computation +/// (e.g., `return this.x`, `if (this.flag)`, `console.log(this.name)`) +/// - **TrivialRead**: Property access that doesn't meaningfully affect program logic +/// (e.g., standalone `this.value;` expressions without side effects) #[derive(Debug, Clone, Hash, Eq, PartialEq)] enum AccessKind {
171-177: Union seems over-engineered for its simple usage.Looking at the usage patterns, this union serves mainly as a parameter type for two functions. The type name is also quite verbose.
Consider simplifying the approach:
-declare_node_union! { - pub AnyCandidateForUsedInExpressionNode = AnyJsUpdateExpression | AnyJsObjectBindingPatternMember | JsStaticMemberExpression | AnyJsBindingPattern -} +declare_node_union! { + pub AnyExpressionCandidate = AnyJsUpdateExpression | AnyJsObjectBindingPatternMember | JsStaticMemberExpression | AnyJsBindingPattern +}Or potentially inline the logic if the union is only used in a couple of places.
886-893: Function could be more concise.The wrapper function adds little value over direct inlining.
Consider removing the wrapper:
-/// Determines the kind of read access for a given node. -fn get_read_access_kind(node: &AnyCandidateForUsedInExpressionNode) -> AccessKind { - if is_used_in_expression_context(node) { - AccessKind::MeaningfulRead - } else { - AccessKind::TrivialRead - } -}And inline the logic at call sites:
-access_kind: get_read_access_kind(&declarator.clone().into()), +access_kind: if is_used_in_expression_context(&declarator.clone().into()) { + AccessKind::MeaningfulRead +} else { + AccessKind::TrivialRead +},
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(31 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧠 Learnings (1)
📚 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/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). (23)
- GitHub Check: End-to-end tests
- GitHub Check: Check Dependencies
- GitHub Check: Documentation
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_package)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Test Node.js API
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: autofix
🔇 Additional comments (2)
crates/biome_js_analyze/src/services/semantic_class.rs (2)
899-926: Excellent contextual analysis improvement!The function now properly handles parent traversal and covers all the meaningful expression contexts from the PR objectives. The logic is solid and comprehensive.
655-660: Object binding name extraction needs refinement.The current approach uses
to_trimmed_text()on the entire declarator, which can yield malformed names like "a: b" for object property destructuring.Apply this diff to extract the property key properly:
if let Some(declarator) = declarator.ok() && is_this_reference(&expression, scoped_this_references) { + let (name_text, range) = match &declarator { + AnyJsObjectBindingPatternMember::JsObjectBindingPatternProperty(prop) => { + if let Ok(key) = prop.key() { + (key.to_trimmed_text(), key.syntax().text_trimmed_range()) + } else { + (declarator.to_trimmed_text(), declarator.syntax().text_trimmed_range()) + } + }, + AnyJsObjectBindingPatternMember::JsObjectBindingPatternShorthandProperty(shorthand) => { + if let Ok(name) = shorthand.name() { + (name.to_trimmed_text(), name.syntax().text_trimmed_range()) + } else { + (declarator.to_trimmed_text(), declarator.syntax().text_trimmed_range()) + } + }, + _ => (declarator.to_trimmed_text(), declarator.syntax().text_trimmed_range()) + }; reads.insert(ClassMemberReference { - name: declarator.clone().to_trimmed_text(), - range: declarator.clone().syntax().text_trimmed_range(), + name: name_text, + range, access_kind: get_read_access_kind(&declarator.clone().into()), });
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
646-664: Object binding: use the property key, not the whole declaratorUsing to_trimmed_text() on the declarator yields strings like "a: b". Extract the key/shorthand identifier and use its range for stable identity.
- reads.insert(ClassMemberReference { - name: declarator.clone().to_trimmed_text(), - range: declarator.clone().syntax().text_trimmed_range(), - access_kind: get_read_access_kind(&declarator.clone().into()), - }); + // Normalise to the property key (handles `{a: b}` and shorthand) + let (name_text, range) = if let Some(prop) = + declarator.as_js_object_binding_pattern_property() + { + if let Ok(key) = prop.key() { + (key.to_trimmed_text(), key.syntax().text_trimmed_range()) + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + } + } else if let Some(shorthand) = + declarator.as_js_object_binding_pattern_shorthand_property() + { + if let Ok(ident) = shorthand.name() { + (ident.to_trimmed_text(), ident.syntax().text_trimmed_range()) + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + } + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + }; + reads.insert(ClassMemberReference { + name: name_text, + range, + access_kind: get_read_access_kind(&declarator.clone().into()), + });
🧹 Nitpick comments (6)
crates/biome_js_analyze/src/services/semantic_class.rs (6)
129-152: AccessKind enum: solid API choiceGood separation of concerns; this will age well. Consider marking it #[non_exhaustive] to allow future variants without breaking semver.
385-388: Alias collection: access kind is incidental hereWe don’t need meaningful/trivial classification for alias bindings; it’s metadata for alias tracking only. Harmless, but you could skip get_read_access_kind here.
- access_kind: get_read_access_kind( - &AnyCandidateForUsedInExpressionNode::from(id), - ), + access_kind: AccessKind::TrivialRead,
895-922: Doc nit + tighten descriptionThe sentence trails off (“requires more work e.g.”). Also, be explicit about “consumed by context”.
-/// Checks if the given node is used in an expression context -/// (e.g., return, call arguments, conditionals, binary expressions). -/// Not limited to `this` references. Can be used for any node, but requires more work e.g. -/// Returns `true` if the read is meaningful, `false` otherwise. +/// Returns true if the node’s value is consumed by its syntactic context +/// (e.g. return/throw, callee/arguments, await/yield, control‑flow tests, unary/binary/logical, +/// template, new); otherwise false. +/// Not limited to `this` references; the union here reflects current supported nodes.
1131-1147: Assignment tests: good start — please add more operatorsAdd cases for '-=', '|=', '^=', '&=', shifts, and '**=' to guard the extended operator set.
1290-1378: Expression-context tests: helpful coverageThese directly validate the context classifier. Consider adding:
- callee vs argument in the same snippet,
switch((this.x))with extra parentheses,new (this.Ctor)()with nesting.
18-18: Unnecessary import
use std::option::Option;is redundant; Option is in the prelude.-use std::option::Option;
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(31 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧠 Learnings (1)
📚 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/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). (24)
- GitHub Check: autofix
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_package)
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Documentation
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Check Dependencies
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Test Node.js API
🔇 Additional comments (9)
crates/biome_js_analyze/src/services/semantic_class.rs (9)
171-177: Node unions look rightThe unions cover update expressions, binding/object pattern members, and static member reads. Nicely scoped for the current use-cases.
612-619: Update expressions handling: nice and tightDetecting both pre and post updates and classifying the read via the whole update expression context is spot on.
790-816: Update expressions: reuse the extracted name; OKClones then reuses name/range for the paired read. Looks fine and avoids re-parsing. Minor naming shadowing, but clear enough.
845-859: Range should be the member token, not the wholethis.fooConsistency matters for de-duplication and identity tracking. Use the member’s trimmed range.
- let name = member.to_trimmed_text(); - reads.insert(ClassMemberReference { - name, - range: static_member.syntax().text_trimmed_range(), + let name = member.to_trimmed_text(); + reads.insert(ClassMemberReference { + name, + range: member.syntax().text_trimmed_range(), access_kind: get_read_access_kind(&AnyCandidateForUsedInExpressionNode::from( static_member.clone(), )), });
886-893: get_read_access_kind: clean abstractionSimple and readable; keeps call sites tidy.
1012-1021: Object binding tests: 👍Covers
thisand alias reads; expectations line up with the new AccessKind.
1067-1077: Static member tests: 👍Meaningful vs trivial contexts are exercised (call args vs bare expr). Nice.
1207-1230: Update expression tests: on pointCovers if/return vs bare updates; matches the intended semantics.
726-743: Cover all compound-assignment operators (verification needed)Search in the sandbox returned no JsSyntaxKind EQ variants; cannot confirm token names. Ensure the match in crates/biome_js_analyze/src/services/semantic_class.rs (≈ lines 726–743) includes these kinds:
- Arithmetic: PLUSEQ, MINUSEQ, STAREQ, STAR2EQ, SLASHEQ, PERCENTEQ
- Bitwise: AMPEQ, PIPEEQ, CARETEQ
- Shifts: LTLTEQ, GTGTEQ, GTGTGTEQ
- Logical/nullish: AMP2EQ, PIPE2EQ, QUESTION2EQ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
726-743: Treat any non-'=' assignment as compound — record a readCurrent match in crates/biome_js_analyze/src/services/semantic_class.rs (~726–743) only covers PIPE2EQ, AMP2EQ, SLASHEQ, STAREQ, PERCENTEQ, PLUSEQ and QUESTION2EQ — missing -=, **=, <<=/>>=, &=, |=, ^=, etc., so meaningful reads are under‑reported. Prefer a semantic check:
if let Ok(op) = assignment.operator_token() {
if op.kind() != JsSyntaxKind::EQ {
// treat as compound assignment: produce a read
}
}
🧹 Nitpick comments (5)
crates/biome_js_analyze/src/services/semantic_class.rs (5)
148-152: Make AccessKind Copy to avoid clones.Enum is small and used pervasively; deriving Copy removes
.clone()noise and micro-allocs.-#[derive(Debug, Clone, Hash, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] pub enum AccessKind {Follow-up: drop
.clone()where AccessKind is copied (see Lines 557 and 565).
545-566: Drop unnecessary clones now that AccessKind is Copy.- access_kind: access_kind.clone(), + access_kind,- access_kind, + access_kind,
18-18: Remove redundantuse std::option::Option;.
Optionis in the prelude. This import is unnecessary.-use std::option::Option;
142-147: Doc nit: fix wording for TrivialRead.Current text says “distinguishing ... access_kind ones.” Replace with clearer phrasing.
-/// This is mostly for distinguishing "dead reads" from truly access_kind ones. +/// Helps distinguish dead reads from genuinely consumed reads.
1292-1293: Remove stray comment.
// WE nlooks like a leftover.- // WE n
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(31 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧠 Learnings (1)
📚 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/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). (14)
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Test Node.js API
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: autofix
🔇 Additional comments (5)
crates/biome_js_analyze/src/services/semantic_class.rs (5)
692-696: LGTM on range and context.Using the member token’s trimmed range and delegating meaningfulness to
get_read_access_kindis consistent and correct.Please ensure callee reads like
this.fn()are covered by your tests (they are viaJS_CALL_EXPRESSION, which looks good).
790-816: Update-expression handling looks correct.
- Writes recorded via the assignment operand.
- Read meaningfulness derived from the update-expression context.
895-923: Expression-context set looks aligned with the PR intent.Covers return, call args/callee, conditionals, logical/binary, throw/await/yield, unary, template, new, and control-flow heads.
Consider adding tests for:
- Parenthesised forms (e.g.
return (this.x)).new (this.Ctor)().- Template tag callee if applicable.
853-859: Use the member’s range, not the entirethis.foospan, for property initialisers.Current code records the whole static member expression range. For consistency, use the member token’s trimmed range.
- reads.insert(ClassMemberReference { - name, - range: static_member.syntax().text_trimmed_range(), - access_kind: get_read_access_kind(&AnyCandidateForUsedInExpressionNode::from( - static_member.clone(), - )), - }); + reads.insert(ClassMemberReference { + name, + range: static_member.member().ok()?.syntax().text_trimmed_range(), + access_kind: get_read_access_kind(&AnyCandidateForUsedInExpressionNode::from( + static_member.clone(), + )), + });
656-660: Fix object binding name/range: extract the property key, not the whole declarator.Using
to_trimmed_text()on the member yields strings like"a: b"; range also points to the whole property node. Normalise to the property key for accurate identity and stable ranges.Apply:
- reads.insert(ClassMemberReference { - name: declarator.clone().to_trimmed_text(), - range: declarator.clone().syntax().text_trimmed_range(), - access_kind: get_read_access_kind(&declarator.clone().into()), - }); + let (name_text, range) = if let Some(prop) = + declarator.as_js_object_binding_pattern_property() + { + if let Ok(key) = prop.key() { + (key.to_trimmed_text(), key.syntax().text_trimmed_range()) + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + } + } else if let Some(shorthand) = + declarator.as_js_object_binding_pattern_shorthand_property() + { + if let Ok(ident) = shorthand.name() { + (ident.to_trimmed_text(), ident.syntax().text_trimmed_range()) + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + } + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + }; + reads.insert(ClassMemberReference { + name: name_text, + range, + access_kind: get_read_access_kind(&declarator.clone().into()), + });
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
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)
726-742: Compound assignments: missing operators → false negativesCovers only a subset of compound ops.
-=|=^=<<=>>=>>>=**=etc. won’t be classified as read+write.Use a generic check for “not simple
=” to future‑proof.Apply:
- if let Ok(operator) = assignment.operator_token() - && let Some(operand) = left.as_any_js_assignment() - && matches!( - operator.kind(), - JsSyntaxKind::PIPE2EQ - | JsSyntaxKind::AMP2EQ - | JsSyntaxKind::SLASHEQ - | JsSyntaxKind::STAREQ - | JsSyntaxKind::PERCENTEQ - | JsSyntaxKind::PLUSEQ - | JsSyntaxKind::QUESTION2EQ - ) + if let Ok(op) = assignment.operator_token() + && op.kind() != JsSyntaxKind::EQ + && let Some(operand) = left.as_any_js_assignment() && let Some(name) = ThisPatternResolver::extract_this_member_reference( operand.as_js_static_member_assignment(), scoped_this_references, AccessKind::MeaningfulRead, )
🧹 Nitpick comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
18-18: Redundant import
Optionis in prelude; thisuseis unnecessary.Apply:
-use std::option::Option;
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(31 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧠 Learnings (1)
📚 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/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). (5)
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Check Dependencies
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Test Node.js API
🔇 Additional comments (2)
crates/biome_js_analyze/src/services/semantic_class.rs (2)
855-859: Static member read range should be the member token, notthis.fooKeeps identity stable and consistent with other paths.
Apply:
- reads.insert(ClassMemberReference { - name, - range: static_member.syntax().text_trimmed_range(), - access_kind: get_read_access_kind(&AnyCandidateForUsedInExpressionNode::from( - static_member.clone(), - )), - }); + reads.insert(ClassMemberReference { + name, + range: member.syntax().text_trimmed_range(), + access_kind: get_read_access_kind(&AnyCandidateForUsedInExpressionNode::from( + static_member.clone(), + )), + });
657-660: Object binding: normalise to property key (avoid “a: b” as name)Using the whole declarator yields names/ranges like
"a: b". Extract the key for both property and shorthand forms.Apply:
- reads.insert(ClassMemberReference { - name: declarator.clone().to_trimmed_text(), - range: declarator.clone().syntax().text_trimmed_range(), - access_kind: get_read_access_kind(&declarator.clone().into()), - }); + let (name_text, range) = if let Some(prop) = + declarator.as_js_object_binding_pattern_property() + { + if let Ok(key) = prop.key() { + (key.to_trimmed_text(), key.syntax().text_trimmed_range()) + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + } + } else if let Some(shorthand) = + declarator.as_js_object_binding_pattern_shorthand_property() + { + if let Ok(ident) = shorthand.name() { + (ident.to_trimmed_text(), ident.syntax().text_trimmed_range()) + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + } + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + }; + reads.insert(ClassMemberReference { + name: name_text, + range, + access_kind: get_read_access_kind(&declarator.clone().into()), + });
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
726-743: Detect all compound assignments (don’t miss -=, <<=, |=, etc.).The match only covers a subset of compound operators. Safer: treat “not plain =” as compound.
Apply:
- if let Ok(operator) = assignment.operator_token() - && let Some(operand) = left.as_any_js_assignment() - && matches!( - operator.kind(), - JsSyntaxKind::PIPE2EQ - | JsSyntaxKind::AMP2EQ - | JsSyntaxKind::SLASHEQ - | JsSyntaxKind::STAREQ - | JsSyntaxKind::PERCENTEQ - | JsSyntaxKind::PLUSEQ - | JsSyntaxKind::QUESTION2EQ - ) + if let Ok(operator) = assignment.operator_token() + && let Some(operand) = left.as_any_js_assignment() + && operator.kind() != JsSyntaxKind::EQ && let Some(name) = ThisPatternResolver::extract_this_member_reference( operand.as_js_static_member_assignment(), scoped_this_references, - AccessKind::MeaningfulRead, + AccessKind::MeaningfulRead, )Optionally, add tests for
-=,**=,<<=,>>=,>>>=,^=,|=.
🧹 Nitpick comments (2)
crates/biome_js_analyze/src/services/semantic_class.rs (2)
18-18: Redundant import of Option.
Optionis in the prelude; thisuseis unnecessary.Apply:
-use std::option::Option;
244-256: Type reuse forthisaliases is confusing.
ClassMemberReferenceis used to store alias identifiers (not class members). Consider a dedicated alias type (name + range) to avoid conflating concepts.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(31 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧠 Learnings (1)
📚 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/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). (23)
- GitHub Check: Documentation
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- 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: Check Dependencies
- GitHub Check: Test Node.js API
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: autofix
🔇 Additional comments (3)
crates/biome_js_analyze/src/services/semantic_class.rs (3)
741-742: Consistency: avoid hardcoding MeaningfulRead (reuse the classifier).Prefer using the same classification helper used elsewhere for reads.
If you later extend AnyCandidateForUsedInExpressionNode to include assignment expressions, replace the hardcoded variant with get_read_access_kind for consistency.
853-859: Use the member token range, not the wholethis.foospan, for initialisers.Keep range identity consistent with other reads (only the property token).
Apply:
- reads.insert(ClassMemberReference { - name, - range: static_member.syntax().text_trimmed_range(), - access_kind: get_read_access_kind(&AnyCandidateForUsedInExpressionNode::from( - static_member.clone(), - )), - }); + reads.insert(ClassMemberReference { + name, + range: member.syntax().text_trimmed_range(), + access_kind: get_read_access_kind(&AnyCandidateForUsedInExpressionNode::from( + static_member.clone(), + )), + });
656-660: Normalise object binding names to the property key (avoid "a: b").Using
to_trimmed_text()on the whole member yields strings like"a: b". Extract the key/shorthand identifier instead so names and ranges refer to the property key only.Apply:
- reads.insert(ClassMemberReference { - name: declarator.clone().to_trimmed_text(), - range: declarator.clone().syntax().text_trimmed_range(), - access_kind: get_read_access_kind(&declarator.clone().into()), - }); + let (name_text, range) = if let Some(prop) = + declarator.as_js_object_binding_pattern_property() + { + if let Ok(key) = prop.key() { + (key.to_trimmed_text(), key.syntax().text_trimmed_range()) + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + } + } else if let Some(shorthand) = + declarator.as_js_object_binding_pattern_shorthand_property() + { + if let Ok(ident) = shorthand.name() { + (ident.to_trimmed_text(), ident.syntax().text_trimmed_range()) + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + } + } else { + (declarator.clone().to_trimmed_text(), declarator.clone().syntax().text_trimmed_range()) + }; + reads.insert(ClassMemberReference { + name: name_text, + range, + access_kind: get_read_access_kind(&declarator.clone().into()), + });
|
@ematipico good for review, thanks |
ematipico
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work!
| /// Not limited to `this` references. | ||
| /// It can be used for any node; additional cases may require extending the context checks. | ||
| fn is_used_in_expression_context(node: &AnyCandidateForUsedInExpressionNode) -> bool { | ||
| node.syntax().ancestors().any(|ancestor| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add skip(1), otherwise you risk considering the current node
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
crates/biome_js_analyze/src/services/semantic_class.rs(31 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_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_js_analyze/src/services/semantic_class.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
🧠 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/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/services/semantic_class.rs
📚 Learning: 2025-09-12T08:06:16.650Z
Learnt from: ematipico
PR: biomejs/biome#7410
File: crates/biome_js_analyze/src/lint/nursery/no_jsx_props_bind.rs:103-149
Timestamp: 2025-09-12T08:06:16.650Z
Learning: In Biome AST traversal, `ancestors().skip(1)` is a reasonable pattern to skip the current node and prevent false positives when searching for ancestor nodes of the same kind.
Applied to 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: Bench (biome_module_graph)
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_package)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Bench (biome_js_analyze)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: autofix
🔇 Additional comments (1)
crates/biome_js_analyze/src/services/semantic_class.rs (1)
855-858: Use the member token’s range for diagnosticsGrabbing
static_member.syntax().text_trimmed_range()stretches the span back to the wholethis.foo, undoing the earlier normalisation and making downstream diagnostics jittery. Please keep the reference anchored on the member token itself.- range: static_member.syntax().text_trimmed_range(), + range: member.syntax().text_trimmed_range(),
| fn is_used_in_expression_context(node: &AnyCandidateForUsedInExpressionNode) -> bool { | ||
| node.syntax().ancestors().skip(1).any(|ancestor| { | ||
| matches!( | ||
| ancestor.kind(), | ||
| JsSyntaxKind::JS_RETURN_STATEMENT | ||
| | JsSyntaxKind::JS_CALL_ARGUMENTS | ||
| | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION | ||
| | JsSyntaxKind::JS_LOGICAL_EXPRESSION | ||
| | JsSyntaxKind::JS_THROW_STATEMENT | ||
| | JsSyntaxKind::JS_AWAIT_EXPRESSION | ||
| | JsSyntaxKind::JS_YIELD_EXPRESSION | ||
| | JsSyntaxKind::JS_UNARY_EXPRESSION | ||
| | JsSyntaxKind::JS_TEMPLATE_EXPRESSION | ||
| | JsSyntaxKind::JS_CALL_EXPRESSION | ||
| | JsSyntaxKind::JS_NEW_EXPRESSION | ||
| | JsSyntaxKind::JS_IF_STATEMENT | ||
| | JsSyntaxKind::JS_SWITCH_STATEMENT | ||
| | JsSyntaxKind::JS_FOR_STATEMENT | ||
| | JsSyntaxKind::JS_FOR_IN_STATEMENT | ||
| | JsSyntaxKind::JS_FOR_OF_STATEMENT | ||
| | JsSyntaxKind::JS_BINARY_EXPRESSION | ||
| ) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Treat class field initialisers as meaningful reads
Right now class C { bar = this.foo; } classifies the this.foo read as trivial, because the walk never recognises the surrounding field initialiser. Once the linter filters on MeaningfulRead, foo will be reported as unused despite actively feeding bar. Let’s mark class field initialisers as meaningful by teaching the matcher about JsInitializerClause + JsPropertyClassMember.
- node.syntax().ancestors().skip(1).any(|ancestor| {
- matches!(
- ancestor.kind(),
- JsSyntaxKind::JS_RETURN_STATEMENT
- | JsSyntaxKind::JS_CALL_ARGUMENTS
- | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION
- | JsSyntaxKind::JS_LOGICAL_EXPRESSION
- | JsSyntaxKind::JS_THROW_STATEMENT
- | JsSyntaxKind::JS_AWAIT_EXPRESSION
- | JsSyntaxKind::JS_YIELD_EXPRESSION
- | JsSyntaxKind::JS_UNARY_EXPRESSION
- | JsSyntaxKind::JS_TEMPLATE_EXPRESSION
- | JsSyntaxKind::JS_CALL_EXPRESSION
- | JsSyntaxKind::JS_NEW_EXPRESSION
- | JsSyntaxKind::JS_IF_STATEMENT
- | JsSyntaxKind::JS_SWITCH_STATEMENT
- | JsSyntaxKind::JS_FOR_STATEMENT
- | JsSyntaxKind::JS_FOR_IN_STATEMENT
- | JsSyntaxKind::JS_FOR_OF_STATEMENT
- | JsSyntaxKind::JS_BINARY_EXPRESSION
- )
- })
+ node
+ .syntax()
+ .ancestors()
+ .skip(1)
+ .any(|ancestor| match ancestor.kind() {
+ JsSyntaxKind::JS_INITIALIZER_CLAUSE => ancestor
+ .parent()
+ .map_or(false, |parent| parent.kind() == JsSyntaxKind::JS_PROPERTY_CLASS_MEMBER),
+ JsSyntaxKind::JS_RETURN_STATEMENT
+ | JsSyntaxKind::JS_CALL_ARGUMENTS
+ | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION
+ | JsSyntaxKind::JS_LOGICAL_EXPRESSION
+ | JsSyntaxKind::JS_THROW_STATEMENT
+ | JsSyntaxKind::JS_AWAIT_EXPRESSION
+ | JsSyntaxKind::JS_YIELD_EXPRESSION
+ | JsSyntaxKind::JS_UNARY_EXPRESSION
+ | JsSyntaxKind::JS_TEMPLATE_EXPRESSION
+ | JsSyntaxKind::JS_CALL_EXPRESSION
+ | JsSyntaxKind::JS_NEW_EXPRESSION
+ | JsSyntaxKind::JS_IF_STATEMENT
+ | JsSyntaxKind::JS_SWITCH_STATEMENT
+ | JsSyntaxKind::JS_FOR_STATEMENT
+ | JsSyntaxKind::JS_FOR_IN_STATEMENT
+ | JsSyntaxKind::JS_FOR_OF_STATEMENT
+ | JsSyntaxKind::JS_BINARY_EXPRESSION => true,
+ _ => false,
+ })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fn is_used_in_expression_context(node: &AnyCandidateForUsedInExpressionNode) -> bool { | |
| node.syntax().ancestors().skip(1).any(|ancestor| { | |
| matches!( | |
| ancestor.kind(), | |
| JsSyntaxKind::JS_RETURN_STATEMENT | |
| | JsSyntaxKind::JS_CALL_ARGUMENTS | |
| | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION | |
| | JsSyntaxKind::JS_LOGICAL_EXPRESSION | |
| | JsSyntaxKind::JS_THROW_STATEMENT | |
| | JsSyntaxKind::JS_AWAIT_EXPRESSION | |
| | JsSyntaxKind::JS_YIELD_EXPRESSION | |
| | JsSyntaxKind::JS_UNARY_EXPRESSION | |
| | JsSyntaxKind::JS_TEMPLATE_EXPRESSION | |
| | JsSyntaxKind::JS_CALL_EXPRESSION | |
| | JsSyntaxKind::JS_NEW_EXPRESSION | |
| | JsSyntaxKind::JS_IF_STATEMENT | |
| | JsSyntaxKind::JS_SWITCH_STATEMENT | |
| | JsSyntaxKind::JS_FOR_STATEMENT | |
| | JsSyntaxKind::JS_FOR_IN_STATEMENT | |
| | JsSyntaxKind::JS_FOR_OF_STATEMENT | |
| | JsSyntaxKind::JS_BINARY_EXPRESSION | |
| ) | |
| }) | |
| fn is_used_in_expression_context(node: &AnyCandidateForUsedInExpressionNode) -> bool { | |
| node | |
| .syntax() | |
| .ancestors() | |
| .skip(1) | |
| .any(|ancestor| match ancestor.kind() { | |
| // Class field initializer: treat `this.foo` in `bar = this.foo` as meaningful. | |
| JsSyntaxKind::JS_INITIALIZER_CLAUSE => ancestor | |
| .parent() | |
| .map_or(false, |parent| parent.kind() == JsSyntaxKind::JS_PROPERTY_CLASS_MEMBER), | |
| JsSyntaxKind::JS_RETURN_STATEMENT | |
| | JsSyntaxKind::JS_CALL_ARGUMENTS | |
| | JsSyntaxKind::JS_CONDITIONAL_EXPRESSION | |
| | JsSyntaxKind::JS_LOGICAL_EXPRESSION | |
| | JsSyntaxKind::JS_THROW_STATEMENT | |
| | JsSyntaxKind::JS_AWAIT_EXPRESSION | |
| | JsSyntaxKind::JS_YIELD_EXPRESSION | |
| | JsSyntaxKind::JS_UNARY_EXPRESSION | |
| | JsSyntaxKind::JS_TEMPLATE_EXPRESSION | |
| | JsSyntaxKind::JS_CALL_EXPRESSION | |
| | JsSyntaxKind::JS_NEW_EXPRESSION | |
| | JsSyntaxKind::JS_IF_STATEMENT | |
| | JsSyntaxKind::JS_SWITCH_STATEMENT | |
| | JsSyntaxKind::JS_FOR_STATEMENT | |
| | JsSyntaxKind::JS_FOR_IN_STATEMENT | |
| | JsSyntaxKind::JS_FOR_OF_STATEMENT | |
| | JsSyntaxKind::JS_BINARY_EXPRESSION => true, | |
| _ => false, | |
| }) | |
| } |
🤖 Prompt for AI Agents
In crates/biome_js_analyze/src/services/semantic_class.rs around lines 899 to
921, the matcher currently overlooks class field initialisers so reads like
`class C { bar = this.foo; }` are treated as trivial; update the ancestor kind
check to include JsInitializerClause and JsPropertyClassMember (i.e. add these
JsSyntaxKind variants to the matches! list) so that a node inside a class field
initializer is considered an expression context / meaningful read. Ensure both
variants are added to the same matches! arm and run tests.
Summary
Adds is_meaningful_read property to ClassMemberReference
This change introduces an is_meaningful_read field on the ClassMemberReference struct.
The purpose of this property is to distinguish between meaningful and non-meaningful (useless) reads, so that rules like no_unused_class_properties can reason more precisely about property usage.
• Applicability:
• The flag is relevant only for reads.
• Writes do not use this distinction, so for all writes, is_meaningful_read is set to None.
• Reads, however, will have Some(true) or Some(false).
A meaningful read is a property access whose value is actually consumed in the program’s logic.
Examples of meaningful reads:
full list of what counts as meaningul read context:
Example of non meaningful reads:
Next step is to simplify no_unused_class_properties to use SemanticClass and extract ONLY reads that have is_meaningful_read set to true.
part of #7499