Skip to content

Add source generator#37

Open
buvinghausen wants to merge 8 commits intomasterfrom
source_generator
Open

Add source generator#37
buvinghausen wants to merge 8 commits intomasterfrom
source_generator

Conversation

@buvinghausen
Copy link
Copy Markdown
Owner

Replace hand-written boilerplate with a Roslyn source generator

The 2,710-line TaskTupleExtensions.cs was entirely hand-written boilerplate — the same awaiter
pattern copy-pasted and incremented 16 times. Every feature addition (e.g. the ConfigureAwaitOptions
overloads) had to be replicated manually across every arity. This PR eliminates that maintenance burden
by replacing the file with a Roslyn incremental source generator.

Changes

src/TaskTupleAwaiter.Generator/ (new project)

A new netstandard2.0 project containing the generator. Key properties:

  • IsRoslynComponent=true + EnforceExtendedAnalyzerRules=true — correct configuration for a Roslyn component
  • IsPackable=false — the generator DLL is a build-time tool only and is never surfaced to consumers of the library

The generator implements IIncrementalGenerator and uses RegisterSourceOutput keyed to
ParseOptionsProvider rather than RegisterPostInitializationOutput. This lets Roslyn query
PreprocessorSymbolNames.Contains("NET8_0_OR_GREATER") once per TFM and cache the result — the
generator only reruns when preprocessor symbols actually change. The output is clean, #if-free code
tailored to each TFM variant.

TFM _options field type ConfigureAwait(ConfigureAwaitOptions) overloads
netstandard2.0, net462 bool
net8.0 ConfigureAwaitOptions

The 2,710 lines of boilerplate are replaced by ~230 lines of generator logic.

src/TaskTupleAwaiter/TaskTupleAwaiter.csproj (modified)

The generator is wired in as an internal build-time analyzer:

<ProjectReference Include="..\TaskTupleAwaiter.Generator\TaskTupleAwaiter.Generator.csproj"
                  OutputItemType="Analyzer"
                  ReferenceOutputAssembly="false" />

OutputItemType="Analyzer" loads the generator into the compiler. ReferenceOutputAssembly="false"
ensures the generator DLL is never emitted as a transitive dependency for consumers of the library.

src/TaskTupleAwaiter/TaskTupleExtensions.cs (deleted)

Replaced entirely by the generator output. Any future changes to the awaiter pattern — adding
ValueTask<T> support, changing the ConfigureAwait mapping, new TFM-specific overloads — now
happen in one place.

TaskTupleAwaiter.slnx (modified)

Generator project registered alongside the library project.

Verification

All existing tests pass unchanged across every TFM:

TFM Tests
net10.0 483 ✓
net9.0 483 ✓
net8.0 483 ✓
net472 499 ✓ (includes the 16 FirstExceptionIsThrown pre-NET8 cases)

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR replaces the hand-written TaskTupleExtensions boilerplate with a Roslyn incremental source generator, reducing maintenance overhead while preserving the tuple-awaiting API surface across TFMs (with ConfigureAwaitOptions only emitted for NET8+).

Changes:

  • Deleted the large hand-written TaskTupleExtensions.cs implementation.
  • Added a new TaskTupleAwaiter.Generator project implementing an IIncrementalGenerator that emits TaskTupleExtensions per-TFM.
  • Wired the generator into the main library project as a build-time analyzer and registered the new project in the solution.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/TaskTupleAwaiter/TaskTupleExtensions.cs Removes the previously hand-authored tuple awaiter/ConfigureAwait implementations (now generated).
src/TaskTupleAwaiter/TaskTupleAwaiter.csproj Adds the generator as an analyzer-only project reference so it runs at build time without flowing to consumers.
src/TaskTupleAwaiter.Generator/TaskTupleExtensionsGenerator.cs Implements the incremental generator that emits TaskTupleExtensions.g.cs tailored to NET8_0_OR_GREATER.
src/TaskTupleAwaiter.Generator/TaskTupleAwaiter.Generator.csproj Introduces the generator project with Roslyn-component settings and required package reference.
TaskTupleAwaiter.slnx Registers the generator project in the solution.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@buvinghausen
Copy link
Copy Markdown
Owner Author

@jnm2 thoughts on the revised structure?

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +11 to +19
public void Initialize(IncrementalGeneratorInitializationContext context) =>
context.RegisterSourceOutput(
context.CompilationProvider,
static (ctx, compilation) =>
{
var hasAwaitOptions = compilation
.GetTypeByMetadataName("System.Threading.Tasks.ConfigureAwaitOptions") is not null;
ctx.AddSource("TaskTupleExtensions.g.cs", GenerateSource(hasAwaitOptions));
});
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.

3 participants