fix(core): scope Rust impl methods to their type, dedup class-scoping walk - #72
Open
pradeepmouli wants to merge 1 commit into
Open
fix(core): scope Rust impl methods to their type, dedup class-scoping walk#72pradeepmouli wants to merge 1 commit into
pradeepmouli wants to merge 1 commit into
Conversation
… 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).
pradeepmouli
requested review from
WinterQuant,
johnintuit,
murari316 and
sandeep-mewara
as code owners
August 30, 2026 18:53
This was referenced Aug 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
tree-sitter-rust'simpl_itemnode has nonamefield — onlybody,trait,type, andtype_parameters(verified against the crate's realnode-types.json, not assumed).find_parent_class's genericchild_by_field_name("name")walk silently returnsNonefor it, so every Rust impl method — inherent or trait — fell back to a flat, unscopedfile::methodid instead offile::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 anew()) silently collapsed into a single graph symbol, with the second one dropped entirely bystore_parquet.rs'ssym_seendedup 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 }) andstruct Beta(impl Greet for Beta { fn hello(&self) -> String }, identical signature) produced only onehellosymbol in the graph —Beta::hellowas silently absent.Fix
rust/entities.scm's impl-method pattern now also captures the impl's owntype:field as@method.parent(the Self type, e.g.Barinimpl Foo for Bar).entities.rsconsumes@method.parentthrough the same decompose-query mechanism (inherit_decompose_query) that relation extraction already uses for compoundINHERITS-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.find_parent_class(entities.rs) andfind_enclosing_class(relations.rs), which had drifted into two independently-buggy copies of the same class-scoping walk (one hadstruct_specifierand the C++ out-of-line-method branch, the other had Elixir'sdefmoduleand Pascal'sdeclClass/declIntf) — now one shared function inextract/mod.rs, used by both.No schema change; purely additive to
entities.scm/entities.rs. Does not touchrelations.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.parentto 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 testtest_rust_same_type_inherent_and_trait_impl_method_still_collidedocuments this explicitly as a known, tracked gap rather than letting it pass silently as "fixed."Testing
entities.rs's test module: one confirms the fix (Alpha::hello/Beta::hellonow survive as distinct, correctly-scoped symbols), one explicitly documents the known residual gap above.tree-sitter-rustas aninfigraph-coredev-dependency to hand-build the test'sQuerydirectly (mirroring the existing Kotlin/Dart test pattern) — usinginfigraph_languages::bundled_registry()from insideinfigraph-core's own inline tests hits a dev-dependency-cycle type mismatch (two distinct compiled instances ofParserBackend), confirmed via compiler error, not guessed around.upstream/main(not reusing my fork's branch results):cargo build --release -p infigraph-cli -p infigraph-mcpsucceeds, full workspacecargo test --workspace --libpasses (738 tests, 0 failures),cargo fmt --all -- --checkclean.clippy:cargo clippy --all-targets -- -D warningson bareupstream/main(before this PR's changes) currently fails on an unrelated pre-existing lint incrates/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 ownCLAUDE.md:dtolnay/rust-toolchain@stablefloats, 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