detect_clones embeds symbol name+docstring, never the actual function body — finds name-alike functions, misses real code duplication
- Dominant language
- Rust
- Stars
- 88
- Forks
- 16
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
`detect_clones` (`crates/infigraph-mcp/src/tools/analysis/clones.rs`, `tool_detect_clones`) is described as "vector similarity" clone detection, but the embedding it computes similarity over is never the function's actual implementation — only its name and docstring:
```rust
let text = if !s.docstring.is_empty() {
format!("{} {}: {}", s.kind, s.name, s.docstring)
} else {
format!("{} {}", s.kind, s.name)
};
let emb = ... embedder.embed(&text) ...
```
This means two functions with similar-sounding names and docstrings but completely different bodies will score as "clones," while two functions that are byte-for-byte (or near-identical) copy-paste implementations under differently-named symbols with no/different docstrings will score as unrelated and never surface — which is exactly the case the tool exists to catch (its own doc comment says "identify copy-paste code and refactoring opportunities").
## Root cause
`embed()` is a generic `text: &str -> Vec` embedding call (`EmbedProvider::embed`, `crates/infigraph-core/src/embed/mod.rs`) — it will happily embed anything it's given. `detect_clones` simply never gives it the source code. There is no structural or body-text signal anywhere in the current pipeline for this feature.
## Why this is cheap to fix, not just theoretically nice
The actual source body for every symbol is already trivially available without any new AST work: every `Symbol` has `start_line`/`end_line` and a resolvable `file` path, and `get_code_snippet` (`tool_get_code_snippet` in `crates/infigraph-mcp/src/tools/graph.rs`) already does exactly this lookup via `infigraph_core::search::read_lines_from_file(&file_path, detail.start_line, detail.end_line)`. `detect_clones` can reuse that same helper to pull each symbol's real body before embedding it, instead of (or in addition to) the name/docstring text it uses today.
## Proposed fix
1. In `tool_detect_clones`, read each candidate symbol's actual source body via `read_lines_from_file` (same call `get_code_snippet` already makes), and embed that — either instead of, or concatenated with, the current `kind+name+docstring` text.
2. Consider exposing both signals as separate, combinable scores (e.g. `name_similarity` vs `body_similarity`) rather than collapsing them into one number — a near-identical body with a differently-named symbol (the real "copy-paste and renamed" case) is arguably the single most valuable clone class to catch, and it's specifically the one the current implementation is blind to.
3. Normalize whitespace/formatting differences before embedding the body (or rely on the embedder's own robustness to this) so trivial reformatting doesn't tank similarity scores for otherwise-identical logic.
4. Add a regression test: two functions with different names/no docstrings but identical (or near-identical, e.g. renamed local variables) bodies must be detected as a clone pair above the default threshold; two functions with similar names/docstrings but unrelated bodies must NOT be detected as a clone pair.
## Scope note
This does not require touching `tool_refactor` (`crates/infigraph-core/src/refactor`), which is a separate consumer of the embeddings pipeline — but worth checking during implementation whether it has the same name/docstring-only blind spot, since it's built on the same `embed_path`/`embeddings.bin` cache.
Contributor guide
Research direction
Start in crates/infigraph-mcp/src/tools/analysis/clones.rs at tool_detect_clones, then compare get_code_snippet in crates/infigraph-mcp/src/tools/graph.rs and read_lines_from_file in crates/infigraph-core/src/search. Trace how embeddings and clone thresholds are calculated. Done means body-based clone cases are detected, name-alike unrelated functions are excluded, and regression coverage verifies both behaviors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100