fix(dart): a bare call stays in the class hierarchy (#2145)

A receiver-less Dart call now reaches only a method of the enclosing class,
something it extends / mixes in / implements, or an extension on one of those
types, and the nearest one wins. The hierarchy is read from each declaration's
head (comments and type arguments dropped), since supertype edges don't exist
on the first pass. Body-less abstract members (extracted as owned functions)
are scoped too.

Mixin-application classes (`class A = B with C;`) were extracted as
`<anonymous>` with no supertypes; both the wasm extractor and the Rust kernel
now name them and emit their extends/implements refs (kernel parity verified
on bloc, riverpod, shelf, dio, getx).

A/B (edges removed / added): riverpod -1945/+517, shelf -785, getx -855/+18,
bloc -178/+95, dio -78.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-09-30 09:30:58 +00:00
committed by GitHub
parent 422db1866e
commit 0ff11b36e7
6 changed files with 397 additions and 2 deletions
+40 -1
View File
@@ -567,6 +567,18 @@ impl<'t> Walker<'t> {
return ctor_name;
}
}
// `class A = B with C;` — the mixin application's own identifier.
if node.kind() == "class_definition" {
let mut cursor = node.walk();
let application = node.named_children(&mut cursor).find(|c| c.kind() == "mixin_application_class");
if let Some(application) = application {
let mut ac = application.walk();
let id = application.named_children(&mut ac).find(|c| c.kind() == "identifier");
if let Some(id) = id {
return self.text(id).to_string();
}
}
}
if let Some(name_node) = node.child_by_field_name("name") {
return self.text(name_node).to_string();
}
@@ -1181,7 +1193,34 @@ impl<'t> Walker<'t> {
let mut cursor = node.walk();
let kids: Vec<Node<'t>> = node.named_children(&mut cursor).collect();
for child in kids {
if child.kind() == "superclass" {
if child.kind() == "mixin_application_class" {
// `class A = B with M implements I;` — extends B, implements
// each mixin and interface, in source order.
let mut cc = child.walk();
let application = child.named_children(&mut cc).find(|c| c.kind() == "mixin_application");
let Some(application) = application else { continue };
let mut ac = application.walk();
let parts: Vec<Node<'t>> = application.named_children(&mut ac).collect();
for t in parts {
match t.kind() {
"type_identifier" => {
let name = self.text(t).to_string();
self.push_ref_at(class_row, &name, "extends", t);
}
"mixins" | "interfaces" => {
let mut mc = t.walk();
let targets: Vec<Node<'t>> = t.named_children(&mut mc).collect();
for m in targets {
if t.kind() == "interfaces" || m.kind() == "type_identifier" {
let name = self.text(m).to_string();
self.push_ref_at(class_row, &name, "implements", m);
}
}
}
_ => {}
}
}
} else if child.kind() == "superclass" {
// extends type + `with` mixins (implements) — dart branch.
let mut cc = child.walk();
let targets: Vec<Node<'t>> = child.named_children(&mut cc).collect();