Skip to content

Rust trait method declarations produce no nodes — 504/654 (77%) missing across 3 repos; trait_item never walks its body and function_signature_item is unhandled #3366

Description

@santoshpy

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions