Summary
A Rust trait gets a node; the methods it declares do not. Across three real Rust
repositories, 504 of 654 trait method declarations (77%) have no node at all. The
ones that do appear are only there because an impl block in the same file happens to
define a method of the same name — the node's source_location points at the impl, not
at the declaration.
The effect is that a trait — the API contract — is present in the graph as a bare name
with no operations attached, so "what does this trait require an implementor to provide?"
cannot be answered from the graph.
Minimal reproduction
// src/lib.rs
pub trait Greeter {
/// signature only; implemented below in this same file
fn greet(&self) -> String;
/// default body, never overridden anywhere
fn shout(&self) -> String {
self.greet().to_uppercase()
}
}
pub struct Alice;
impl Greeter for Alice {
fn greet(&self) -> String {
"alice".to_string()
}
}
pub fn plain_function() -> u8 { 7 }
// src/lonely.rs — a trait with no implementor in the corpus
pub trait Lonely {
fn only_signature(&self) -> u8;
fn with_default(&self) -> u8 { 42 }
}
graphify extract . --code-only produces:
src/lib.rs: 5 nodes src/lonely.rs: 2 nodes
L1 lib.rs L1 lonely.rs
L1 Greeter L2 Lonely
L11 Alice (only_signature — absent)
L14 .greet() <- the impl at L14, (with_default — absent)
not the declaration at L3
L19 plain_function()
(shout — absent)
Note .greet() is anchored at L14. There is no node for the declaration at L3; the
only method node came from the impl. shout, which has a default body and no override
anywhere, is absent entirely. In lonely.rs, with no impl in the corpus, the trait
contributes zero method nodes.
Scale, on real repositories
Counting fn declarations inside trait blocks and checking whether a node with that
name exists in the same file (graphifyy 0.9.55, --code-only):
| repo |
traits |
trait methods |
with no node |
lost |
| Abhilekh-Cloud |
40 |
112 |
70 |
62% |
| Abhilekh-Collab |
37 |
144 |
91 |
63% |
| Abhilekh-App (rust-lib) |
87 |
398 |
343 |
86% |
| total |
164 |
654 |
504 |
77% |
Examples: Acts::policy_acts (libs/access-control/src/act.rs:8), enforce_action
(libs/access-control/src/collab.rs:11), TypeOption::json_cell and stringify_cell
(collab-database/src/fields/type_option/mod.rs:131,138), workspace_id / device_id
(collab-integrate/src/collab_builder.rs:72,73).
Where it comes from
In extractors/rust.py, the trait_item branch mints the trait node and then only
iterates trait_bounds — it never walks the trait's body:
if t in ("struct_item", "enum_item", "trait_item"):
name_node = node.child_by_field_name("name")
if name_node:
...
add_node(item_nid, item_name, line)
add_edge(file_nid, item_nid, "contains", line)
if t == "trait_item":
for c in node.children:
if c.type != "trait_bounds":
continue
...
And function_signature_item — the tree-sitter-rust node type for a bodyless
fn greet(&self) -> String; — does not appear anywhere in the package:
$ grep -rn "function_signature_item" site-packages/graphify/
(no matches)
So a signature-only method has no extraction path at all, and a default-bodied method
inside a trait body is not reached either.
Suggested fix
In the trait_item branch, walk the trait's declaration_list and emit a node per
function_signature_item and per function_item, with a contains (or defines) edge
from the trait node — mirroring what impl_item already does for impl methods. That
would also give traits the same shape as structs/enums, whose members are already
represented.
Two consequences worth having beyond the node count: explain <Trait> could list the
operations a trait requires, and an implementor's method could be linked to the
declaration it satisfies.
Environment
graphifyy 0.9.55 (uv tool, Python 3.12.12), macOS 15 arm64, graphify extract --code-only.
Related: #3365 (Dart source_location), found in the same cross-validation sweep.
Summary
A Rust trait gets a node; the methods it declares do not. Across three real Rust
repositories, 504 of 654 trait method declarations (77%) have no node at all. The
ones that do appear are only there because an
implblock in the same file happens todefine a method of the same name — the node's
source_locationpoints at the impl, notat the declaration.
The effect is that a trait — the API contract — is present in the graph as a bare name
with no operations attached, so "what does this trait require an implementor to provide?"
cannot be answered from the graph.
Minimal reproduction
graphify extract . --code-onlyproduces:Note
.greet()is anchored at L14. There is no node for the declaration at L3; theonly method node came from the impl.
shout, which has a default body and no overrideanywhere, is absent entirely. In
lonely.rs, with no impl in the corpus, the traitcontributes zero method nodes.
Scale, on real repositories
Counting
fndeclarations insidetraitblocks and checking whether a node with thatname exists in the same file (graphifyy 0.9.55,
--code-only):Examples:
Acts::policy_acts(libs/access-control/src/act.rs:8),enforce_action(
libs/access-control/src/collab.rs:11),TypeOption::json_cellandstringify_cell(
collab-database/src/fields/type_option/mod.rs:131,138),workspace_id/device_id(
collab-integrate/src/collab_builder.rs:72,73).Where it comes from
In
extractors/rust.py, thetrait_itembranch mints the trait node and then onlyiterates
trait_bounds— it never walks the trait's body:And
function_signature_item— the tree-sitter-rust node type for a bodylessfn greet(&self) -> String;— does not appear anywhere in the package:So a signature-only method has no extraction path at all, and a default-bodied method
inside a trait body is not reached either.
Suggested fix
In the
trait_itembranch, walk the trait'sdeclaration_listand emit a node perfunction_signature_itemand perfunction_item, with acontains(ordefines) edgefrom the trait node — mirroring what
impl_itemalready does for impl methods. Thatwould also give traits the same shape as structs/enums, whose members are already
represented.
Two consequences worth having beyond the node count:
explain <Trait>could list theoperations a trait requires, and an implementor's method could be linked to the
declaration it satisfies.
Environment
graphifyy 0.9.55 (uv tool, Python 3.12.12), macOS 15 arm64,
graphify extract --code-only.Related: #3365 (Dart
source_location), found in the same cross-validation sweep.