How Code-Graph-RAG Handles Duplicate Code Definitions and Method Overloading
Code-Graph-RAG treats duplicate code definitions—including method overloads—as distinct entities tagged with line-number markers, enabling precise detection of clones while preserving semantic relationships for accurate call resolution.
This article explains the architectural approach used by vitali87/code-graph-rag to identify, group, and resolve duplicate code definitions. The system handles everything from exact copy-paste clones to language-level overloading through a unified duplicate detection pipeline.
Duplicate Detection Architecture
The duplicate detection system operates through two complementary strategies implemented in [codebase_rag/duplicates.py](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/duplicates.py).
Exact-Copy Detection
Code-Graph-RAG first identifies structurally identical definitions by computing a whole-AST fingerprint—a cryptographic hash of the complete abstract syntax tree representation. Identical fingerprints guarantee identical code structure regardless of source location.
The collect_duplicates() function delegates to _exact_groups() for this phase:
# codebase_rag/duplicates.py (lines 61-78)
def _exact_groups(members: list[Member]) -> list[DuplicateGroup]:
fingerprint_map: dict[str, list[Member]] = defaultdict(list)
for m in members:
fp = m.structural_fingerprint()
fingerprint_map[fp].append(m)
# Groups with >1 member are exact duplicates
return [
DuplicateGroup(kind="exact", similarity=1.0, members=ms)
for fp, ms in fingerprint_map.items()
if len(ms) > 1
]
This approach is computationally efficient—O(n) for n definitions—and catches copy-paste duplication without false negatives.
Similarity-Based Detection
For definitions that differ slightly (refactored parameters, renamed variables, modified bodies), Code-Graph-RAG employs branch-fingerprint similarity using Jaccard overlap:
# codebase_rag/duplicates.py (lines 41-50)
def _similar_groups(
members: list[Member],
threshold: float = 0.6
) -> list[DuplicateGroup]:
# Build prefix index for efficient candidate generation
index = PrefixIndex(m.branch_fingerprint for m in members)
candidates = _candidate_pairs(index, threshold)
# Find maximal cliques of mutually similar members
cliques = _maximal_cliques(candidates, members)
return [
DuplicateGroup(kind="similar", similarity=score, members=clique)
for clique, score in cliques
]
The AllPairs/PPJoin indexing strategy reduces the search space from O(n²) to near-linear for typical codebases.
Nested Member Pruning
To prevent false positives where an inner function appears "similar" to its containing outer function, the system drops contained members:
# codebase_rag/duplicates.py
def _drop_contained_members(members: list[Member]) -> list[Member]:
return [
m for m in members
if not any(_member_nested_in(other, m) for other in members)
]
This ensures that legitimate nesting relationships don't trigger spurious duplicate reports.
The DUP_QN_MARKER System for Overloads
The core mechanism distinguishing duplicate definitions is the DUP_QN_MARKER suffix appended to qualified names.
Marker Definition
Located in [codebase_rag/constants/core.py](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/constants/core.py):
# codebase_rag/constants/core.py
DUP_QN_MARKER = "@" # <qualified_name>@<start_line>
DUP_QN_COLUMN_MARKER = "_" # Optional: @<line>_<column>
When a qualified name appears multiple times, each instance receives a unique marker encoding its source position.
Parser Integration
Java method resolution demonstrates the marker application in [codebase_rag/parsers/java/method_resolver.py](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/java/method_resolver.py):
# codebase_rag/parsers/java/method_resolver.py
def _resolve_method_declaration(node: Tree, ctx: ParseContext) -> Method:
natural_qn = f"{ctx.current_type}.{node.name}"
start_line = node.start_point[0] + 1 # 1-indexed
# Append duplicate marker for overloads
if ctx.is_overloaded(natural_qn):
variant = f"{natural_qn}{cs.DUP_QN_MARKER}{start_line}"
else:
variant = natural_qn
return Method(qualified_name=variant, ...)
This guarantees that Vehicle.start() on line 45 and Vehicle.start() on line 78 become Vehicle.start@45 and Vehicle.start@78 respectively.
Normalization for Semantic Comparison
Downstream components strip markers before hierarchical or signature-based comparisons:
# codebase_rag/duplicates.py
import re
from codebase_rag.constants import core as cs
_DUP_QN_MARKER_RE = re.compile(
re.escape(cs.DUP_QN_MARKER) + r"\d+(?:"
+ re.escape(cs.DUP_QN_COLUMN_MARKER) + r"\d+)?$"
)
def _qn_normalized(qn: str) -> str:
"""Remove overload/duplicate markers for semantic comparison."""
return _DUP_QN_MARKER_RE.sub("", qn)
This dual representation enables both precision and flexibility: the marked qn preserves identity for graph storage, while the normalized form enables type hierarchy and signature matching.
Overload Resolution in Practice
The Java parser implements complete overload resolution through ranked candidate selection.
Candidate Collection and Ranking
# codebase_rag/parsers/java/method_resolver.py
def resolve_overload(
base_qn: str, # e.g., "Utils.toString"
arg_types: tuple[str | None, ...],
candidates: list[Method]
) -> Method | None:
# Filter by normalized name
overloads = [m for m in candidates
if _qn_normalized(m.qn) == base_qn]
# Score each candidate
ranked = [
(_overload_rank(m, arg_types), m)
for m in overloads
]
ranked = [(score, m) for score, m in ranked if score is not None]
return min(ranked, key=lambda x: x[0])[1] if ranked else None
def _overload_rank(method: Method, arg_types: tuple) -> int | None:
params = method.parameter_types
if len(params) != len(arg_types):
return None # Arity mismatch
# Exact type matches score 0, inheritance distance scores higher
score = sum(
_inheritance_distance(arg, param)
for arg, param in zip(arg_types, params)
)
return score
The ranking algorithm prefers exact signature matches (score 0), then closest inheritance distance for polymorphic arguments.
Python @overload Handling
For Python's @overload decorator pattern, the parser treats each stub as a distinct definition:
# Example: Python overload stubs become separate graph nodes
from typing import overload
@overload
def process(data: bytes) -> str: ...
@overload
def process(data: str) -> bytes: ...
def process(data): # Implementation (line 11)
# runtime logic
pass
Graph representation:
mymodule.process@3→ first overload (bytes → str)mymodule.process@6→ second overload (str → bytes)mymodule.process@11@11→ implementation (note: implementation may also receive marker if name collision detected)
The duplicate detection engine groups these via _qn_normalized(), while call resolution uses argument type inference to select the appropriate overload stub.
Duplicate Group Output Format
The final DuplicateGroup data structure carries complete provenance:
@dataclass
class DuplicateGroup:
kind: Literal["exact", "similar"]
similarity: float # 1.0 for exact, 0.0-1.0 for similar
members: list[Member]
# Metadata for diagnostics
skipped_symbols: list[str] = field(default_factory=list)
truncated: bool = False
This enables downstream tools to:
- Flag exact clones for refactoring prioritization
- Review similar code for abstraction opportunities
- Preserve overload relationships in API documentation
Summary
- Duplicate detection operates through exact AST fingerprinting and Jaccard-based similarity analysis in [
duplicates.py](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/duplicates.py) - Overload differentiation uses the
DUP_QN_MARKER(@) suffix defined in [constants/core.py](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/constants/core.py) to create unique qualified names for same-named definitions at different locations - Semantic preservation via
_qn_normalized()ensures type hierarchies and call resolution work correctly despite marker suffixes - Overload ranking in [
method_resolver.py](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/parsers/java/method_resolver.py) selects optimal candidates by signature matching and inheritance distance - Nested member pruning prevents false positives from legitimate code nesting patterns
Frequently Asked Questions
How does Code-Graph-RAG distinguish between code clones and legitimate overloads?
Both clones and overloads receive DUP_QN_MARKER suffixes to create unique graph nodes. The distinction emerges in downstream processing: clones typically share fingerprints (exact) or high Jaccard similarity, while overloads have different signatures and participate in method resolution. The _qn_normalized() function treats them identically for type hierarchy queries.
What happens when more than two definitions share the same qualified name?
The marker system scales arbitrarily. Each additional definition receives its own <qn>@<line> entry. The duplicate detection engine forms maximal cliques of similar definitions, so a method with five overloads creates a single group containing all five variants if they're structurally similar, or multiple groups if some variants diverge significantly.
Does the marker system affect cross-language analysis?
The DUP_QN_MARKER constant is language-agnostic. Java, Python, and TypeScript parsers all apply the same convention, enabling unified duplicate detection across polyglot codebases. Language-specific overload rules (Java's subtyping vs. Python's nominal typing) are handled in their respective method resolvers before the generic duplicate grouping logic executes.
Can duplicate detection be tuned for specific similarity thresholds?
Yes. The _similar_groups() function accepts a threshold parameter defaulting to 0.6. Lower values detect more aggressive refactoring candidates; higher values restrict reports to near-identical code. This threshold propagates from CLI configuration through [duplicates.py](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/duplicates.py) without requiring code changes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →