Conversation
There was a problem hiding this comment.
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.csimplementation. - Added a new
TaskTupleAwaiter.Generatorproject implementing anIIncrementalGeneratorthat emitsTaskTupleExtensionsper-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.
|
@jnm2 thoughts on the revised structure? |
There was a problem hiding this comment.
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.
| 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)); | ||
| }); |
Replace hand-written boilerplate with a Roslyn source generator
The 2,710-line
TaskTupleExtensions.cswas entirely hand-written boilerplate — the same awaiterpattern copy-pasted and incremented 16 times. Every feature addition (e.g. the
ConfigureAwaitOptionsoverloads) 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.0project containing the generator. Key properties:IsRoslynComponent=true+EnforceExtendedAnalyzerRules=true— correct configuration for a Roslyn componentIsPackable=false— the generator DLL is a build-time tool only and is never surfaced to consumers of the libraryThe generator implements
IIncrementalGeneratorand usesRegisterSourceOutputkeyed toParseOptionsProviderrather thanRegisterPostInitializationOutput. This lets Roslyn queryPreprocessorSymbolNames.Contains("NET8_0_OR_GREATER")once per TFM and cache the result — thegenerator only reruns when preprocessor symbols actually change. The output is clean,
#if-free codetailored to each TFM variant.
_optionsfield typeConfigureAwait(ConfigureAwaitOptions)overloadsnetstandard2.0,net462boolnet8.0ConfigureAwaitOptionsThe 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:
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 theConfigureAwaitmapping, new TFM-specific overloads — nowhappen in one place.
TaskTupleAwaiter.slnx(modified)Generator project registered alongside the library project.
Verification
All existing tests pass unchanged across every TFM:
net10.0net9.0net8.0net472FirstExceptionIsThrownpre-NET8 cases)