Skip to content

Refactor source generation and improve generated mapping support - #49

Merged
ycherkes merged 22 commits into
mainfrom
refactor/roslyn-generator-incrementality
Sep 1, 2026
Merged

Refactor source generation and improve generated mapping support#49
ycherkes merged 22 commits into
mainfrom
refactor/roslyn-generator-incrementality

Conversation

@ycherkes

@ycherkes ycherkes commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Refactor source generation and improve generated mapping support

Summary

This PR refactors AlephMapper's Roslyn generator around mapper-scoped incremental outputs, strengthens generated-code diagnostics and nullable handling, and expands support for complex expression and generic mapping scenarios. It also updates generated attributes, documentation, samples, and test coverage.

Breaking change

[Expressive] has been renamed to [Projectable] with no compatibility attribute. Replace every [Expressive] use with [Projectable].

Generated mappings now include the assembly revision in their GeneratedCode version metadata. Local builds emit 0.7.0.0; CI-packaged analyzers emit 0.7.0.<run-number>. Consumers that inspect this attribute using an exact version string must account for the revision component.

Highlights

Incremental generator architecture

  • Replaces compiler-object-heavy cached models with value-oriented, mapper-scoped generation results.
  • Narrows syntax candidate discovery to relevant mapper methods and attributes.
  • Centralizes mapper generation, mapping lookup, diagnostics, and generated-file rendering.
  • Separates source-only incremental output from compilation-dependent diagnostic rebinding, so unrelated compilation changes do not feed into AddSource.
  • Projects generated source data into a dedicated value with source-only equality; diagnostic-only changes (including source-location shifts) no longer invalidate AddSource.
  • Removes obsolete lookup and diagnostic paths while preserving packaging behavior.

Generated API and diagnostics

  • Embeds generated configuration types as internal implementation details rather than exposing them through consumer APIs.
  • Marks generated sources explicitly and improves attribute defaults and nullable contracts to eliminate generator and generated-attribute warnings.
  • Adds analyzer release tracking and improves diagnostic descriptions and metadata.
  • Introduces AM0018: [Adapt] on a generic mapping method is skipped because the generic mapper already provides the polymorphic behavior.

Mapping and expression generation

  • Generates generic Projectable expression companions and Updatable overloads with the original type parameters and where constraints.
  • Distinguishes generic method arity during generated-member conflict detection.
  • Preserves nullable type information when rewriting null-conditional expressions, avoiding false nullable warnings in generated expressions.
  • Preserves source nullable context around each generated member, including nullable-disabled sources, and validates the generated nullable-disabled fixture for CS86xx warnings.
  • Avoids conflicting file-level nullable directives by emitting the appropriate #nullable state only around each generated member.
  • Emits null-forgiving suppression only when nullable warnings are enabled; nullable-disabled output retains typed null casts without redundant ! operators.
  • Centralizes nullable behavior in one effective policy per source mapping method, resolving annotation and warning inheritance independently and reusing it for type rendering, lowering, adaptation, updates, and member directives.
  • Round-trips adaptation diagnostic source locations with their original file and line spans.
  • Preserves diagnostic descriptor metadata and formatted messages when diagnostics cross the incremental-generation boundary.
  • Rebinds reconstructed diagnostics to their original syntax trees at emission time, preserving source-based controls such as #pragma warning disable.
  • Corrects the AM0004 crash-report help link.
  • Allows ordinary external helper methods with expression bodies to be inlined without applying mapper candidate-discovery restrictions.
  • Improves null-conditional, conditional-expression, switch-expression, extension-method, and invocation rewriting.
  • Improves conditional and switch formatting while preserving single-line versus multi-line source intent.
  • Emits collision-safe global::-qualified type references in generated declarations, expressions, object creation, casts, and constraints while retaining C# keyword types and generic parameters, and omits the redundant System.CodeDom.Compiler using.

Tests, samples, and documentation

  • Expands incremental-generator, diagnostic, generic mapping, null-handling, conditional-formatting, adaptation, and inlining coverage.
  • Clarifies incremental tracking coverage: unchanged values are tested for stable generation results, without claiming that the test proves a cache hit.
  • Adds coverage proving that a diagnostic-location-only change leaves generated source text and the source-output projection unchanged.
  • Refreshes generated baselines and integration fixtures.
  • Updates Adapt documentation, README content, and sample mappers.
  • Bumps the package version to 0.7.0.

