mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-07 08:26:11 +00:00
* fix(csharp): bind qualified constructor names, capture : base/: this, fix generic strip Mirrors the Java #1928 parsing-layer fixes for the C# scope-resolution path — the same three defect classes exist verbatim in C#: - Qualified / qualified-generic / alias-qualified constructor calls (`new Ns.Foo()`, `new A.B.Foo()`, `new Ns.Box<int>()`, `new MyAlias::Foo()`, `new global::Foo()`) bound only `@reference.call.constructor.qualified` with no `@reference.name`, so the central extractor fell back to the whole-expression anchor and the reference name became the raw `new Ns.Foo()` text (never resolved). Derive the simple-name tail via the existing `terminalTypeNameNode` helper (handles qualified_name, generic tail, and alias_qualified_name), and add a query arm for the top-level `alias_qualified_name` shape that was not captured at all. - `: base(...)` / `: this(...)` explicit constructor initializers, modeled by tree-sitter as `constructor_initializer` and never matched by the scope query, dropped the chained-constructor CALLS edges. Synthesize them: `this` → enclosing type name; `base` → the base type's bare name (first base-list entry, which C# requires to be the base class). Arity attached for overload disambiguation. - `interpretCsharpTypeBinding`'s qualifier strip used `lastIndexOf('.')` over the whole string, cutting inside a qualified generic type ARGUMENT (`Dictionary<string, Ns.User>` → `User>`). Make stripQualifier generic-aware: reduce only the segment before the first `<`, re-attaching the generic suffix — multi-arg generics stay intact so the `.Values`/`.Keys` collection-accessor unwrap keeps working. Tests: capture-level unit tests for every constructor shape (incl. alias-qualified, double-match guard) and `: base`/`: this` (incl. struct/record/mixed-base); interpretCsharpTypeBinding unit tests (the corruption case + nullable/nested/ unknown-generic edges); end-to-end resolver tests with new fixtures. The csharp-captures golden was regenerated — drift is purely additive (only the new fixtures; zero existing-fixture digests changed). Co-authored-by: Cursor <cursoragent@cursor.com> * fix(csharp): enhance constructor resolution and namespace qualification - Implemented qualified constructor name binding to resolve collisions between types in different namespaces. - Added support for `: base(...)` and `: this(...)` constructor initializers to ensure correct edge emission in the scope resolution. - Improved generic argument stripping to prevent incorrect parsing of qualified types. - Introduced tests for new features, including handling of interface-only base classes and qualified constructor calls. This update addresses issues related to constructor resolution and namespace qualification, ensuring accurate type references in C# code. Tests have been added to validate these changes. * fix(csharp): implement namespace prefix tagging for file-level type definitions - Updated the C# ingestion process to tag file-level type definitions with their enclosing namespace path using a new `namespacePrefix` field, without altering the `qualifiedName`. - Enhanced the scope resolver to utilize the `namespacePrefix` for resolving same-tail collisions in constructor calls, improving accuracy in type resolution. - Added unit tests to validate the new functionality, ensuring that namespace prefixes are correctly applied to both block-scoped and file-scoped types, while leaving namespace-free types untagged. This change addresses issues related to namespace qualification and constructor resolution in C# code, facilitating better handling of type references. * refactor(scope-resolution): share isOverloadableCallable via util Extract the ctor/function/method overload predicate into callable-labels.ts so graph-bridge registration and lookup stay aligned without duplicated private copies in ids.ts and node-lookup.ts. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
82 lines
3.4 KiB
TypeScript
82 lines
3.4 KiB
TypeScript
/**
|
|
* Coverage for `interpretCsharpTypeBinding` type normalization, focused on the
|
|
* F41 analog (#1928): the qualifier strip must not reach into generic type
|
|
* ARGUMENTS. `Dictionary<string, Ns.User>` was corrupted into `User>` by an
|
|
* unguarded `lastIndexOf('.')`. Multi-arg generics must stay intact so the
|
|
* collection-accessor (`.Values`/`.Keys`) unwrap keeps working.
|
|
*/
|
|
import { describe, it, expect } from 'vitest';
|
|
import type { Capture, CaptureMatch } from 'gitnexus-shared';
|
|
import { interpretCsharpTypeBinding } from '../../../../src/core/ingestion/languages/csharp/interpret.js';
|
|
|
|
const ZERO = { startLine: 0, startCol: 0, endLine: 0, endCol: 0 } as const;
|
|
const cap = (name: string, text: string): Capture => ({ name, text, range: ZERO });
|
|
|
|
function raw(typeText: string): string | undefined {
|
|
const m: CaptureMatch = {
|
|
'@type-binding.name': cap('@type-binding.name', 'x'),
|
|
'@type-binding.type': cap('@type-binding.type', typeText),
|
|
'@type-binding.annotation': cap('@type-binding.annotation', typeText),
|
|
};
|
|
return interpretCsharpTypeBinding(m)?.rawTypeName;
|
|
}
|
|
|
|
describe('interpretCsharpTypeBinding — type normalization (F41 analog #1928)', () => {
|
|
it('does not corrupt a qualified generic TYPE ARGUMENT (the bug)', () => {
|
|
// Was `User>` before the fix.
|
|
expect(raw('Dictionary<string, Ns.User>')).toBe('Dictionary<string, Ns.User>');
|
|
});
|
|
|
|
it('leaves an unqualified multi-arg generic intact (collection-accessor unwrap)', () => {
|
|
expect(raw('Dictionary<string, Widget>')).toBe('Dictionary<string, Widget>');
|
|
});
|
|
|
|
it('strips the OUTER qualifier of a generic while keeping the type args', () => {
|
|
expect(raw('Ns.Dictionary<string, User>')).toBe('Dictionary<string, User>');
|
|
});
|
|
|
|
it('unwraps a single-arg known container to its (qualified) element type', () => {
|
|
expect(raw('List<Ns.User>')).toBe('User');
|
|
expect(raw('List<User>')).toBe('User');
|
|
expect(raw('Task<User>')).toBe('User');
|
|
});
|
|
|
|
it('strips a plain qualifier', () => {
|
|
expect(raw('Ns.User')).toBe('User');
|
|
expect(raw('A.B.User')).toBe('User');
|
|
});
|
|
|
|
it('strips a nullable suffix', () => {
|
|
expect(raw('User?')).toBe('User');
|
|
});
|
|
|
|
it('unwraps a nullable single-arg generic (`List<User>?` → `User`)', () => {
|
|
expect(raw('List<User>?')).toBe('User');
|
|
});
|
|
|
|
it('does not corrupt a nested generic — keeps it intact (no `>>` artifact)', () => {
|
|
expect(raw('List<Dictionary<string, User>>')).toBe('List<Dictionary<string, User>>');
|
|
});
|
|
|
|
it('strips the outer qualifier of an unrecognized generic, keeping its args', () => {
|
|
// Accepted limitation: unknown generics are not erased to the bare base —
|
|
// only the OUTER qualifier is removed; the generic suffix is preserved.
|
|
expect(raw('Ns.Box<User>')).toBe('Box<User>');
|
|
});
|
|
|
|
it('passes through a plain simple type', () => {
|
|
expect(raw('User')).toBe('User');
|
|
});
|
|
|
|
it('preserves a collection-accessor suffix (`data.Values`)', () => {
|
|
expect(raw('data.Values')).toBe('data.Values');
|
|
});
|
|
|
|
it('strips nested types through a generic outer (`Ns.Outer<int>.Inner` → `Inner`)', () => {
|
|
// Prior generic-aware strip sliced at the first `<` and regressed to
|
|
// `Outer<int>.Inner` (unresolvable). Last `.` at bracket depth 0 fixes both
|
|
// this shape and the F41 `Dictionary<string, Ns.User>` case (#2046 P3).
|
|
expect(raw('Ns.Outer<int>.Inner')).toBe('Inner');
|
|
expect(raw('Outer.Inner')).toBe('Inner');
|
|
});
|
|
});
|