mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-25 03:49:59 +08:00
fix(dart): a typedef references the types it aliases (#2763)
In
class Report { final int score; const Report(this.score); }
typedef ReportCallback = void Function(Report r);
`codegraph callers Report` did not list ReportCallback. Both extractors
minted the typedef's node and nothing else. tree-sitter-dart gives
`type_alias` no fields, so extractTypeAlias found no `value` to read and
had no Dart branch, and the kernel's extract_type_alias mirrored that.
Impact on a model type missed every callback or collection type built
on it. The legacy form with a return type, `typedef Report
LegacyMaker(int n);`, was also named after that return type: with no
`name` field the name fell back to the first type name in the
declaration, so a `type_alias Report` (or `Widget`, `Future`, `bool`)
stood beside the real type and took references meant for it.
Both extractors now name a typedef by the last type name before its
`=`, its type parameters or its parameter list, and add a `references`
ref from the typedef, on each type's name, for every type its aliased
type names: a function type's return and parameter types, generic
arguments, record fields, a type parameter's bound, and the legacy
form's return and parameter types. Annotations are not part of it, an
import prefix names no type, and a type parameter (the typedef's own,
or one a generic function type or an old-style generic function-typed
parameter declares) is skipped unless written through a prefix. Kernel
and wasm output are identical; a new parity fixture,
TortureTypedefs.dart, pins every form in LF and CRLF.
Validation, kernel path, arms built from dbb6aa9b and from this change,
each with its own kernel:
- felangel/bloc b9be1e25: 17,875 -> 17,884 edges (+9, 0 removed).
- rrousselGit/riverpod 4ded4f01: 84,726 -> 84,761 (+35, 0 removed).
- jonataslaw/getx e3c8fa04: 8,436 -> 8,456 (+20, 0 removed).
- alibaba/flutter-go a41b2124 (9 legacy typedefs, 6 with a return
type): 7,994 -> 7,968; 39 wrong links removed (34 `Widget` and 5
`Future` uses that had gone to typedefs misnamed `Widget` and
`Future`), 13 added (4 from typedefs, 9 fields typed by the renamed
ones).
- peng8350/flutter_pulltorefresh 0be096c1 (8 legacy, 5 with a return
type): 3,186 -> 2,976; 221 wrong `Widget` links removed, 11 added.
All 88 added non-contains edges were checked against the source; every
removed edge pointed at a typedef misnamed after its return type. The
other new typedef refs name SDK or Flutter types and stay unresolved.
gin and zustand dumps are byte-identical across the arms, kernel and
wasm dumps are byte-identical on bloc, riverpod and pulltorefresh, and
the parity sweep over the five Dart repos is 2,330/2,633 files at
parity, 0 diffs, 303 deferred on both arms. Index time is unchanged
within noise (median of 5 interleaved runs: riverpod 12.6 s vs 11.9 s,
bloc 5.0 s vs 5.0 s, getx 2.8 s vs 2.8 s), and the DB grows by 8 to
24 KB (pulltorefresh shrinks by 64 KB).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
dbb6aa9ba9
commit
bf6f1fa67e
@@ -156,6 +156,23 @@ fn next_named<'t>(node: Node<'t>) -> Option<Node<'t>> {
|
||||
next
|
||||
}
|
||||
|
||||
/// dartTypeAliasName (dart.ts) — the name a typedef declares: the last type
|
||||
/// name before the `=`, the type parameters or the parameter list. The
|
||||
/// legacy form writes its return type first (`typedef Report LegacyMaker(int
|
||||
/// n);`), so the first type name is not always it.
|
||||
fn type_alias_name<'t>(node: Node<'t>) -> Option<Node<'t>> {
|
||||
let mut name = None;
|
||||
let mut cursor = node.walk();
|
||||
for child in node.children(&mut cursor) {
|
||||
match child.kind() {
|
||||
"=" | "type_parameters" | "formal_parameter_list" => break,
|
||||
"type_identifier" => name = Some(child),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
name
|
||||
}
|
||||
|
||||
struct Scope {
|
||||
row: u32,
|
||||
kind: &'static str,
|
||||
@@ -668,6 +685,14 @@ impl<'t> Walker<'t> {
|
||||
/// name field → the method_signature inner unwrap → identifier-ish
|
||||
/// child → `<anonymous>` (operators land here).
|
||||
fn extract_name(&self, node: Node<'t>) -> String {
|
||||
// resolveName hook (dart.ts): a typedef has no `name` field, and a
|
||||
// legacy one writes its return type before its name (`typedef Report
|
||||
// LegacyMaker(int n);`).
|
||||
if node.kind() == "type_alias" {
|
||||
if let Some(name) = type_alias_name(node) {
|
||||
return self.text(name).to_string();
|
||||
}
|
||||
}
|
||||
// resolveName hook (dart.ts:244-260): named ctor/factory → ctor name.
|
||||
if let Some((class_name, ctor_name)) = self.ctor_info(node) {
|
||||
if ctor_name != class_name {
|
||||
@@ -1034,12 +1059,76 @@ impl<'t> Walker<'t> {
|
||||
return false;
|
||||
}
|
||||
let docstring = self.docstring_of(node);
|
||||
// `value` field is null (type_alias has no fields) → no refs from
|
||||
// the aliased type; returns false → children re-visited.
|
||||
self.create_node("type_alias", &name, node, Extra { docstring, ..Default::default() });
|
||||
// type_alias has no fields (no `value`): the dart branch reads the
|
||||
// aliased type from the rest of the declaration (#2327). Returns
|
||||
// false → children re-visited (they match nothing type-related).
|
||||
let row = self.create_node("type_alias", &name, node, Extra { docstring, ..Default::default() });
|
||||
if let Some(row) = row {
|
||||
self.type_alias_refs(node, row);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// pushDartTypeAliasRefs (dart.ts) — a `references` ref from the typedef
|
||||
/// for every type its aliased type names; its annotations are not its
|
||||
/// type, and a type parameter is no type of the project's.
|
||||
fn type_alias_refs(&mut self, node: Node<'t>, from_row: u32) {
|
||||
let name = type_alias_name(node);
|
||||
let scope = self.type_parameter_scope(node, &[]).unwrap_or_default();
|
||||
let mut cursor = node.walk();
|
||||
let kids: Vec<Node<'t>> = node.named_children(&mut cursor).collect();
|
||||
for child in kids {
|
||||
if child.kind() == "annotation" || Some(child) == name {
|
||||
continue;
|
||||
}
|
||||
self.aliased_type_refs(child, from_row, &scope);
|
||||
}
|
||||
}
|
||||
|
||||
/// dartTypeParameterScope (dart.ts) — `scope` and the type parameters
|
||||
/// `node` declares (`Mapper<T>`, `T Function<T>(T a)`), or None when it
|
||||
/// declares none.
|
||||
fn type_parameter_scope(&self, node: Node<'t>, scope: &[&'t str]) -> Option<Vec<&'t str>> {
|
||||
let mut cursor = node.walk();
|
||||
let parameters = node.named_children(&mut cursor).find(|c| c.kind() == "type_parameters")?;
|
||||
let mut inner = scope.to_vec();
|
||||
let mut pc = parameters.walk();
|
||||
for parameter in parameters.named_children(&mut pc) {
|
||||
if parameter.kind() != "type_parameter" {
|
||||
continue;
|
||||
}
|
||||
let mut ic = parameter.walk();
|
||||
let name = parameter.named_children(&mut ic).find(|c| c.kind() == "type_identifier");
|
||||
if let Some(name) = name {
|
||||
inner.push(self.text(name));
|
||||
}
|
||||
}
|
||||
Some(inner)
|
||||
}
|
||||
|
||||
/// pushDartAliasedTypeRefs (dart.ts) — type_refs_dart for a typedef's
|
||||
/// aliased type, where a type parameter in `scope` is no type of its own
|
||||
/// (but `p.T` is the type an import names).
|
||||
fn aliased_type_refs(&mut self, node: Node<'t>, from_row: u32, scope: &[&'t str]) {
|
||||
stack_guard!();
|
||||
if node.kind() == "type_identifier" {
|
||||
let text = self.text(node);
|
||||
let parameter =
|
||||
scope.contains(&text) && !node.prev_sibling().map(|s| s.kind() == ".").unwrap_or(false);
|
||||
if !parameter && self.is_type_name_dart(node) {
|
||||
self.push_ref_at(from_row, text, "references", node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let inner = self.type_parameter_scope(node, scope);
|
||||
let scope = inner.as_deref().unwrap_or(scope);
|
||||
let mut cursor = node.walk();
|
||||
let kids: Vec<Node<'t>> = node.named_children(&mut cursor).collect();
|
||||
for child in kids {
|
||||
self.aliased_type_refs(child, from_row, scope);
|
||||
}
|
||||
}
|
||||
|
||||
// --- extractImport (:3170; hook dart.ts extractImport) -----------------
|
||||
|
||||
fn extract_import(&mut self, node: Node<'t>) {
|
||||
|
||||
Reference in New Issue
Block a user