mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-25 05:53:44 +00:00
* fix(csharp): bind nullable field types for receiver resolution (#3498) A C# field written `User? repo;` got no type binding. Its type is a `nullable_type` node, and the field_declaration rules only matched identifier, generic_name and qualified_name types. Parameters, properties, method returns and foreach variables already have a nullable_type rule, so fields were the odd one out: the class scope had no binding for `repo`, and `this.repo.Save()` was dropped as receiver-unresolved. Add the missing field rule. interpretCsharpTypeBinding already strips the `?`, so the field binds `User`, as the property spelling does. Shape arm: csharp `decoratedFieldType` moves VISIBLE-GAP -> RESOLVES, with the edge on `User.Save`. No corpus fixture declares a nullable field, so the count arm and every other cell are unchanged, and the corpus edge (2838) and drop (192) sets match main exactly. Scope-capture fingerprints are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(csharp): preserve nullable field ownership * fix(csharp): refuse unowned nullable types * test(csharp): guard nullable-member fixture cleanup (#3664) Address nexus-check review on resolvers/csharp.test.ts:1471: skip the temp-root removal when mkdtempSync never ran, so a setup failure is not masked by a secondary rmSync(undefined) TypeError. Matches the guard in csharp-nullable-field-review.test.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(bench): scope nullable wrapper claim to what the code does (#3664) The ownership-controls note said the nullable-field marker preserves custom generic wrappers. Its only control, Custom.List<User>?, passes through the qualified-type refusal instead. State that unqualified wrappers keep the wrapper type (no native control yet) and that qualified spellings, custom wrappers included, stay unbound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: preserve C# integration notes * fix(csharp): preserve generic argument qualifiers Ignore comments when checking outer nullable-field qualification. Refs #3664 --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
87 lines
3.1 KiB
TypeScript
87 lines
3.1 KiB
TypeScript
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import {
|
|
getRelationships,
|
|
runPipelineFromRepo,
|
|
writeFixtureRepo,
|
|
type PipelineResult,
|
|
} from './resolvers/helpers.js';
|
|
|
|
describe('nullable generic field receiver ownership', () => {
|
|
let root: string | undefined;
|
|
let result: PipelineResult;
|
|
|
|
beforeAll(async () => {
|
|
root = fs.mkdtempSync(path.join(os.tmpdir(), 'gn-csharp-generic-field-'));
|
|
writeFixtureRepo(root, {
|
|
'Wrapper.cs': 'class List<T> { public void Save() {} }',
|
|
'Outer.cs': 'class Outer<T> { public void Save() {} }',
|
|
'Inner.cs': 'class Inner<T> { public void Save() {} }',
|
|
'User.cs': `class User { public void Save() {} }
|
|
namespace Models { class User { public void Save() {} } }`,
|
|
'Host.cs': `class Host {
|
|
List<User>? plain;
|
|
List<Models.User>? dotted;
|
|
Custom.List<User>? qualified;
|
|
Outer<User>.Inner<Models.User>? nestedOuter;
|
|
List</* > */Models.User>? commented;
|
|
List<// >
|
|
Models.User>? lineCommented;
|
|
Outer</* < */User>.Inner<User>? commentedOuter;
|
|
Outer<User>/* < */.Inner<User>? commentedSuffix;
|
|
void PlainCall() { this.plain.Save(); }
|
|
void DottedCall() { this.dotted.Save(); }
|
|
void QualifiedCall() { this.qualified.Save(); }
|
|
void NestedOuterCall() { this.nestedOuter.Save(); }
|
|
void CommentedCall() { this.commented.Save(); }
|
|
void LineCommentedCall() { this.lineCommented.Save(); }
|
|
void CommentedOuterCall() { this.commentedOuter.Save(); }
|
|
void CommentedSuffixCall() { this.commentedSuffix.Save(); }
|
|
}`,
|
|
});
|
|
result = await runPipelineFromRepo(root, () => {}, {});
|
|
}, 240000);
|
|
|
|
afterAll(() => {
|
|
if (root) fs.rmSync(root, { recursive: true, force: true });
|
|
});
|
|
|
|
const saves = (source: string) =>
|
|
getRelationships(result, 'CALLS')
|
|
.filter((edge) => edge.source === source && edge.target === 'Save')
|
|
.map((edge) => edge.targetFilePath);
|
|
|
|
it('keeps an unqualified wrapper as the receiver', () => {
|
|
expect(saves('PlainCall')).toEqual(['Wrapper.cs']);
|
|
});
|
|
|
|
it('keeps qualification inside the argument without selecting User.Save', () => {
|
|
expect(saves('DottedCall')).toEqual(['Wrapper.cs']);
|
|
});
|
|
|
|
it('still declines a qualified outer wrapper', () => {
|
|
expect(saves('QualifiedCall')).toEqual([]);
|
|
});
|
|
|
|
it('still declines qualification after a generic outer type', () => {
|
|
expect(saves('NestedOuterCall')).toEqual([]);
|
|
});
|
|
|
|
it('ignores a block comment when classifying an argument-qualified wrapper', () => {
|
|
expect(saves('CommentedCall')).toEqual(['Wrapper.cs']);
|
|
});
|
|
|
|
it('ignores a line comment when classifying an argument-qualified wrapper', () => {
|
|
expect(saves('LineCommentedCall')).toEqual(['Wrapper.cs']);
|
|
});
|
|
|
|
it('does not borrow standalone Outer or Inner through an argument comment', () => {
|
|
expect(saves('CommentedOuterCall')).toEqual([]);
|
|
});
|
|
|
|
it('does not borrow standalone Outer or Inner through a suffix comment', () => {
|
|
expect(saves('CommentedSuffixCall')).toEqual([]);
|
|
});
|
|
});
|