Skip to content

v0.24.0 fix(callgraph): Rust qualifier-aware resolver eliminates phantom callers#19

Merged
sdsrss merged 21 commits into
mainfrom
feat/bare-name-call-qualifier
May 10, 2026
Merged

v0.24.0 fix(callgraph): Rust qualifier-aware resolver eliminates phantom callers#19
sdsrss merged 21 commits into
mainfrom
feat/bare-name-call-qualifier

Conversation

@sdsrss
Copy link
Copy Markdown
Owner

@sdsrss sdsrss commented May 10, 2026

Summary

Captures qualifier metadata on Rust call expressions (Type::method, crate::path::fn, self.method, Self::method, builder chains) and uses it at edge-resolution time to eliminate phantom callers in impact_analysis and find_dead_code.

Core changes (parser → wire → resolver):

  • Parser (helpers.rs, mod.rs): CalleeQualifier enum (Bare / Path / SelfType / SelfRecv / Receiver / Chain); extract_callee Rust-only dispatch on scoped_identifier + field_expression; reserved-prefix strip (crate/super/self); current_rust_impl plumbed through walk_for_relations.
  • Wire format (zero schema migration — edges.metadata TEXT column already existed): {"q":"path","v":"snapshot"} / {"q":"self","v":"Db"} / {"q":"chain"} etc.
  • Resolver (resolve.rs, index_files.rs Phase 2): parse_callee_metadata + path_filter_candidates (file segment + qualified_name segment match) + self_filter_candidates (qualified_name LIKE 'Type.%', not file-restricted). Dispatch: Chain/Receiver → drop; Path-no-match → drop; Path-match → use filtered set; Self/SelfType-match → use filtered set; Bare → existing fallback chain unchanged.
  • Bonus fix (treesitter.rs + relations/mod.rs): strip path prefix from impl type so impl crate::path::Db { ... } produces Db.method qualified_names instead of crate::path::Db.method — was breaking same-type LIKE matching for split impl blocks.

Verification

  • impact run_full_index: 36 → 33 transitive callers (3 documented phantoms removed: decompress_with_cap, try_acquire_index_lock, from_project_root — all called File::create / OpenOptions::create(), not snapshot::create)
  • routing_bench P@1: 22/22 = 100% (no regression)
  • 558 tests pass (default + --no-default-features + --all-features)
  • Clippy clean with --all-features
  • Rust calls edge qualifier coverage: 21.5% (382/1780): Path 272 + SelfRecv 93 + SelfType 17. The 1398 bare-NULL edges are stdlib + free-function calls (no qualifier syntax at call site — expected).

Migration

Existing .code-graph/ databases keep working (qualifier-aware resolution is a no-op when edges.metadata IS NULL). Run code-graph-mcp index --rebuild to populate qualifier metadata on existing Rust files; incremental indexing picks it up automatically as files change.

Diff stats

18 files, +917 / -22 lines. 21 commits (TDD-cycled per parser feature + per resolver rule).

Out of scope (future followups)

  • Go selector_expression / JS field_expression / C# member_access_expression / PHP scoped_call_expression qualifier capture — Go has cleanest signal (explicit imports), would benefit most.
  • C++ Class::method qualified_identifier scope (tracked separately).
  • Cross-file type inference (let p = Path::new(); p.exists() recovering p's type) — different problem class.

Test plan

  • cargo test --no-default-features — all green
  • cargo test --all-features — all green
  • cargo +1.95.0 clippy --all-targets --all-features -- -D warnings — no warnings
  • cargo bench --bench routing_bench — P@1 22/22, no regression
  • Index this repo + verify 3 documented phantoms removed
  • CHANGELOG + 9-file version sync (sync-versions.js)

[skip-memory-check]

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed Rust callgraph routing to prevent bare-name calls (e.g., Type::method, crate::path::fn) from incorrectly attributing calls to unrelated functions.
    • Corrected qualified impl block path parsing.
  • Chores

    • Released version 0.24.0.
    • Existing databases remain compatible; run code-graph-mcp index --rebuild to populate enhanced metadata.

Review Change Stack

sdsrss and others added 21 commits May 11, 2026 02:40
…r change)

Add CalleeQualifier enum and extract_callee() wrapper to helpers.rs.
Swap the call_expression arm in mod.rs to call extract_callee() instead
of extract_callee_name() directly; qualifier is discarded (metadata stays
None) so runtime behavior is identical. Subsequent tasks layer in
Rust-specific qualifier extraction.
…etadata

Walk scoped_identifier trees to collect path segments, strip reserved
prefixes (crate/super/self), and serialize non-bare qualifiers as
{"q":"path","v":"<segments>"} in edges.metadata. Bare calls (identifier
or post-strip-empty path) produce None metadata, preserving backward
compatibility with non-Rust edges and old DB rows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-up)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds Chain+Receiver early-exit in Phase 2 inline resolution: when
parse_callee_metadata returns Chain or Receiver, the edge is dropped
before the same-file / same-language lookup, eliminating phantom
callers from builder chains (e.g. OpenOptions::new().create()
falsely binding to snapshot::create).

