Skip to content

fix - #51

Merged
lofcz merged 1 commit into
nextfrom
feat-polymorphism
Aug 24, 2026
Merged

fix#51
lofcz merged 1 commit into
nextfrom
feat-polymorphism

Conversation

@lofcz

@lofcz lofcz commented Aug 24, 2026

Copy link
Copy Markdown
Owner

No description provided.

Copilot AI lite review requested due to automatic review settings August 24, 2026 19:49
@lofcz
lofcz merged commit bf529ab into next Aug 24, 2026
7 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The generator changes are guarded, well-covered by new tests, and the remaining feedback is limited to minor nullable-warning cleanups in the new test file.

Pull request overview

This PR fixes source-generator collection cloning for issue #50 by switching from CollectionKind-based assumptions to verifying the actual constructor/API surface of the concrete type the generator will instantiate, preventing invalid capacity/copy-ctor/CollectionsMarshal fast paths and falling back to the runtime cloner when codegen can’t be proven correct.

Changes:

  • Add regression tests for issue #50 covering Collection<T>, BindingList<T>, derived collection/dictionary types, getter-only collections, stack ordering, and a non-verifiable custom enumerable fallback.
  • Introduce CollectionCapabilities and generation guards (CanGenerateCollectionHelper / CanGenerateDictionaryHelper) so helpers are only emitted when construction + population are guaranteed to compile and behave correctly.
  • Tighten fast paths in CollectionHelperGenerator to require verified capacity/copy constructors and exact List<T> before using CollectionsMarshal.
File summaries
File Description
src/FastCloner.Tests/SourceGeneratorIssue50Tests.cs Adds comprehensive regression coverage for issue #50 scenarios and edge cases.
src/FastCloner.SourceGenerator/TypeAnalyzer.cs Adds CollectionCapabilities, helper-generation viability checks, and fixes immutable set kind ordering.
src/FastCloner.SourceGenerator/NonPublicAccessorEmitter.cs Adds null-forgiving operators to suppress nullability warnings in generated accessor code.
src/FastCloner.SourceGenerator/NestedTypeCollector.cs Gates helper registration on verified collection/dictionary capabilities and propagates capability flags into MemberModel.
src/FastCloner.SourceGenerator/MemberModel.cs Extends MemberModel to carry verified concrete collection capability flags.
src/FastCloner.SourceGenerator/MemberCollector.cs Reworks getter-only collection “populatable” detection to be API-based instead of a whitelist.
src/FastCloner.SourceGenerator/MemberCloneGenerator.cs Adds null-forgiving on Cloner<T>.Clone(...) argument (safe due to null-guard in Clone).
src/FastCloner.SourceGenerator/CollectionHelperGenerator.cs Requires verified ctor surfaces for capacity/copy ctor fast paths and restricts CollectionsMarshal usage to exact List<T>.
Review details

Suppressed comments (3)

src/FastCloner.Tests/SourceGeneratorIssue50Tests.cs:237

  • clone.Items is nullable (DerivedIntDictionary?), but it’s indexed/assigned without ! after only runtime assertions, which will trigger nullable warnings.
        await Assert.That(clone.Items["b"]).IsEqualTo(2);

        clone.Items["c"] = 3;

src/FastCloner.Tests/SourceGeneratorIssue50Tests.cs:274

  • clone.Numbers is nullable (DerivedIntHashSet?), but it’s used without ! after only runtime assertions, which will trigger nullable warnings.
        clone.Numbers.Add(4);

src/FastCloner.Tests/SourceGeneratorIssue50Tests.cs:256

  • clone.Items is nullable (DerivedObservableCollection?), but it’s indexed without ! after only runtime assertions, which will trigger nullable warnings.
        await Assert.That(clone.Items[0]).IsNotSameReferenceAs(original.Items![0]);
        await Assert.That(clone.Items[0].Name).IsEqualTo("First");
  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +217 to +218
await Assert.That(clone.Items["a"]).IsNotSameReferenceAs(original.Items!["a"]);
await Assert.That(clone.Items["b"].Name).IsEqualTo("B");
@github-actions

Copy link
Copy Markdown

Deep Clone Benchmarks

  • OS: ubuntu-latest
  • Generated (UTC): 2026-08-24 19:58:40

Current FastCloner vs DeepCloner

Benchmark DeepCloner FastCloner Delta Time DC Alloc FC Alloc Delta Alloc
SmallObject 100.00 ns 68.76 ns -31% faster 184 B 48 B -74% less
FileSpec 487.82 ns 305.07 ns -37% faster 920 B 416 B -55% less
StringArray_1000 517.10 ns 513.52 ns ~same 8,160 B 8,024 B ~same
SmallObjectWithCollections 661.90 ns 387.21 ns -42% faster 1,096 B 576 B -47% less
DynamicWithDictionary 1,364.77 ns 1,031.48 ns -24% faster 2,712 B 1,600 B -41% less
MediumNestedObject 1,747.80 ns 1,112.42 ns -36% faster 3,416 B 1,616 B -53% less
DynamicWithNestedObject 1,872.89 ns 1,250.00 ns -33% faster 3,560 B 1,776 B -50% less
DynamicWithArray 5,631.93 ns 4,365.43 ns -22% faster 8,800 B 2,744 B -69% less
LargeEventDocument_10MB 66,494.96 ns 37,605.24 ns -43% faster 129,792 B 49,824 B -62% less
ObjectList_100 166,277.69 ns 109,639.14 ns -34% faster 318,888 B 149,816 B -53% less
ObjectDictionary_50 823,703.29 ns 178,222.54 ns -78% faster 549,665 B 218,768 B -60% less
LargeLogBatch_10MB 5,653,225.52 ns 3,545,243.32 ns -37% faster 3,564,655 B 2,649,126 B -26% less

FastCloner vs latest next baseline

  • Baseline generated (UTC): 2026-08-24 19:57:52
  • Regression thresholds: time > 5%, alloc > 5%
Status Benchmark Delta Time Delta Alloc
🟢 DynamicWithArray -7% faster ~same
DynamicWithDictionary -3% faster ~same
DynamicWithNestedObject ~same ~same
🔴 FileSpec +6% slower ~same
LargeEventDocument_10MB -2% faster ~same
🔴 LargeLogBatch_10MB +7% slower ~same
MediumNestedObject -2% faster ~same
ObjectDictionary_50 +4% slower ~same
ObjectList_100 +4% slower ~same
🟢 SmallObject -15% faster ~same
🔴 SmallObjectWithCollections +6% slower ~same
StringArray_1000 ~same ~same

Regressions

  • FileSpec: time +6% slower, alloc ~same
  • LargeLogBatch_10MB: time +7% slower, alloc ~same
  • SmallObjectWithCollections: time +6% slower, alloc ~same

Improvements

  • DynamicWithArray: time -7% faster, alloc ~same
  • SmallObject: time -15% faster, alloc ~same

Mixed changes

  • none

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants