Improve LoggerMessageGenerator and JsonSourceGenerator incrementality#124077
Improve LoggerMessageGenerator and JsonSourceGenerator incrementality#124077
Conversation
Removed Compilation from generator pipelines to improve incrementality. - LoggerMessageGenerator: Extract well-known symbols in ForAttributeWithMetadataName transform - JsonSourceGenerator: Move KnownTypeSymbols creation into ForAttributeWithMetadataName transform - Both generators now avoid Combine with CompilationProvider Co-authored-by: cincuranet <4540597+cincuranet@users.noreply.github.com>
- Remove conditional using directive for GetBestTypeByMetadataName extension - Update Roslyn 3.11 generator to use new Parser and Emitter APIs - Both generators now build successfully Co-authored-by: cincuranet <4540597+cincuranet@users.noreply.github.com>
- Change IsBaseOrIdentity to take Compilation instead of SemanticModel - Simplify Execute method by removing unnecessary grouping - Add clarifying comment in Roslyn 3.11 generator Co-authored-by: cincuranet <4540597+cincuranet@users.noreply.github.com>
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs
Outdated
Show resolved
Hide resolved
|
@copilot is it possible to get the benchmark numbers showing the improvement? |
Move all parsing logic into ForAttributeWithMetadataName transform to extract value-based LoggerClass data. No longer pass SemanticModel or INamedTypeSymbol references through the incremental pipeline, which was causing cache misses. Parser now runs in the transform and returns LoggerClass instances directly. Execute step merges classes and emits code without needing symbols. Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
The improvement is in build-time incrementality rather than runtime performance. The generator now avoids full re-runs when unrelated code changes (e.g., changing a method body that doesn't affect logger attributes). To measure this, you'd need to:
Roslyn's incremental generator infrastructure doesn't expose built-in metrics for this. The improvement is structural - by removing Would you like me to create a test project demonstrating the reduced re-runs, or would the structural fix verification be sufficient? |
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs
Show resolved
Hide resolved
src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs
Outdated
Show resolved
Hide resolved
…ural equality - Add LoggerClassSpec, LoggerMethodSpec, LoggerParameterSpec as immutable records - Implement IEquatable with structural equality using ImmutableEquatableArray - Keep original mutable classes for parsing, convert to Spec in transform - Add FromSpec method to convert back to mutable classes for emission - Include required polyfills (IsExternalInit, RequiredMember, HashHelpers, etc.) Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
- Use Key.Equals and Value.Equals instead of EqualityComparer - Remove unnecessary null-conditional operators in GetHashCode - Consistent with IEquatable constraints Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
|
@copilot could you add the following information to the PR description for the record: In the |
Added to the PR description under a new "Pragma Suppression Behavioral Note" section. |
|
@copilot please fix the build failure: |
…lable The original Parser reported MissingRequiredType when System.Exception was missing. After refactoring the transform to check required types upfront, the diagnostic was lost because it returned default instead of collecting a diagnostic. Now creates a DiagnosticInfo for MissingRequiredType and returns it in the tuple so Execute can report it, matching original behavior. Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
... Fixed in 6943e81. The |
|
@copilot please fix the failure: Discovering: Microsoft.Extensions.Logging.Generators.Roslyn3.11.Tests (method display = ClassAndMethod, method display options = None) |
The Roslyn 3.11 generator was silently returning when System.Exception was unavailable, instead of reporting the MissingRequiredType diagnostic. Split the combined null check to handle exceptionSymbol separately, matching the original Parser behavior that reported the diagnostic before returning. Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
Fixed in bfe3713. The Roslyn 3.11 path had a combined null check that silently returned when |
|
@copilot run the code review skill |
Code review completed. Here are the findings: No ❌ errors found.
Line 102 in context.RegisterSourceOutput(loggerClasses.Collect(), static (spc, items) => Execute(items, spc));Using
Lines 151-155: When merging partial class methods, a new 💡 suggestion — Lines 182-194: 💡 suggestion — The spec types are declared as ✅ Verified correct:
Code review completed. Found and fixed one issue in fdae4aa:
💡 TemplateMap ordering (follow-up, not blocking) — No errors found. All other aspects look correct:
|
All items from the same compilation should agree on hasStringCreate, but using |= instead of = is more resilient and costs nothing. Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
|
@tarekgh do you have a roslyn tracking issue for the We'll likely also need to file some breaking change notice since we document the pragma approach in https://learn.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib1025 /cc @ericstj |
|
I found #92509 and dotnet/roslyn#68291 which have more background on the issue |
|
Right, #92509 is the tracking issue for different generators. I'll file the breaking change doc. |
📋 Breaking Change Documentation RequiredCreate a breaking change issue with AI-generated content Generated by Breaking Change Documentation Tool - 2026-02-10 15:02:39 |
|
I filed the breaking change doc and linked it to this PR. @akoeplinger thanks for pointing at the failures and sorry for the inconvenience. Please let me know if there is anything else I can help with. |
Description
Both generators were passing
Compilationthrough their pipelines, causing full re-runs on any compilation change rather than only when attributed syntax nodes changed. This PR refactors both generators to follow proper incremental generator patterns.Changes Made
LoggerMessageGenerator:
ForAttributeWithMetadataNametransform, returning immutable specs with structural equalityWithTrackingName(StepNames.LoggerMessageTransform)for Roslyn 4.4+MissingRequiredTypediagnostic whenSystem.Exceptionis unavailable in both Roslyn 3.11 and 4.0+ pathsJsonSourceGenerator:
CompilationProviderdependencyKnownTypeSymbolsinto transformTesting:
Pragma Suppression Behavioral Note
In the test
.targetsand.WithDiagnostics.csfiles, the PR adds project-level<NoWarn>forSYSLIB1002;SYSLIB1013;SYSLIB1018;8795and<WarningsNotAsErrors>8795</WarningsNotAsErrors>. This is becauseDiagnosticInfo.GetTrimmedLocationcreates locations withoutSyntaxTreereferences, breaking#pragma warning disablematching. While this only affects the test project directly, the sameDiagnosticInfopattern is used in the productionRoslyn 4.0 generatorpath, meaning end-users of theLoggerMessageGeneratorwill lose the ability to suppress diagnostics via#pragmadirectives. This is a behavioral regression. The existingSystem.Text.Jsongenerator already has this limitation too, so the Logging source generator will start to have the same behavior asSystem.Text.Json.Performance Impact
Generator caches pipeline values and only reprocesses when attributed methods change.
Testing
Original prompt
LoggerMessageGeneratorincrementality #76119✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.