Repository navigation
C#: calls through an interface are no longer resolved since 1.6.1 (interface members extracted as private) #2164
Description
Summary
Since 1.6.1, a C# call through an interface-typed receiver is no longer resolved. This covers fields, parameters and primary-constructor parameters. The call ends up in unresolved_refs instead of producing a calls edge. Nearly all service-to-service calls in dependency-injected .NET code look like this, so most callers of interface methods disappear from codegraph_callers and codegraph_impact.
The problem is still present on main (ca3bb44).
Reproduction
// Orders.Application/OrderService.cs
namespace Orders.Application;
public interface IOrderService
{
void CancelOrder(int id);
}
// Orders.Api/OrderController.cs
namespace Orders.Api;
public class OrderController(IOrderService service) // primary constructor
{
public void Cancel(int id) => service.CancelOrder(id);
}
// Orders.Api/FieldController.cs
public class FieldController
{
private readonly IOrderService _service;
public FieldController(IOrderService service) { _service = service; }
public void Cancel(int id) => _service.CancelOrder(id); // field
}
// Orders.Api/ParamController.cs
public class ParamController
{
public void Cancel(IOrderService service, int id) => service.CancelOrder(id); // parameter
}npx @colbymchenry/codegraph@<version> init -y .
sqlite3 .codegraph/codegraph.db "SELECT s.qualified_name, t.qualified_name FROM edges e
JOIN nodes s ON s.id = e.source JOIN nodes t ON t.id = e.target
WHERE e.kind = 'calls' AND s.file_path LIKE 'Orders.Api/%'"| Version | calls edges from Orders.Api |
|---|---|
| 1.6.0 | 3 (all three controllers → IOrderService::CancelOrder) |
| 1.6.1 | 0 (service.CancelOrder / _service.CancelOrder in unresolved_refs) |
main @ ca3bb44 |
0 |
Root cause
git bisect between v1.6.0 and v1.6.1 identifies 2c251e2, "fix(resolution): a definition its language makes file-local is not a cross-file target (#1731) (#1745)".
That commit declines cross-file targets with visibility === 'private' for csharp, which is in PRIVATE_IS_FILE_LOCAL. The C# extractor, however, labels every member without an access modifier as private (src/extraction/languages/csharp.ts):
getVisibility: (node) => {
...
return 'private'; // C# defaults to private
},That default is correct for class and struct members, but not for interface members. They are implicitly public, and they almost never carry a modifier. In a 1.6.0 index, every interface method has visibility = 'private':
method Orders.Application::IOrderService::CancelOrder private
method Orders.Application::OrderService::CancelOrder public
So since #1745, every interface method is treated as file-local, and every call through an interface from another file is dropped.
Suggested fix
In the C# getVisibility, return public for a member without a modifier when its enclosing declaration is an interface_declaration. Two related cases have the same problem:
- Top-level types without a modifier. Their C# default is
internal, notprivate, so they are visible to every file in the assembly. - Explicit interface implementations (
void IOrderService.CancelOrder(int id)). They have no modifier but are reachable through the interface.
A regression test would be the reproduction above: an interface in one file, called through a field, a parameter and a primary-constructor parameter in another file. It would also be worth re-running the #1745 corpora with a C# project, since the table in that commit has no C# corpus.
Environment
- @colbymchenry/codegraph 1.6.1, and
main@ca3bb44 - Linux, Node 24.18, wasm extraction
Activity
maxmilian commentedon Oct 1, 2026
Verified fixed on
main: I ran the reproduction from this issue (all three controllers — primary-constructor parameter, field, and parameter) against a build ofmain@17993905, and all threecallsedges toOrders.Application::IOrderService::CancelOrderare back:The same project on the published 1.6.1 still gives 0, as reported. The fix is f9530d7 (#2171), which makes C# interface members default to
public(and namespace-level types tointernal) insrc/extraction/languages/csharp.ts, so the #1745 file-local rule no longer drops them. #2171 didn't reference this issue, so it stayed open; it should be resolved once the next release ships.colbymchenry commentedon Oct 2, 2026
Thanks for the precise bisect! This is fixed on
mainby #2171, which makes C# interface members default topublic(and namespace-level types tointernal), so the file-local rule no longer drops calls through an interface. I re-ran your three-controller reproduction against currentmain: all threecallsedges toIOrderService::CancelOrderare back. It ships in the next release; re-index C# projects after upgrading.