Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b0a23b1
refactor(parser): add CalleeQualifier scaffold (Bare-only, no behavio…
sdsrss May 10, 2026
0b9be5f
feat(parser): capture Rust scoped_identifier path qualifier in edge m…
sdsrss May 10, 2026
5358846
refactor(parser): idiomatic strip + identifier-arm comment (T2 follow…
sdsrss May 10, 2026
8b93bc0
test(parser): cover single-segment Path qualifier (Type::method)
sdsrss May 10, 2026
ca40732
test(parser): crate-only Path qualifier collapses to Bare
sdsrss May 10, 2026
a45760a
test(parser): super:: strip, multi-segment Path, chained reserved pre…
sdsrss May 10, 2026
dec6761
feat(parser): capture Rust field_expression Receiver qualifier
sdsrss May 10, 2026
e2db603
test(parser): builder chain emits Chain qualifier
sdsrss May 10, 2026
f847211
refactor(parser): thread current_rust_impl through walk_for_relations
sdsrss May 10, 2026
32c7c3d
feat(parser): SelfRecv qualifier picks up impl block type
sdsrss May 10, 2026
cd7cfc4
feat(parser): SelfType qualifier for Self::method() inside impl
sdsrss May 10, 2026
8574d15
test(parser): regression guard — non-Rust callee metadata stays None
sdsrss May 10, 2026
b2ff8bf
feat(resolver): add parse_callee_metadata for q/v wire format
sdsrss May 10, 2026
7a74f10
feat(resolver): drop edges for q=chain and q=recv qualifiers
sdsrss May 10, 2026
8705cf8
feat(resolver): Path qualifier filter via file-segment + qualified_na…
sdsrss May 10, 2026
fe49682
test(resolver): Path qualifier disambiguates same-name candidates
sdsrss May 10, 2026
5c7c6b7
feat(resolver): SelfRecv/SelfType filter via qualified_name LIKE
sdsrss May 10, 2026
be12d56
fix(parser): strip impl type prefix so SelfRecv resolves across split…
sdsrss May 10, 2026
46c11ec
test(resolver): non-Rust callgraph baseline unchanged
sdsrss May 10, 2026
6e1ddef
chore(release): v0.24.0 — bare-name call qualifier (Rust)
sdsrss May 10, 2026
224c353
chore(parser,resolver): drop dead_code suppressors + stale task-progr…
sdsrss May 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
},
"metadata": {
"description": "AST knowledge graph plugin for Claude Code — semantic search, call graph, HTTP tracing, impact analysis",
"version": "0.23.1"
"version": "0.24.0"
},
"plugins": [
{
"name": "code-graph-mcp",
"source": "./claude-plugin",
"description": "AST knowledge graph for intelligent code navigation — auto-indexes your codebase and provides semantic search, call graph traversal, HTTP route tracing, and impact analysis via MCP tools",
"version": "0.23.1",
"version": "0.24.0",
"author": {
"name": "sdsrs"
},
Expand Down
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
# Changelog

## v0.24.0 — Bare-name call qualifier (Rust)

### Fixed
- callgraph: Rust qualified calls (`Type::method`, `crate::path::fn`,
`self.method`, `Self::method`, builder chains like `OpenOptions::new().create()`)
no longer route to unrelated project functions sharing the rightmost name.
Eliminates phantom callers in `impact_analysis` and `find_dead_code` for
short-named functions (`new`/`create`/`open`/`from`).
- parser: `impl crate::path::Type { ... }` impl-block type now strips the
leading path so qualified_name and SelfRecv payloads match (was producing
`crate::path::Type.method` qualified_names that broke same-type LIKE
matching).

### 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.

### Verification
- `impact run_full_index`: 36 → 33 transitive callers; the 3 documented
phantoms (decompress_with_cap, try_acquire_index_lock, from_project_root)
no longer appear.
- routing_bench P@1: 22/22 (no regression).
- 558 tests pass with default + `--no-default-features`. Clippy clean with
`--all-features`.

## v0.23.1 — snapshot UX + FTS garbage-query guard

Follow-up enhancements to v0.23.0 snapshot work plus an unrelated
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "code-graph-mcp"
version = "0.23.1"
version = "0.24.0"
edition = "2021"

[features]
Expand Down
2 changes: 1 addition & 1 deletion claude-plugin/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"author": {
"name": "sdsrs"
},
"version": "0.23.1",
"version": "0.24.0",
"keywords": [
"code-graph",
"ast",
Expand Down
2 changes: 1 addition & 1 deletion npm/darwin-arm64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sdsrs/code-graph-darwin-arm64",
"version": "0.23.1",
"version": "0.24.0",
"description": "code-graph-mcp binary for macOS ARM64",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion npm/darwin-x64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sdsrs/code-graph-darwin-x64",
"version": "0.23.1",
"version": "0.24.0",
"description": "code-graph-mcp binary for macOS x64",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion npm/linux-arm64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sdsrs/code-graph-linux-arm64",
"version": "0.23.1",
"version": "0.24.0",
"description": "code-graph-mcp binary for Linux ARM64",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion npm/linux-x64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sdsrs/code-graph-linux-x64",
"version": "0.23.1",
"version": "0.24.0",
"description": "code-graph-mcp binary for Linux x64",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion npm/win32-x64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sdsrs/code-graph-win32-x64",
"version": "0.23.1",
"version": "0.24.0",
"description": "code-graph-mcp binary for Windows x64",
"license": "MIT",
"repository": {
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sdsrs/code-graph",
"version": "0.23.1",
"version": "0.24.0",
"description": "MCP server that indexes codebases into an AST knowledge graph with semantic search, call graph traversal, and HTTP route tracing",
"license": "MIT",
"repository": {
Expand Down Expand Up @@ -35,10 +35,10 @@
"node": ">=16"
},
"optionalDependencies": {
"@sdsrs/code-graph-linux-x64": "0.23.1",
"@sdsrs/code-graph-linux-arm64": "0.23.1",
"@sdsrs/code-graph-darwin-x64": "0.23.1",
"@sdsrs/code-graph-darwin-arm64": "0.23.1",
"@sdsrs/code-graph-win32-x64": "0.23.1"
"@sdsrs/code-graph-linux-x64": "0.24.0",
"@sdsrs/code-graph-linux-arm64": "0.24.0",
"@sdsrs/code-graph-darwin-x64": "0.24.0",
"@sdsrs/code-graph-darwin-arm64": "0.24.0",
"@sdsrs/code-graph-win32-x64": "0.24.0"
}
}
81 changes: 81 additions & 0 deletions src/indexer/pipeline/index_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,87 @@ pub(super) fn index_files(
}
}

// Bare-name call qualifier (Rust): inspect metadata to
// skip / restrict candidate set before the existing fallback
// chain. See spec
// docs/superpowers/specs/2026-05-11-bare-name-call-qualifier-design.md.
if rel.relation == REL_CALLS {
use super::resolve::{parse_callee_metadata, path_filter_candidates, self_filter_candidates, CalleeMeta};
match parse_callee_metadata(rel.metadata.as_deref()) {
Some(CalleeMeta::Chain) | Some(CalleeMeta::Receiver(_)) => {
// Receiver type not statically inferable; same-language
// unique match is overwhelmingly false. Drop the edge
// entirely (do not buffer in pending — re-scan won't help).
continue;
}
Some(CalleeMeta::SelfRecv(impl_type)) | Some(CalleeMeta::SelfType(impl_type)) => {
let all = name_to_ids.get(&rel.target_name).cloned().unwrap_or_default();
let same_lang: Vec<i64> = all
.iter()
.filter(|id| matches!(
node_id_to_language.get(id).and_then(|l| l.as_deref()),
Some(l) if l == pf.language.as_str()
))
.copied()
.collect();
let filtered = self_filter_candidates(&impl_type, &same_lang, db)?;
if filtered.is_empty() {
// No method on this impl type found in the project.
// Drop without buffering — qualifier is fixed and a
// re-scan will yield the same answer.
continue;
}
for &src_id in &source_ids {
for &tgt_id in &filtered {
if src_id != tgt_id
&& insert_edge_cached(db.conn(), src_id, tgt_id, &rel.relation, rel.metadata.as_deref())?
{
total_edges_created += 1;
}
}
}
continue;
}
Some(CalleeMeta::Path(segments)) => {
let all = name_to_ids.get(&rel.target_name).cloned().unwrap_or_default();
let same_lang: Vec<i64> = all.iter()
.filter(|id| matches!(
node_id_to_language.get(id).and_then(|l| l.as_deref()),
Some(l) if l == pf.language.as_str()
))
.filter(|id| !local_ids.contains(id))
.copied()
.collect();
let filtered = path_filter_candidates(
&segments,
&same_lang,
&node_id_to_path,
db,
)?;
if filtered.is_empty() {
// No project candidate matches the Path qualifier.
// External crate (or unmatched module) — drop without buffering.
continue;
}
let final_targets = if filtered.len() > 1 {
refine_ambiguous_targets(&filtered, &pf.rel_path, &node_id_to_path)
} else {
filtered
};
for &src_id in &source_ids {
for &tgt_id in &final_targets {
if src_id != tgt_id
&& insert_edge_cached(db.conn(), src_id, tgt_id, &rel.relation, rel.metadata.as_deref())? {
total_edges_created += 1;
}
}
}
continue;
}
_ => {} // None (Bare) or unrecognized q → falls through to default chain below.
}
}

// Default resolution: global name-based lookup with language-aware layering.
// Tier order: same-file → same-language → (calls: drop) / (other: global).
// Dropping calls without a same-language match prevents Rust `hasher.update()`
Expand Down
Loading
Loading