CI and packaging

  • Adds a GitHub Actions workflow that restores, builds, tests, and packs the solution on pull requests, pushes to main, and manual dispatch.
  • Pins the .NET SDK, caches NuGet packages, and uploads TRX test reports alongside the generated package artifact.
  • Restores the generated package into a minimal consumer project and compiles a [Projectable] mapper through the packaged analyzer.
  • Stops automatically packing during ordinary builds; packaging is now an explicit release/CI step.
  • Uses the GitHub Actions run number as the assembly-version revision; NuGet package versioning remains independently stable.

Generator reliability

  • Compiles generated syntax trees as part of the source-generator baseline tests, while allowing fixture-only dependency errors to remain isolated.
  • Re-throws unexpected generator exceptions in DEBUG builds instead of suppressing them after diagnostic creation.
  • Derives generated-code version metadata from assembly metadata, including the build revision, avoiding a second hard-coded package version.
  • Cleans up nullable contracts in samples and test fixtures, and preserves nullability in generated signatures and expressions without global warning suppressions.

Validation

  • dotnet build AlephMapper.slnx --configuration Release --no-restore — succeeded with 0 warnings and 0 errors
  • dotnet run --project tests/AlephMapper.Tests/AlephMapper.Tests.csproj -c Release --no-restore — 58 passed
  • dotnet test AlephMapper.slnx --configuration Release --no-build --no-restore -- --report-trx --results-directory artifacts/test-results — 95 passed
  • dotnet pack source/AlephMapper.csproj --configuration Release --no-build --no-restore --output artifacts/nuget — produced AlephMapper.0.7.0.nupkg
  • dotnet test AlephMapper.slnx --no-restore — 96 passed

  - Hide generated configuration types from consumer APIs
  - Mark generated sources explicitly
  - Filter unrelated syntax candidates
  - Remove compiler objects from cached models
  - Scope generation to individual mappers
  - Preserve existing mapping behavior and packaging
  - One-line source ternaries stay on one line.
  - Multi-line ternaries use aligned ? / : indentation.
  - Switch-expression lowering preserves whether the original switch was one or multi-line.
  - Multi-line rewritten branches still expand cleanly.
  - Added direct and switch formatting coverage.

  Verified: dotnet test AlephMapper.slnx --no-restore --verbosity quiet — 86 passed.
  - source/AlephMapper.csproj rebuilds with 0 warnings.
  - Generated AdaptAttribute.Name now defaults to string.Empty, eliminating CS8618 in consumer-generated attribute code.
  - Fixed nullable contracts in the inliner and property collector.
  - Added analyzer release tracking and corrected diagnostic-message metadata.
  - Fixed analyzer package dependency metadata.

  Verified:

  - dotnet build source\AlephMapper.csproj --no-restore -t:Rebuild — 0 warnings
  - dotnet test AlephMapper.slnx --no-restore --verbosity quiet — 86 passed

  Remaining warnings are nullable-flow warnings from generated mapping bodies in integration/experiment fixtures, not
  generated attributes.
@ycherkes ycherkes changed the title Refactor/roslyn generator incrementality Refactor source generation and improve generated mapping support Sep 1, 2026
  - Preserved formatted diagnostic messages during round-trip.
  - Rebound diagnostics to original SyntaxTree instances at output time, restoring #pragma warning disable AM0018
    suppression.

  - Corrected the crash-report link from IMP005 to AM0004.
  - Added regression coverage for message formatting, syntax-tree location, pragma suppression, and crash-link ID.
  - source/AlephSourceGenerator.cs
      - Split mapper output into source-only and diagnostics branches.
      - Removed misleading Candidates tracking name.
      - CompilationProvider is now used only by diagnostics.

  - source/Generation/MapperSourceOutput.cs
      - Replaced mixed Emit with EmitSource and EmitDiagnostics.

  - tests/AlephMapper.Tests/SourceGeneratorTests.cs
      - Updated incremental tracking coverage to assert the real source-output branch stays Unchanged after an unrelated
        compilation edit. This is not described as a cache hit.
@ycherkes
ycherkes merged commit 00de16d into main Sep 1, 2026
1 check passed
@ycherkes
ycherkes deleted the refactor/roslyn-generator-incrementality branch September 1, 2026 16:46
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.

1 participant