Address C# typed-property review feedback

This commit is contained in:
Carmel, Richard 2026-05-07 12:17:35 -05:00
parent 32a8853978
commit e72edb6003
4 changed files with 47 additions and 34 deletions

View file

@ -17,11 +17,7 @@
*/
import type { Capture, CaptureMatch } from 'gitnexus-shared';
import {
findNodeAtRange,
nodeToCapture,
syntheticCapture,
} from '../../utils/ast-helpers.js';
import { findNodeAtRange, nodeToCapture, syntheticCapture } from '../../utils/ast-helpers.js';
import { splitUsingDirective } from './import-decomposer.js';
import { computeCsharpArityMetadata } from './arity-metadata.js';
import { synthesizeCsharpReceiverBinding } from './receiver-binding.js';
@ -261,6 +257,8 @@ export function emitCsharpScopeCaptures(
function synthesizeGenericTypeArgumentReferences(root: SyntaxNode): CaptureMatch[] {
const out: CaptureMatch[] = [];
// Treat all generic type arguments as static type references, including
// declaration signatures and call-site generic instantiations.
visit(root, (node) => {
if (node.type !== 'generic_name') return;
const args = findNamedChild(node, 'type_argument_list');

View file

@ -1757,9 +1757,9 @@ export class LocalBackend {
// Run incoming-ref queries in parallel — they are independent.
const [ctorIncoming, fileIncoming, typedPropertyIncoming, typedProperties] =
await Promise.all([
executeParameterized(
repo.id,
`
executeParameterized(
repo.id,
`
MATCH (n)-[hm:CodeRelation]->(ctor:Constructor)
WHERE n.id = $symId AND hm.type = 'HAS_METHOD'
MATCH (caller)-[r:CodeRelation]->(ctor)
@ -1767,11 +1767,11 @@ export class LocalBackend {
RETURN r.type AS relType, caller.id AS uid, caller.name AS name, caller.filePath AS filePath, labels(caller)[0] AS kind
LIMIT 30
`,
{ symId },
),
executeParameterized(
repo.id,
`
{ symId },
),
executeParameterized(
repo.id,
`
MATCH (f:File)-[rel:CodeRelation]->(n)
WHERE n.id = $symId AND rel.type = 'DEFINES'
MATCH (caller)-[r:CodeRelation]->(f)
@ -1779,11 +1779,11 @@ export class LocalBackend {
RETURN r.type AS relType, caller.id AS uid, caller.name AS name, caller.filePath AS filePath, labels(caller)[0] AS kind
LIMIT 30
`,
{ symId },
),
executeParameterized(
repo.id,
`
{ symId },
),
executeParameterized(
repo.id,
`
MATCH (p:\`Property\`)
WHERE p.declaredType = $name
OR p.declaredType STARTS WITH $genericPrefix
@ -1793,15 +1793,15 @@ export class LocalBackend {
RETURN r.type AS relType, caller.id AS uid, caller.name AS name, caller.filePath AS filePath, labels(caller)[0] AS kind
LIMIT 30
`,
{
name: sym.name,
genericPrefix: `${sym.name}<`,
genericArg: `<${sym.name}>`,
},
),
executeParameterized(
repo.id,
`
{
name: sym.name,
genericPrefix: `${sym.name}<`,
genericArg: `<${sym.name}>`,
},
),
executeParameterized(
repo.id,
`
MATCH (p:\`Property\`)
WHERE p.declaredType = $name
OR p.declaredType STARTS WITH $genericPrefix
@ -1810,13 +1810,13 @@ export class LocalBackend {
p.declaredType AS declaredType
LIMIT 30
`,
{
name: sym.name,
genericPrefix: `${sym.name}<`,
genericArg: `<${sym.name}>`,
},
),
]);
{
name: sym.name,
genericPrefix: `${sym.name}<`,
genericArg: `<${sym.name}>`,
},
),
]);
typedPropertyRows = typedProperties;
// Deduplicate by (relType, uid) — a caller can have multiple relation

View file

@ -11,6 +11,9 @@ import type { GraphRelationship } from 'gitnexus-shared';
const LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES: Readonly<Record<string, ReadonlySet<string>>> = {
csharp: new Set([
'emits the using-import edge App/Program.cs -> Models/User.cs through the scope-resolution path',
// Generic type-argument USES edges are emitted by the registry-primary
// resolver only; the legacy DAG path does not synthesize these references.
'emits USES edges for generic type arguments',
]),
go: new Set([
// The legacy DAG path does not resolve method calls when the method is

View file

@ -453,4 +453,16 @@ describe('emitCsharpScopeCaptures — references', () => {
expect(names).toContain('USER_INFO');
expect(names).not.toContain('string');
});
it('captures call-site generic type arguments as type references', () => {
const matches = emitCsharpScopeCaptures(
'class A { void M(IRepo repo) { repo.Get<USER_INFO>(); } }',
'test.cs',
);
const names = matches
.filter((m) => '@reference.type' in m)
.map((m) => m['@reference.name'].text);
expect(names).toContain('USER_INFO');
});
});