Version
0.10.8
Platform
macOS (Apple Silicon), Darwin 25.6.0
Summary
Two Swift methods on the same type that differ only in their parameter labels end up as a single node in the graph. The node that survives keeps one overload's line range, but it ends up holding the CALLS edges from both of them.
The practical fallout is that trace_path reports callers that don't exist. A function that only ever calls one overload shows up as a caller of whatever the other overload calls.
Overloading by argument label is pretty common in Swift (work(flag:) vs work(name:)), so this comes up a lot in real code.
Minimal reproduction
Three files, no dependencies, nothing to build.
Sources/Sink.swift
class Sink {
func target() {}
}
Sources/Service.swift
class Service {
let sink = Sink()
// Overload A: DOES call target()
func work(flag: Bool) {
self.sink.target()
}
// Overload B: does NOT call target()
func work(name: String) {
print(name)
}
}
Sources/Caller.swift
class Caller {
let service = Service()
// Calls ONLY overload B, which never reaches target()
func onlyCallsOverloadB() {
self.service.work(name: "x")
}
}
codebase-memory-mcp cli index_repository --repo_path <repro> --name overload-repro --mode fast
codebase-memory-mcp cli search_graph --project overload-repro --name_pattern '^work$'
codebase-memory-mcp cli trace_path --project overload-repro --function_name target --direction inbound --depth 3
Actual
search_graph finds one node where the source has two methods:
total: 1
work Method 10-12 1 2
Lines 10-12 are overload B, work(name:). Overload A at lines 5-7 gets no node at all. But the surviving node reports out 2, and the body of work(name:) is just print(name), so it has picked up the call to target() from the overload that no longer exists.
trace_path then reports a caller that isn't real:
function: target
direction: inbound
callers_total: 2
onlyCallsOverloadB 2 <- should not be here
work 1
onlyCallsOverloadB calls work(name:), and that body is a single print, so it never reaches target() at any depth.
Expected
Two separate nodes, one per overload, with target() reachable only from work(flag:).
Notes
parse_partial_count is 0 for this repo, so nothing is failing to parse and this isn't a grammar issue. It looks like the qualified name doesn't include the parameter labels that tell Swift overloads apart, so the second declaration just overwrites the first.
This might belong to the same family as the fabricated CALLS edges filed for Rust (#2053) and Go (#1906, #1909, #1927), but the mechanism looks different. There's no stdlib receiver involved here, and both symbols are project-local methods on the same type.
Version
0.10.8
Platform
macOS (Apple Silicon), Darwin 25.6.0
Summary
Two Swift methods on the same type that differ only in their parameter labels end up as a single node in the graph. The node that survives keeps one overload's line range, but it ends up holding the CALLS edges from both of them.
The practical fallout is that trace_path reports callers that don't exist. A function that only ever calls one overload shows up as a caller of whatever the other overload calls.
Overloading by argument label is pretty common in Swift (work(flag:) vs work(name:)), so this comes up a lot in real code.
Minimal reproduction
Three files, no dependencies, nothing to build.
Sources/Sink.swiftSources/Service.swiftSources/Caller.swiftActual
search_graph finds one node where the source has two methods:
Lines 10-12 are overload B, work(name:). Overload A at lines 5-7 gets no node at all. But the surviving node reports out 2, and the body of work(name:) is just print(name), so it has picked up the call to target() from the overload that no longer exists.
trace_path then reports a caller that isn't real:
onlyCallsOverloadB calls work(name:), and that body is a single print, so it never reaches target() at any depth.
Expected
Two separate nodes, one per overload, with target() reachable only from work(flag:).
Notes
parse_partial_count is 0 for this repo, so nothing is failing to parse and this isn't a grammar issue. It looks like the qualified name doesn't include the parameter labels that tell Swift overloads apart, so the second declaration just overwrites the first.
This might belong to the same family as the fabricated CALLS edges filed for Rust (#2053) and Go (#1906, #1909, #1927), but the mechanism looks different. There's no stdlib receiver involved here, and both symbols are project-local methods on the same type.