fix(csharp-extractor): capture aliased usings and qualified new expressions in the call graph#441
Conversation
…ressions in the call graph Two C# extractor edge cases dropped real declarations: 1. Aliased using directives whose target is a single undotted name (`using Alias = System;`) emit the target as a plain `identifier`, not a `qualified_name`. The alias branch only looked for `qualified_name` and returned null, dropping the whole import. Now it falls back to the identifier that follows the `=` token (not the first identifier, which is the alias name itself). 2. `object_creation_expression` for a qualified type (`new System.Text.StringBuilder()`) places the type as a `qualified_name` child, which neither the `identifier` nor `generic_name` lookup matched, so the object creation was dropped from the call graph. Added `qualified_name` to the fallback chain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thejesh23
left a comment
There was a problem hiding this comment.
1. Aliased using drops the alias name entirely.
For using Alias = System; the import is recorded as { source: "System", specifiers: ["System"] }, so downstream consumers can't tell the symbol bound in scope is Alias. The alias is the only name actually resolvable in the file; emitting specifiers: ["Alias"] (or both) would match how import 'x' as y; should surface in PR #435's Dart extractor too.
2. Qualified generic new is untested and likely partially wrong.
new System.Collections.Generic.List<int>() isn't covered by either the existing new List<int>() test or the new qualified-type test. Depending on how tree-sitter-c-sharp shapes this (qualified_name wrapping a generic_name, vs. a distinct node), the callee text may include the type arguments or silently fall through the fallback chain. Worth one explicit case.
3. global using and file-scoped alias forms are unhandled.
The extractUsing switch only matches using_directive; C# 10's global using Alias = System; parses as global_using_directive (or as a using_directive under a global modifier child, depending on grammar version), so the new alias fix doesn't reach it. A one-line note or follow-up issue would be useful.
Nit: the manual seenEquals scan duplicates the work findChild(node, "=") already did; a single pass that records both presence-of-= and the post-= identifier would be tidier.
Problem
Two C# extractor edge cases silently dropped real declarations:
Aliased using directives with a simple-identifier target.
extractUsingSourcehandled the alias formusing Alias = Some.Namespace;by looking only for aqualified_namechild after the=. When the alias target is a single, undotted name (e.g.using Alias = System;), tree-sitter emits the target as a plainidentifier, not aqualified_name. The alias branch returnednull, so the whole using directive was dropped from imports. Falling back to the existingfindChild(node, "identifier")would also be wrong, because the first identifier child is the alias nameAlias, not the targetSystem.newof a qualified type. Theobject_creation_expressionhandler only looked for a directidentifierorgeneric_namechild to name the constructed type. For a qualified type likenew System.Text.StringBuilder(), tree-sitter places the type as aqualified_namechild, so the object creation was dropped from the call graph entirely. (new Bar()andnew List<int>()were captured correctly.)Fix
In the alias branch of
extractUsingSource, when noqualified_nameis found, fall back to the identifier that appears after the=token (tracking aseenEqualsflag), so the target — not the alias name — is read.Add
qualified_nameto the type-node fallback chain in theobject_creation_expressionhandler:Testing
csharp-extractor.test.ts:using Alias = System;now yieldsimports == [{ source: "System", specifiers: ["System"], lineNumber: 1 }].new System.Text.StringBuilder()inside methodMnow produces call-graph entry{ caller: "M", callee: "new System.Text.StringBuilder", lineNumber: 1 }.[]; call graph[]) and pass after.eslint) and typecheck (tsc --noEmit) on the changed files both exit 0.🤖 Generated with Claude Code