Skip to content

#249 Circular reference / reference handling - #307

Merged
sanelli merged 22 commits into
mainfrom
249-circular-reference-reference-handling
Aug 8, 2026
Merged

#249 Circular reference / reference handling#307
sanelli merged 22 commits into
mainfrom
249-circular-reference-reference-handling

Conversation

@sanelli

@sanelli sanelli commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add circular-reference / reference handling via ReferenceReusing, MaxRuntimeDepth, and MaxCompileTimeDepth (short, negative = Undefined), with MappaReferenceManager on MappaContext.
  • Generator emits reuse/depth codegen when MappaContext is present, warns otherwise (MP00074/MP00075), enforces compile-time depth (MP00076) and type-pair cycle detection (MP00077), and rejects reference handling on IQueryable projections.
  • Includes samples, AOT runner, benchmarks, SVG chart updates, documentation, and coverage-restoring unit tests.

Test plan

  • .\Scripts\RunTestsAndReportCoverage.ps1 — 2844 tests passed; coverage ≥ baseline (line 98.3%, branch 90.4%, method 100%; Generator branch 90.3%)
  • CI PR checks (build, version increase, tests)
  • Spot-check samples: ReferenceReusingCycleMapper / MaxRuntimeDepthMapper with MappaContext
  • Confirm projection + ReferenceReusing still fails with MP00057

Closes #249

sanelli and others added 22 commits August 3, 2026 19:37
Introduce MappaReferenceManager, MappaException, and MappaSettings for ReferenceReusing, MaxRuntimeDepth, and MaxCompileTimeDepth. Expose a private ReferenceManager on MappaContext and cover it with unit and UnsafeAccessor tests. Bump to 10.2.0-alpha.50.

Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce warnings for missing MappaContext on root and nested maps, plus errors for MaxCompileTimeDepth overflow and compile-time mapping cycles. Resources and factory helpers are wired; call sites remain for later steps.

Co-authored-by: Cursor <cursoragent@cursor.com>
…nfig.

Expose ReferenceReusing, MaxRuntimeDepth, and MaxCompileTimeDepth on user settings with negative depth treated as Undefined. Read the matching mappa.* editorconfig keys and assert fully unset depth defaults.

Co-authored-by: Cursor <cursoragent@cursor.com>
Track CurrentDepth on the root algorithm context and wrap both GetStrategy entry points so nested strategy discovery stops with MP00076 when the effective compile-time depth limit is exceeded.

Co-authored-by: Cursor <cursoragent@cursor.com>
Track in-flight target/source type pairs on the root algorithm context and stop strategy discovery with MP00077 when the same pair re-enters before the outer call completes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add reference-manager accessor scaffolding, root MaxDepth initialization, and nested IncreaseDepth using wraps for reference-type maps. Also avoid MP00077 false positives on intentional GetStrategy re-entry (nullable unwrap and polymorphism MapSourceType).

Co-authored-by: Cursor <cursoragent@cursor.com>
Wrap root and nested reference-type maps with TryGetReference/AddReferencePair, register pairs early after construction for cycles, and warn with MP00074 when reference handling is requested without MappaContext.

Co-authored-by: Cursor <cursoragent@cursor.com>
Warn with MP00075 when an invoked map method lacks MappaContext while reference handling is requested, reject IQueryable projections that enable reference handling, and share IsReferenceHandlingRequested/Active helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>
Cover MP00074–MP00076 and projection rejection with full syntax walks, and require generated-code validation after diagnostic assertions in the workflow rule.

Co-authored-by: Cursor <cursoragent@cursor.com>
Cover method/class/editorconfig precedence for ReferenceReusing, MaxRuntimeDepth, and MaxCompileTimeDepth, including negative depth inherit and global-options parse fallbacks.

Co-authored-by: Cursor <cursoragent@cursor.com>
Cover default and boundary limits, disabled depth, sibling restore, settings sources, and collection/tuple/nullable nesting with full syntax walks.

Co-authored-by: Cursor <cursoragent@cursor.com>
Cover mutual nesting cycles, explicit map-method breakouts, sibling and distinct-pair false positives, and ReferenceReusing still reporting compile-time cycles.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Wrap nullable reference map-method strategies in NullableStrategy so null cycle edges short-circuit before nested Map calls.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Add unit and integration tests for ReferenceHandlingCodeGenerator, MappaReferenceManager, MappaBuilderContext, TypeMapIdentifierWithMapMethodAlgorithm early returns, ProjectionCapabilityAnalyzer, and ProjectionMapMethodEligibilityValidator.