Integration test in tests/integration_call_qualifier.rs confirms
FAIL→PASS for the chain-builder case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… impls

`impl crate::db_a::Db { fn helper() }` stored qualified_name
`crate::db_a::Db.helper`, but the SelfRecv payload from the caller's
impl block was `Db` (bare name), making the LIKE pattern `Db.%` miss.

Strip `::` path prefix in both parser walks (treesitter.rs parent_class
capture and relations/mod.rs child_rust_impl) so the rightmost segment
`Db` is used consistently. Confirmed with new cross-file split-impl test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Guard that JS caller→helper bare-name edges survive unfiltered — the
qualifier resolver is Rust-only and must not touch other languages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ession comments

Final-review cleanup: by T18 every CalleeMeta variant + parse_callee_metadata
+ path_filter_candidates + self_filter_candidates + CalleeQualifier is consumed;
the #[allow(dead_code)] suppressors only mask future regressions. Wildcard arm
comment in index_files.rs Phase 2 dispatch updated to reflect actual semantics
(None / unrecognized q → fallthrough), not the now-irrelevant T16 reference.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 10, 2026

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81be57ab-f3a7-4417-b071-c070b8b7faac

📥 Commits

Reviewing files that changed from the base of the PR and between 1542ea0 and 224c353.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • .claude-plugin/marketplace.json
  • CHANGELOG.md
  • Cargo.toml
  • claude-plugin/.claude-plugin/plugin.json
  • npm/darwin-arm64/package.json
  • npm/darwin-x64/package.json
  • npm/linux-arm64/package.json
  • npm/linux-x64/package.json
  • npm/win32-x64/package.json
  • package.json
  • src/indexer/pipeline/index_files.rs
  • src/indexer/pipeline/resolve.rs
  • src/parser/relations/helpers.rs
  • src/parser/relations/mod.rs
  • src/parser/relations/tests.rs
  • src/parser/treesitter.rs
  • tests/integration_call_qualifier.rs

📝 Walkthrough

Walkthrough

This PR implements a Rust-specific "bare-name call qualifier" system to disambiguate function calls during code-graph analysis, then releases it as v0.24.0. The parser extracts qualifiers from Rust call syntax (path segments, self/impl receivers, or builder chains), serializes them as JSON metadata, and the indexer applies specialized filtering rules to route calls to the correct target while avoiding phantom edges to unrelated same-named functions. All manifests and changelog are updated accordingly.

Changes

Bare-name Call Qualifier for Rust

Layer / File(s) Summary
Parser Qualifier Types
src/parser/relations/helpers.rs, src/parser/relations/mod.rs, src/parser/treesitter.rs
CalleeQualifier enum captures disambiguation shapes (Path, SelfType, SelfRecv, Receiver, Chain, Bare). extract_callee() returns callee name and qualifier by analyzing Rust call syntax (scoped_identifier, field_expression). walk_for_relations() threads impl type context through AST traversal and serialize_callee_qualifier() emits JSON metadata.
Indexer Metadata Parsing and Filtering
src/indexer/pipeline/resolve.rs, src/indexer/pipeline/index_files.rs
CalleeMeta enum and parse_callee_metadata() decode JSON metadata. path_filter_candidates() and self_filter_candidates() filter call targets by path segments and impl type. Phase 2 resolution applies metadata-driven filtering before default fallback.
Parser Qualifier Extraction Tests
src/parser/relations/tests.rs
Tests validate reserved-prefix stripping (crate::..., super::...), path/type/method preservation, receiver qualification (obj.method()), builder chains, self-method context inside impl, and non-Rust regression checks.
End-to-End Integration Tests
tests/integration_call_qualifier.rs
Constructs temporary Rust/JavaScript projects and validates that qualifier-driven resolution avoids phantom call edges for builder chains, bare-name File::create, path-qualified crate::snapshot::create, self-method type isolation, and cross-file impl blocks.

Release v0.24.0 — Manifest and Changelog

Layer / File(s) Summary
Version Bump Across All Manifests
Cargo.toml, package.json, .claude-plugin/marketplace.json, claude-plugin/.claude-plugin/plugin.json, npm/*/*.json
Version bumped from 0.23.1 to 0.24.0 across all package and plugin manifests.
Release Notes and Migration Guidance
CHANGELOG.md
Documents v0.24.0 release covering Rust callgraph routing fixes, impl-block path-stripping, backward compatibility, and instructions to rebuild qualifier metadata on existing databases.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 A rabbit hops through the code with glee,
Call qualifiers now disambiguate with ease,
Path, self, and receiver shapes align,
No phantom edges blur the design!
Rust's callgraph now shines, refined and clean.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/bare-name-call-qualifier

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sdsrss sdsrss merged commit 6d918e7 into main May 10, 2026
8 of 9 checks passed
@sdsrss sdsrss deleted the feat/bare-name-call-qualifier branch May 10, 2026 20:01
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