Indexing any C# project produced zero `references` edges, so `codegraph_callers SomeDto` returned no hits even when the DTO was used as a param/return type across the codebase, and `codegraph_callees` on a service class only saw its `using` imports — the headline structural query silently degraded to text-search on half of every typical backend stack. Two root causes: 1. `csharp.ts` was missing `returnField` (default `'return_type'` doesn't exist on C# AST; the field is `'type'`) AND had `paramsField:'parameter_list'` (the node TYPE, not the field NAME `'parameters'`) — so parameter type extraction silently no-op'd. 2. `extractTypeRefsFromSubtree` only emitted refs for `type_identifier` leaves. C# tree-sitter doesn't produce `type_identifier` — it uses `identifier`, `predefined_type`, `qualified_name`, `generic_name`, `array_type`, `nullable_type`, `tuple_type`, etc. Fix: - `csharp.ts`: `paramsField:'parameters'`, `returnField:'type'`. - Route C# through a dedicated `extractCsharpTypeRefs` + `walkCsharpTypePosition`. Descends ONLY into known type fields (`parameter.type`, `method.type`, `property.type`, `variable_declaration.type`, `tuple_element.type`), so parameter NAMES like `request` in `Build(UserDto request)` never leak as type refs. - Hook `extractField` and `extractProperty` to call `extractTypeAnnotations` so property/field type refs land in the graph. Validation on dotnet/eShop (527 .cs files): C# `references` edges: 35 -> 925 (+26x) No regression in calls/imports/instantiates/extends/implements. Closes #381.
This commit is contained in:
@@ -742,6 +742,59 @@ func UseAliased() {
|
||||
expect(target?.filePath.replace(/\\/g, '/')).toBe('pkgb/lib.go');
|
||||
});
|
||||
|
||||
it('C# extracts references from method/property/field types (#381)', async () => {
|
||||
// Pre-#381, every C# project produced ZERO `references` edges:
|
||||
// csharp.ts was missing returnField, and the type-leaf walker
|
||||
// only recognized TS/Java's `type_identifier` nodes — C# uses
|
||||
// `identifier`/`predefined_type`/`qualified_name`/`generic_name`.
|
||||
const srcDir = path.join(tempDir, 'src');
|
||||
fs.mkdirSync(srcDir, { recursive: true });
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(srcDir, 'Dtos.cs'),
|
||||
`namespace MyApp;
|
||||
public class SessionInfoDto { public string Id { get; set; } = ""; }
|
||||
public class UserDto { public string Name { get; set; } = ""; }
|
||||
`
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(srcDir, 'Service.cs'),
|
||||
`using System.Threading.Tasks;
|
||||
namespace MyApp;
|
||||
public class DataExporter
|
||||
{
|
||||
public SessionInfoDto Build(UserDto user, SessionInfoDto session) { return session; }
|
||||
public Task<SessionInfoDto> BuildAsync(UserDto user) { return Task.FromResult(new SessionInfoDto()); }
|
||||
public SessionInfoDto Latest { get; set; } = new();
|
||||
private UserDto _cached;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
cg = await CodeGraph.init(tempDir, { index: true });
|
||||
|
||||
const sessionDto = cg
|
||||
.getNodesByKind('class')
|
||||
.find((n) => n.name === 'SessionInfoDto');
|
||||
const userDto = cg
|
||||
.getNodesByKind('class')
|
||||
.find((n) => n.name === 'UserDto');
|
||||
expect(sessionDto).toBeDefined();
|
||||
expect(userDto).toBeDefined();
|
||||
|
||||
const sessionIncoming = cg
|
||||
.getIncomingEdges(sessionDto!.id)
|
||||
.filter((e) => e.kind === 'references');
|
||||
const userIncoming = cg
|
||||
.getIncomingEdges(userDto!.id)
|
||||
.filter((e) => e.kind === 'references');
|
||||
|
||||
// SessionInfoDto: Build return, Build param, BuildAsync return (inside Task<>), Latest property.
|
||||
// UserDto: Build param, BuildAsync param, _cached field.
|
||||
expect(sessionIncoming.length).toBeGreaterThanOrEqual(4);
|
||||
expect(userIncoming.length).toBeGreaterThanOrEqual(3);
|
||||
});
|
||||
|
||||
it('Go: leaves stdlib calls (fmt.Println, etc.) external', async () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tempDir, 'go.mod'),
|
||||
|
||||
Reference in New Issue
Block a user