[Bug]: C# nested type identity is truncated to one level, merging distinct methods into one node
code-review-graph version
2.3.8
Operating system
macOS
Python version
3.12.4
AI platform
claude-code
Output of code-review-graph status
Method nodes inside a nested class are qualified with only the immediate enclosing class, not the full containing-type path. When two nested classes share a name — the standard MediatR-on-ASP.NET feature layout used by gothinkster/aspnetcore-realworld-example-app — their methods collapse into a single node. One method is silently lost. No uncertainty caveat covers this.Steps to reproduce
mkdir -p /tmp/crg-nested && cd /tmp/crg-nested && git init -q . cat > Features.cs <<'EOF' using MediatR;
namespace App.Articles;
public class Details { public record Query(string Slug) : IRequest;
public class QueryHandler : IRequestHandler<Query, string>
{
public Task<string> Handle(Query request, CancellationToken ct) => Task.FromResult("details");
}
}
public class Edit { public record Query(string Slug) : IRequest;
public class QueryHandler : IRequestHandler<Query, string>
{
public Task<string> Handle(Query request, CancellationToken ct) => Task.FromResult("edit");
}
} EOF code-review-graph build python3 - <<'EOF' import sqlite3 c = sqlite3.connect('.code-review-graph/graph.db') for r in c.execute("select kind, qualified_name from nodes where kind!='File' order by qualified_name"): print(f"{r[0]:9} {r[1]}") EOF
Expected vs actual behavior
Expected Features.cs::Details.QueryHandler.Handle and Features.cs::Edit.QueryHandler.Handle as separate nodes.
Actual: Class Features.cs::Details Class Features.cs::Details.Query Class Features.cs::Details.QueryHandler Class Features.cs::Edit Class Features.cs::Edit.Query Class Features.cs::Edit.QueryHandler Function Features.cs::QueryHandler.Handle ← one node for two distinct methods
Additional context
This issue seems to affect more than C#
Cause parser.py:10298, when recursing into a class body
Julia is the only language that accumulates nested scope; every other language passes the bare simple name to _qualify (parser.py:14378). _julia_scope_join (parser.py:7428) already implements the needed join, including its repeated-prefix guard.
Impact
Silent node loss with no caveat. Any tool that addresses a method — callers_of, tests_for, impact radius, review context, refactor/rename — targets a node standing for two unrelated methods. This is the failure mode CRG's uncertainty machinery exists to prevent, and it is not covered.
Second symptom: only one level is ever retained
mkdir -p /tmp/crg-deep && cd /tmp/crg-deep && git init -q . cat > Deep.cs <<'EOF' namespace App; public class A { public class B { public class C { public void M() { } } } } EOF code-review-graph build
Nodes: Deep.cs::A, Deep.cs::A.B, Deep.cs::B.C (should be A.B.C), Deep.cs::C.M (should be A.B.C.M). The containment tree is broken as a result: A.B.C's CONTAINS edge comes from the File rather than from A.B, and the edge Deep.cs::C -> Deep.cs::C.M has a source node that does not exist. Separately, querying by the correctly-qualified nested name does not resolve — code-review-graph query children_of Details.QueryHandler returns target not indexed: no node matching 'Details.QueryHandler' even though that node exists.
Source: tirth8205/code-review-graph