Co-authored-by: Cursor <cursoragent@cursor.com>
Publish the post-generator package bump, add ReferenceReusing/MaxRuntimeDepth samples with tests and AOT runners, and require a post-generator-tests coverage checkpoint in the development workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
Compare AutoMapper, Mapperly, Mapster, and Mappa on closed cycles and shared-reference DAGs. Leave the SVG chart subset unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add enum/reference series with capped time-percentage axis, proportional overflow stubs, rounded break waves, inside-bar labels, and exclude string/enum from memory charts.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add unlabeled minor Y-axis guides on percentage charts and use comma thousands separators for values of 1000 or more.

Co-authored-by: Cursor <cursoragent@cursor.com>
…gnostics.

Update tutorial, attributes, algorithm, features, error codes, and READMEs for ReferenceReusing, MaxRuntimeDepth, MaxCompileTimeDepth, and MP00074-MP00077.

Co-authored-by: Cursor <cursoragent@cursor.com>
…rage gate.

Add unit tests for empty inner-code depth wraps, additional container wrappers, and mixed type eligibility so overall and Generator branch coverage meet the issue baseline.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sanelli sanelli linked an issue Aug 8, 2026 that may be closed by this pull request
9 tasks
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

Code coverage

Compared against the latest published coverage history from the code coverage gist.

Metric Baseline PR Δ Trend
Line 98.3% 98.3% 0 =
Branch 90.3% 90.4% +0.1
Method 100% 100% 0 =

No coverage metric dropped by more than 1 percentage point(s) versus baseline.

Benchmarks

AutoMapper is the BenchmarkDotNet ratio baseline. Absolute values are mean time in nanoseconds and allocated bytes. Ratio columns are competitor / Mappa as a percentage (n/a when either side is missing or Mappa is zero).

Benchmark Automapper Mean (ns) Automapper Allocated (B) Mapster Mean (ns) Mapster Allocated (B) Mapperly Mean (ns) Mapperly Allocated (B) Mappa Mean (ns) Mappa Allocated (B) Automapper/Mappa Time Automapper/Mappa Alloc Mapperly/Mappa Time Mapperly/Mappa Alloc Mapster/Mappa Time Mapster/Mappa Alloc
ArrayToListBenchmark 328720 592056.32 208050 648058.88 15280 128051.2 15590 128051.2 2108.5% 462.4% 98% 100% 1334.5% 506.1%
DictionaryBenchmark 342850 615014.4 240190 742277.12 27840 151019.52 35110 222218.24 976.5% 276.8% 79.3% 68% 684.1% 334%
EnumToStringBenchmark 33.806 24 10.644 24 1.013 0 0.899 0 3760.4% n/a 112.7% n/a 1184% n/a
FastListToArrayBenchmark 336380 592025.6 206210 648028.16 15130 128020.48 15400 128020.48 2184.3% 462.4% 98.2% 100% 1339% 506.2%
IQueryableProjectionBenchmark 458000 67973.12 433600 67205.12 443300 69150.72 442300 69150.72 103.5% 98.3% 100.2% 100% 98% 97.2%
ListToArrayBenchmark 332310 592025.6 215170 648028.16 15660 128020.48 16800 128020.48 1978% 462.4% 93.2% 100% 1280.8% 506.2%
NestedDtoBenchmark 375690 742768.64 256260 789555.2 28090 149401.6 28400 157900.8 1322.9% 470.4% 98.9% 94.6% 902.3% 500%
ReferenceReusingSharedDagBenchmark 162.44 344 133.42 400 175.82 800 71.68 416 226.6% 82.7% 245.3% 192.3% 186.1% 96.2%
StringToEnumBenchmark 29.996 0 57.846 216 3.037 0 3.019 0 993.6% n/a 100.6% n/a 1916.1% n/a

@sanelli
sanelli merged commit 1b7b77b into main Aug 8, 2026
1 check passed
@sanelli
sanelli deleted the 249-circular-reference-reference-handling branch August 8, 2026 11:27
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.

Circular reference / reference handling

1 participant