mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-18 13:53:01 +08:00
fix(dart): a comment inside a member chain no longer loses a call or a read (#2385)
tree-sitter-dart keeps a comment as a named node wherever it is written, so in a member chain broken over lines (`tester //`, then `.state(…)` on the next line: the empty `//` keeps dart format from joining the lines) the comment, not `tester`, is the sibling right before the `.state` selector. Both extractors (the TS walker and the native kernel) read that sibling as the receiver or the callee: - A call with a comment between the member and its arguments (`box.grow /* by */ (3)`) was lost, and recorded as a read of `box.grow`. - A getter read (`box //` + `.area`) or a static access (`Config //` + `.instance`) written after a comment was lost. - A call after a comment kept only its bare name. Since #2376 the resolver reads such a chain link back past comments, so those calls already linked correctly; they now carry the receiver in the name. Every sibling step along a member chain now skips `comment` and `documentation_comment` nodes: the callee before an argument part, dartReceiverOf / receiver_of, dartCalleeOfArgPart, the type in front of a constructor's type arguments, and the member read's receiver and next selector. The misparsed-generic-call gate keeps reading raw siblings, and now rejects a comment against the `<` (`ref.read /* c */<Repo>(p)`), so that shape stays a comparison instead of losing its read. Validated with the kernel loaded on felangel/bloc @b9be1e2, rrousselGit/riverpod @4ba1be2 and flutter/samples @63411527. No edge is added or removed: the 14 comment-split chains there are all in riverpod, and its 5 calls among them gain their receiver and stay unlinked. Kernel/wasm parity has 0 diffs on all three. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -120,6 +120,31 @@ fn is_prefixed(kind: &str) -> bool {
|
||||
matches!(kind, "unary_expression" | "await_expression")
|
||||
}
|
||||
|
||||
/// DART_COMMENTS (dart.ts) — line and block comments, and dartdoc.
|
||||
fn is_comment(node: Node) -> bool {
|
||||
matches!(node.kind(), "comment" | "documentation_comment")
|
||||
}
|
||||
|
||||
/// dartPrevNamed (dart.ts) — the named sibling before `node`, past comments:
|
||||
/// `tester //` + newline + `.state(…)` puts the comment, not `tester`, right
|
||||
/// before the `.state` selector.
|
||||
fn prev_named<'t>(node: Node<'t>) -> Option<Node<'t>> {
|
||||
let mut prev = node.prev_named_sibling();
|
||||
while let Some(p) = prev.filter(|p| is_comment(*p)) {
|
||||
prev = p.prev_named_sibling();
|
||||
}
|
||||
prev
|
||||
}
|
||||
|
||||
/// dartNextNamed (dart.ts) — the named sibling after `node`, past comments.
|
||||
fn next_named<'t>(node: Node<'t>) -> Option<Node<'t>> {
|
||||
let mut next = node.next_named_sibling();
|
||||
while let Some(n) = next.filter(|n| is_comment(*n)) {
|
||||
next = n.next_named_sibling();
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
struct Scope {
|
||||
row: u32,
|
||||
kind: &'static str,
|
||||
@@ -1082,7 +1107,8 @@ impl<'t> Walker<'t> {
|
||||
if !has_arg_part {
|
||||
return None;
|
||||
}
|
||||
let prev = node.prev_named_sibling()?;
|
||||
// Past comments: `box.grow /* by */ (3)` calls `box.grow`.
|
||||
let prev = prev_named(node)?;
|
||||
return self.callee_name(prev);
|
||||
}
|
||||
|
||||
@@ -1137,11 +1163,11 @@ impl<'t> Walker<'t> {
|
||||
}
|
||||
|
||||
/// dartReceiverOf (dart.ts) — the receiver of the member access
|
||||
/// `selector`: the node in front of it, or the end of an arrow function's
|
||||
/// body when the grammar ended the arrow in front of a generic call
|
||||
/// (`(ref) => ref.watch<int>(p)` → `((ref) => ref).watch<int>(p)`).
|
||||
/// `selector`: the node in front of it, past comments, or the end of an
|
||||
/// arrow function's body when the grammar ended the arrow in front of a
|
||||
/// generic call (`(ref) => ref.watch<int>(p)` → `((ref) => ref).watch<int>(p)`).
|
||||
fn receiver_of(&self, selector: Node<'t>) -> Option<Node<'t>> {
|
||||
let prev = selector.prev_named_sibling();
|
||||
let prev = prev_named(selector);
|
||||
let body = prev
|
||||
.filter(|p| p.kind() == "function_expression")
|
||||
.and_then(|p| p.named_child(p.named_child_count().checked_sub(1)?));
|
||||
@@ -1242,8 +1268,10 @@ impl<'t> Walker<'t> {
|
||||
if !matches!(args.kind(), "parenthesized_expression" | "record_literal") || args.start_byte() != gt.end_byte() {
|
||||
return None;
|
||||
}
|
||||
// A comment against the `<` (`ref.read /* c */<Repo>(p)`) is a
|
||||
// sibling of its own, so the callee is not against it.
|
||||
let before = lt.prev_named_sibling()?;
|
||||
if before.end_byte() != lt.start_byte() {
|
||||
if is_comment(before) || before.end_byte() != lt.start_byte() {
|
||||
return None;
|
||||
}
|
||||
// The type argument: `T`, `p.T`, `T<…>` or `p.T<…>`, and nothing else.
|
||||
@@ -1321,7 +1349,7 @@ impl<'t> Walker<'t> {
|
||||
|
||||
/// dartCalleeOfArgPart (dart.ts:100-116).
|
||||
fn callee_of_arg_part(&self, arg_part: Node<'t>) -> Option<String> {
|
||||
let prev = arg_part.prev_named_sibling()?;
|
||||
let prev = prev_named(arg_part)?;
|
||||
if prev.kind() == "identifier" {
|
||||
return Some(self.text(prev).to_string());
|
||||
}
|
||||
@@ -1339,7 +1367,7 @@ impl<'t> Walker<'t> {
|
||||
found
|
||||
});
|
||||
if let Some(method_id) = method_id {
|
||||
let accessor_prev = prev.prev_named_sibling();
|
||||
let accessor_prev = prev_named(prev);
|
||||
if let Some(ap) = accessor_prev {
|
||||
if ap.kind() == "identifier" {
|
||||
return Some(format!("{}.{}", self.text(ap), self.text(method_id)));
|
||||
@@ -1364,7 +1392,7 @@ impl<'t> Walker<'t> {
|
||||
if selector.named_child(0)?.kind() != "type_arguments" {
|
||||
return None;
|
||||
}
|
||||
let type_node = selector.prev_named_sibling()?;
|
||||
let type_node = prev_named(selector)?;
|
||||
if type_node.kind() != "identifier" {
|
||||
return None;
|
||||
}
|
||||
@@ -1687,11 +1715,11 @@ impl<'t> Walker<'t> {
|
||||
found
|
||||
});
|
||||
let Some(member) = member else { return };
|
||||
let Some(receiver) = node.prev_named_sibling() else { return };
|
||||
let Some(receiver) = prev_named(node) else { return };
|
||||
if receiver.kind() != "identifier" {
|
||||
return;
|
||||
}
|
||||
let next = node.next_named_sibling();
|
||||
let next = next_named(node);
|
||||
if let Some(next) = next {
|
||||
if next.kind() == "selector" {
|
||||
let mut nc = next.walk();
|
||||
|
||||
Reference in New Issue
Block a user