mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-19 06:22:38 +08:00
fix(dart): link calls in top-level and field initializers (#2367)
Both Dart extractors (the TS walker and the native kernel) skipped the code in initializers. A `final`/`const` declaration minted its constant and stopped, and a field's or `var`'s initializer only had its types recorded (#2360). In Riverpod apps, where most wiring lives in provider closures (`final repoProvider = Provider((ref) => Repository(ref.watch(dioProvider)));`), what a provider builds with had no callers, and impact and explore flows stopped at the provider. - A `final`/`const` initializer is walked like a body, for its constant: calls, instantiations, static and member reads, types, closures with block bodies and local functions, function values. - A field's (instance, `static var`, typed, `late`) or a top-level `var`/`late final` initializer is walked for its class or the file, since neither mints a node. A local declaration's second variable (`for (var i = 0, j = n(); ...)`) stays its function's. - Each initializer is walked once: a new `walkInitializer` context method marks it, and the dispatcher's function-as-value scan skips it. Other languages' hooks are unchanged. - Resolver: a Dart call through a type's static constant (`FutureProvider.autoDispose(...)` on `static const autoDispose = ...Builder();`) links that constant, not another type's same-named method. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,9 @@
|
||||
//! `method "<anonymous>"`; the unnamed constructor is skipped
|
||||
//! (isMisparsedFunction) while named ctors/factories are named by the CTOR
|
||||
//! name with the class as returnType; instance fields mint NO nodes (only
|
||||
//! static_final_declaration → constant, via the hook); prefixed return
|
||||
//! static_final_declaration → constant, via the hook); every initializer is
|
||||
//! body-walked once — a constant's from the constant, a field's or a
|
||||
//! top-level variable's from the class or the file; prefixed return
|
||||
//! types keep the PREFIX (`other.OtherClass f()` → returnType `other` —
|
||||
//! bug, preserved); enum `with` mixins emit nothing while enum `implements`
|
||||
//! works; deferred imports are invisible; named-argument callbacks are NOT
|
||||
@@ -629,24 +631,34 @@ impl<'t> Walker<'t> {
|
||||
});
|
||||
let name = self.text(name_node).to_string();
|
||||
let constant = self.create_node("constant", &name, node, Extra { signature, ..Default::default() });
|
||||
// The types its initializer names are the constant's (#2327).
|
||||
// The initializer is code the constant runs — its calls,
|
||||
// instantiations, reads and the types it names (#2327) are
|
||||
// the constant's. walkInitializer: the body walk, and the
|
||||
// dispatcher's fn-ref scan skips what it walked.
|
||||
if let Some(constant) = constant {
|
||||
let mut value = name_node.next_named_sibling();
|
||||
while let Some(v) = value {
|
||||
self.type_refs_dart(v, constant);
|
||||
value = v.next_named_sibling();
|
||||
}
|
||||
self.stack.push(Scope { row: constant, kind: "constant", name });
|
||||
self.visit_body(node);
|
||||
self.stack.pop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.scan_fn_ref_subtree(node, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// A field's declared type and initializer types are its class's (Dart
|
||||
// fields mint no nodes); a top-level variable's are the file's (#2327).
|
||||
// The visitNode hook's `declaration` / `program` rows (dart.ts).
|
||||
// (Parent lookups walk down from the root, so only the node kinds that
|
||||
// can be these positions ask for theirs.)
|
||||
// A field's or top-level variable's initializer is code its class or
|
||||
// the file runs (neither mints a node) — the hook's
|
||||
// `initialized_identifier` row, walked like a body (walkInitializer).
|
||||
if node.kind() == "initialized_identifier" && self.is_field_or_top_level_entry(node) {
|
||||
self.visit_body(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// A field's declared type is its class's (Dart fields mint no nodes);
|
||||
// a top-level variable's is the file's (#2327). The visitNode hook's
|
||||
// `declaration` / `program` rows (dart.ts). (Parent lookups walk down
|
||||
// from the root, so only the node kinds that can be these positions
|
||||
// ask for theirs.)
|
||||
if node.kind() == "declaration" {
|
||||
let in_body = node
|
||||
.parent()
|
||||
@@ -661,26 +673,16 @@ impl<'t> Walker<'t> {
|
||||
if is_field {
|
||||
let owner = self.top_row();
|
||||
for child in kids {
|
||||
match child.kind() {
|
||||
"initialized_identifier_list" => self.initializer_type_refs_dart(child, owner),
|
||||
"static_final_declaration_list" => {}
|
||||
_ => self.type_refs_dart(child, owner),
|
||||
if !matches!(child.kind(), "initialized_identifier_list" | "static_final_declaration_list") {
|
||||
self.type_refs_dart(child, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let top_level_type =
|
||||
matches!(node.kind(), "type_identifier" | "type_arguments" | "function_type" | "record_type");
|
||||
if (top_level_type || node.kind() == "initialized_identifier_list")
|
||||
&& node.parent().map(|p| p.kind() == "program").unwrap_or(false)
|
||||
{
|
||||
let owner = self.top_row();
|
||||
if top_level_type {
|
||||
self.type_refs_dart(node, owner);
|
||||
} else {
|
||||
self.initializer_type_refs_dart(node, owner);
|
||||
}
|
||||
}
|
||||
} else if matches!(node.kind(), "type_identifier" | "type_arguments" | "function_type" | "record_type")
|
||||
&& node.parent().map(|p| p.kind() == "program").unwrap_or(false)
|
||||
{
|
||||
let owner = self.top_row();
|
||||
self.type_refs_dart(node, owner);
|
||||
}
|
||||
|
||||
// maybeCaptureFnRefs (:990) — the double-walk fn-ref twin source.
|
||||
@@ -1387,21 +1389,23 @@ impl<'t> Walker<'t> {
|
||||
}
|
||||
}
|
||||
|
||||
/// pushDartInitializerTypeRefs (dart.ts) — each `initialized_identifier`'s
|
||||
/// value (every named child after its name).
|
||||
fn initializer_type_refs_dart(&mut self, list: Node<'t>, from_row: u32) {
|
||||
let mut cursor = list.walk();
|
||||
let entries: Vec<Node<'t>> = list.named_children(&mut cursor).collect();
|
||||
for entry in entries {
|
||||
if entry.kind() != "initialized_identifier" {
|
||||
continue;
|
||||
}
|
||||
let mut ec = entry.walk();
|
||||
let parts: Vec<Node<'t>> = entry.named_children(&mut ec).skip(1).collect();
|
||||
for part in parts {
|
||||
self.type_refs_dart(part, from_row);
|
||||
}
|
||||
/// isDartFieldOrTopLevelEntry (dart.ts) — an `initialized_identifier`
|
||||
/// declaring a field or a top-level variable, not the second variable of
|
||||
/// a local declaration (`for (var i = 0, j = n(); …)`).
|
||||
fn is_field_or_top_level_entry(&self, node: Node<'t>) -> bool {
|
||||
let Some(list) = node.parent() else { return false };
|
||||
if list.kind() != "initialized_identifier_list" {
|
||||
return false;
|
||||
}
|
||||
let Some(owner) = list.parent() else { return false };
|
||||
if owner.kind() == "program" {
|
||||
return true;
|
||||
}
|
||||
owner.kind() == "declaration"
|
||||
&& owner
|
||||
.parent()
|
||||
.map(|p| matches!(p.kind(), "class_body" | "extension_body" | "enum_body"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// dartMemberRead (dart.ts) — `x.area` / `x?.area` not followed by a call's
|
||||
|
||||
Reference in New Issue
Block a user