Bug 1: Static method calls via MemberAccessExpr do not create caller→type edges
Expected
ProductListViewController should have a direct edge to AlertManager because the code calls AlertManager.showConfirmAlert(for:from:) at line 149.
Actual
No edge exists between ProductListViewController and AlertManager. The only connection is a 4-hop path through method nodes:
ProductListViewController → .tableView() --calls [INFERRED]--> .showConfirmAlert() ← AlertManager
The graph has AlertManager --method--> .showConfirmAlert() (ownership direction), but no reverse edge recording that ProductListViewController calls AlertManager.
Source code
ProductListViewController.swift:149 — calls AlertManager directly:
extension ProductListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let product = products[indexPath.row]
AlertManager.showConfirmAlert(for: product, from: self) // ← L149
}
}
AlertManager.swift:11 — the static method being called:
enum AlertManager {
static func showConfirmAlert(for product: Product, from viewController: UIViewController) {
// ...
let detailVC = ProductDetailViewController(product: product)
viewController.navigationController?.pushViewController(detailVC, animated: true)
// ...
}
}
Graph evidence
ProductListViewController (13 edges) — no edge to AlertManager:
ProductListViewController
--> UIViewController [inherits]
--> Product [references]
--> .loadProducts() [method]
--> .tableView() [method]
--> UITableView [references]
--> UITableViewDataSource [implements]
--> UITableViewDelegate [implements]
(no AlertManager edge)
AlertManager (2 edges) — no incoming calls:
AlertManager
--> .showConfirmAlert() [method]
<-- AlertManager.swift [contains]
(no incoming call edges from any caller)
Root cause
The Swift AST extractor handles MemberAccessExpr (AlertManager.showConfirmAlert) by creating the ownership direction (AlertManager --method--> .showConfirmAlert()), but does not create the caller direction (ProductListViewController --calls--> AlertManager).
Bug 2: Overloaded methods with identical base names are collapsed into a single graph node
Expected
Each overloaded method should produce a distinct graph node, or the node ID should include parameter type information for disambiguation (e.g., .init(product:) vs .init(coder:)).
Actual
Methods sharing the same base name are collapsed into one node. All edges from different overloads are merged together, making it impossible to tell which parameter/return type belongs to which method.
Source code — two init overloads in ProductDetailViewController
init(product:) at line 27:
init(product: Product) {
self.product = product
self.viewModel = ProductDetailViewModel(product: product)
super.init(nibName: nil, bundle: nil)
}
init?(coder:) at line 33:
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Source code — three tableView overloads in ProductListViewController
tableView(numberOfRowsInSection:) at line 110:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return products.count
}
tableView(cellForRowAt:) at line 114:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// ...
}
tableView(didSelectRowAt:) at line 145:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// ...
AlertManager.showConfirmAlert(for: product, from: self)
}
Graph evidence — single .init() node with merged edges
One node demo_productdetailviewcontroller_productdetailviewcontroller_init (label: .init()) carries all edges from both inits:
.init()
--> Product [parameter_type] L27 ← from init(product:)
--> ProductDetailViewModel [call] L29 ← from init(product:)
--> NSCoder [parameter_type] L33 ← from init?(coder:)
Graph evidence — single .tableView() node with merged edges
One node demo_productlistviewcontroller_productlistviewcontroller_tableview (label: .tableView()) carries edges from all three delegate methods:
.tableView()
--> Int [return_type] L110 ← numberOfRowsInSection
--> UITableViewCell [return_type] L114 ← cellForRowAt
--> UITableView [parameter_type] L145 ← didSelectRowAt
--> IndexPath [parameter_type] L145 ← didSelectRowAt
--> .showConfirmAlert() [calls INFERRED] L149 ← didSelectRowAt
Impact
For Swift codebases, this is particularly severe because UIKit's UITableViewDataSource and UITableViewDelegate protocols require multiple same-named tableView(...) methods, and NSCoding requires two init variants. The merged nodes lose method-level granularity entirely.
Suggested fix
Include parameter type information in the method node ID, e.g.:
xxx_init_product vs xxx_init_coder
xxx_tableview_numberofrows vs xxx_tableview_cellforrowat vs xxx_tableview_didselectrowat
Bug 1: Static method calls via
MemberAccessExprdo not create caller→type edgesExpected
ProductListViewControllershould have a direct edge toAlertManagerbecause the code callsAlertManager.showConfirmAlert(for:from:)at line 149.Actual
No edge exists between
ProductListViewControllerandAlertManager. The only connection is a 4-hop path through method nodes:The graph has
AlertManager --method--> .showConfirmAlert()(ownership direction), but no reverse edge recording thatProductListViewControllercallsAlertManager.Source code
ProductListViewController.swift:149 — calls
AlertManagerdirectly:AlertManager.swift:11 — the static method being called:
Graph evidence
ProductListViewController(13 edges) — no edge to AlertManager:AlertManager(2 edges) — no incoming calls:Root cause
The Swift AST extractor handles
MemberAccessExpr(AlertManager.showConfirmAlert) by creating the ownership direction (AlertManager --method--> .showConfirmAlert()), but does not create the caller direction (ProductListViewController --calls--> AlertManager).Bug 2: Overloaded methods with identical base names are collapsed into a single graph node
Expected
Each overloaded method should produce a distinct graph node, or the node ID should include parameter type information for disambiguation (e.g.,
.init(product:)vs.init(coder:)).Actual
Methods sharing the same base name are collapsed into one node. All edges from different overloads are merged together, making it impossible to tell which parameter/return type belongs to which method.
Source code — two
initoverloads inProductDetailViewControllerinit(product:) at line 27:
init?(coder:) at line 33:
Source code — three
tableViewoverloads inProductListViewControllertableView(numberOfRowsInSection:) at line 110:
tableView(cellForRowAt:) at line 114:
tableView(didSelectRowAt:) at line 145:
Graph evidence — single
.init()node with merged edgesOne node
demo_productdetailviewcontroller_productdetailviewcontroller_init(label:.init()) carries all edges from both inits:Graph evidence — single
.tableView()node with merged edgesOne node
demo_productlistviewcontroller_productlistviewcontroller_tableview(label:.tableView()) carries edges from all three delegate methods:Impact
For Swift codebases, this is particularly severe because UIKit's
UITableViewDataSourceandUITableViewDelegateprotocols require multiple same-namedtableView(...)methods, andNSCodingrequires twoinitvariants. The merged nodes lose method-level granularity entirely.Suggested fix
Include parameter type information in the method node ID, e.g.:
xxx_init_productvsxxx_init_coderxxx_tableview_numberofrowsvsxxx_tableview_cellforrowatvsxxx_tableview_didselectrowat