Skip to content

fix(core): scope Rust impl methods to their type, dedup class-scoping walk - #72

Open
pradeepmouli wants to merge 1 commit into
intuit:mainfrom
pradeepmouli:upstream-pr/rust-impl-parent-scoping
Open

fix(core): scope Rust impl methods to their type, dedup class-scoping walk#72
pradeepmouli wants to merge 1 commit into
intuit:mainfrom
pradeepmouli:upstream-pr/rust-impl-parent-scoping

Conversation

@pradeepmouli

Copy link
Copy Markdown
Contributor

Summary

tree-sitter-rust's impl_item node has no name field — only body, trait, type, and type_parameters (verified against the crate's real node-types.json, not assumed). find_parent_class's generic child_by_field_name("name") walk silently returns None for it, so every Rust impl method — inherent or trait — fell back to a flat, unscoped file::method id instead of file::Type::method.

Concretely, this meant any two types in the same file with a same-named method (e.g. both implementing Display, or both having a new()) silently collapsed into a single graph symbol, with the second one dropped entirely by store_parquet.rs's sym_seen dedup guard — not just mis-scoped, actually missing from the graph.

Reproduced empirically before fixing: indexing a fixture with struct Alpha (impl Alpha { fn hello(&self) -> String }) and struct Beta (impl Greet for Beta { fn hello(&self) -> String }, identical signature) produced only one hello symbol in the graph — Beta::hello was silently absent.

Fix

  • rust/entities.scm's impl-method pattern now also captures the impl's own type: field as @method.parent (the Self type, e.g. Bar in impl Foo for Bar).
  • entities.rs consumes @method.parent through the same decompose-query mechanism (inherit_decompose_query) that relation extraction already uses for compound INHERITS-edge bases (generic/qualified impl targets) — reused, not duplicated. Falls back to the existing ancestor walk when the capture is absent, following the same capture-with-fallback idiom already established for @func.params/@func.return_type.
  • Along the way, consolidated find_parent_class (entities.rs) and find_enclosing_class (relations.rs), which had drifted into two independently-buggy copies of the same class-scoping walk (one had struct_specifier and the C++ out-of-line-method branch, the other had Elixir's defmodule and Pascal's declClass/declIntf) — now one shared function in extract/mod.rs, used by both.

No schema change; purely additive to entities.scm/entities.rs. Does not touch relations.scm.

Known residual gap (not fixed by this PR)

A single type with two same-named methods from different sources — e.g. an inherent impl and a trait impl of the same type (impl Bar { fn x() {} } + impl SomeTrait for Bar { fn x() {} }) — still collide, since both resolve @method.parent to the same type name. That's a genuine overload-disambiguation problem, not a scoping bug, and needs separate work (tracked in my fork, not part of this PR's scope). The added test test_rust_same_type_inherent_and_trait_impl_method_still_collide documents this explicitly as a known, tracked gap rather than letting it pass silently as "fixed."

Testing

  • Two new regression tests in entities.rs's test module: one confirms the fix (Alpha::hello/Beta::hello now survive as distinct, correctly-scoped symbols), one explicitly documents the known residual gap above.
  • Added tree-sitter-rust as an infigraph-core dev-dependency to hand-build the test's Query directly (mirroring the existing Kotlin/Dart test pattern) — using infigraph_languages::bundled_registry() from inside infigraph-core's own inline tests hits a dev-dependency-cycle type mismatch (two distinct compiled instances of ParserBackend), confirmed via compiler error, not guessed around.
  • Verified independently on a fresh worktree off upstream/main (not reusing my fork's branch results): cargo build --release -p infigraph-cli -p infigraph-mcp succeeds, full workspace cargo test --workspace --lib passes (738 tests, 0 failures), cargo fmt --all -- --check clean.
  • Note on clippy: cargo clippy --all-targets -- -D warnings on bare upstream/main (before this PR's changes) currently fails on an unrelated pre-existing lint in crates/infigraph-core/src/embed/mod.rs (chunks_exact_to_as_chunks) — confirmed this file isn't touched by this PR's diff at all, so it's local-toolchain-vs-CI drift (per this repo's own CLAUDE.md: dtolnay/rust-toolchain@stable floats, so a newer local clippy can surface lints CI's pinned version doesn't), not something this PR introduces or is responsible for fixing.

🤖 Generated with Claude Code

… walk

tree-sitter-rust's impl_item has no "name" field (only body/trait/type/
type_parameters, verified against its real node-types.json), so every
Rust impl method -- inherent or trait -- fell back to a flat, unscoped
file::method id. Two types in the same file with a same-named method
(e.g. both implementing Display, or both having new()) silently
collapsed into one symbol, with the second dropped entirely by
store_parquet.rs's sym_seen dedup guard.

Fix: rust/entities.scm now captures the impl's own `type:` field as
@method.parent; entities.rs consumes it via the same decompose-query
mechanism already used for compound INHERITS-edge bases (generic/
qualified impl targets), falling back to the existing ancestor walk
when absent -- same capture-with-fallback idiom as @func.params/
@func.return_type.

Also consolidates find_parent_class (entities.rs) and
find_enclosing_class (relations.rs), which had drifted into two
independently-buggy copies of the same class-scoping walk (one had
struct_specifier and the C++ out-of-line-method branch, the other had
Elixir's defmodule and Pascal's declClass/declIntf) -- now one shared
function in extract/mod.rs, used by both.

Known residual gap, not fixed here (tracked as
#125): a single type with two same-named methods
from different sources (e.g. an inherent impl and a trait impl of the
same type) still collide, since both resolve to the same parent name.
That needs the separate disambiguation work in #126.

Part of the symbol-identity-and-scoping-hardening spec's Phase 1
(docs/superpowers/specs/2026-08-30-symbol-identity-and-scoping-hardening-design.md).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant