mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-20 16:05:00 +08:00
fix(go): a defined type links the types it is defined from (#2441)
A Go defined type (`type WatchChan <-chan WatchResponse`, gin's `type HandlerFunc func(*Context)` and `type HandlersChain []HandlerFunc`) is a `type_alias` node, and it referenced nothing: only an `=` alias (#2417) named the types on its right-hand side. Impact on `WatchResponse` or gin's `Context` missed the declarations built from them, and everything that uses those. Fix (TS extractor and the Rust kernel, mirrored): goAliasTypeNames / alias_type_names walk the `type` field of every type_alias node, a defined type's as well as an alias's, so each type it names becomes a `references` ref on that name, where resolution reads the package qualifier back. Still skipped: the declaration's own type parameters, Go's predeclared types, and now its own name written bare, which in a recursive type (prometheus' `type stateFn func(*Lexer) stateFn`) is the declaration itself: no self-edge, and no failed row that would make dead code treat every namesake as referenced. A qualified name is never skipped (`type PutResponse pb.PutResponse` names pb's). Resolution is unchanged. A defined type is a new type: goAliasTarget still follows `=` aliases only, so a method called on a defined type is not looked up on its underlying type. The gateTargetKind rule that leaves a type_alias's reference written through a package the index doesn't know unresolved now covers defined types too; without it, every such name bound to the same-file declaration of its name (`type Op clientv3.Op` linked itself). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+21
-12
@@ -590,8 +590,8 @@ impl<'t> Walker<'t> {
|
||||
Extra { docstring, is_exported, ..Extra::default() },
|
||||
);
|
||||
// (go has no `value` field — no TS-style type-ref walk or member
|
||||
// extraction.) An alias references what its `type` field names; a
|
||||
// defined type (`type_spec`) declares a type of its own.
|
||||
// extraction.) An alias and a defined type (`type_spec`) alike
|
||||
// reference what their `type` field names.
|
||||
if let Some(row) = row {
|
||||
let references = edge_kind_index("references").unwrap();
|
||||
for ty in self.alias_type_names(node) {
|
||||
@@ -873,16 +873,15 @@ impl<'t> Walker<'t> {
|
||||
}
|
||||
|
||||
/// goAliasTypeNames (languages/go.ts): the name nodes of the types an
|
||||
/// alias's `type` field names, in source order, but its own type
|
||||
/// parameters and the predeclared types. Only a `type_alias` is one here:
|
||||
/// a generic alias, which tree-sitter-go 0.23 parses as a `type_spec`
|
||||
/// alias's or a defined type's `type` field names, in source order, but
|
||||
/// its own type parameters, the predeclared types and its own name written
|
||||
/// bare (a recursive type's, `type stateFn func(*Lexer) stateFn`). A
|
||||
/// generic alias, which tree-sitter-go 0.23 parses as a `type_spec`
|
||||
/// around an error, never reaches the kernel (its file defers to wasm).
|
||||
fn alias_type_names(&self, node: Node<'t>) -> Vec<Node<'t>> {
|
||||
let mut names = Vec::new();
|
||||
if node.kind() != "type_alias" {
|
||||
return names;
|
||||
}
|
||||
let Some(ty) = node.child_by_field_name("type") else { return names };
|
||||
let own = node.child_by_field_name("name").map(|n| self.text(n));
|
||||
let mut params: HashSet<&str> = HashSet::new();
|
||||
if let Some(list) = node.child_by_field_name("type_parameters") {
|
||||
for decl in (0..list.named_child_count()).filter_map(|i| list.named_child(i)) {
|
||||
@@ -893,22 +892,32 @@ impl<'t> Walker<'t> {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.collect_alias_type_names(ty, ¶ms, &mut names);
|
||||
self.collect_alias_type_names(ty, ¶ms, own, false, &mut names);
|
||||
names
|
||||
}
|
||||
|
||||
fn collect_alias_type_names(&self, node: Node<'t>, params: &HashSet<&str>, out: &mut Vec<Node<'t>>) {
|
||||
/// `qualified`: the name of a `pkg.Name`, which is that package's
|
||||
/// whatever this one declares.
|
||||
fn collect_alias_type_names(
|
||||
&self,
|
||||
node: Node<'t>,
|
||||
params: &HashSet<&str>,
|
||||
own: Option<&str>,
|
||||
qualified: bool,
|
||||
out: &mut Vec<Node<'t>>,
|
||||
) {
|
||||
stack_guard!();
|
||||
if node.kind() == "type_identifier" {
|
||||
let text = self.text(node);
|
||||
if !params.contains(text) && !is_go_predeclared_type(text) {
|
||||
if !is_go_predeclared_type(text) && (qualified || (!params.contains(text) && own != Some(text))) {
|
||||
out.push(node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let qualified = node.kind() == "qualified_type";
|
||||
for i in 0..node.named_child_count() {
|
||||
if let Some(c) = node.named_child(i) {
|
||||
self.collect_alias_type_names(c, params, out);
|
||||
self.collect_alias_type_names(c, params, own, qualified, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user