From 9b04f4105493bd70cfbe85d26a9e539a2bcd10fe Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Wed, 19 Feb 2025 09:11:09 -0800 Subject: [PATCH 1/5] Remove unneeded design time generation --- .../SourceGenerators/RazorSourceGenerator.cs | 88 +++++++++---------- 1 file changed, 41 insertions(+), 47 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs index 5c1d6482e54..5f12716776c 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs @@ -233,67 +233,61 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .WithLambdaComparer((old, @new) => old.Left.Equals(@new.Left) && old.Right.SequenceEqual(@new.Right)) .Combine(razorSourceGeneratorOptions); - // Currently unused. See https://github.com/dotnet/roslyn/issues/71024. - var razorHostOutputsEnabled = analyzerConfigOptions.CheckGlobalFlagSet("EnableRazorHostOutputs"); - var withOptionsDesignTime = withOptions.EmptyOrCachedWhen(razorHostOutputsEnabled, false); - IncrementalValuesProvider<(string, SourceGeneratorRazorCodeDocument)> processed(bool designTime) - { - return (designTime ? withOptionsDesignTime : withOptions) - .Select((pair, _) => - { - var ((sourceItem, imports), razorSourceGeneratorOptions) = pair; + var processed = + withOptions + .Select((pair, _) => + { + var ((sourceItem, imports), razorSourceGeneratorOptions) = pair; - RazorSourceGeneratorEventSource.Log.ParseRazorDocumentStart(sourceItem.RelativePhysicalPath); + RazorSourceGeneratorEventSource.Log.ParseRazorDocumentStart(sourceItem.RelativePhysicalPath); - var projectEngine = GetGenerationProjectEngine(sourceItem, imports, razorSourceGeneratorOptions); + var projectEngine = GetGenerationProjectEngine(sourceItem, imports, razorSourceGeneratorOptions); - var document = projectEngine.ProcessInitialParse(sourceItem, designTime); + var document = projectEngine.ProcessInitialParse(sourceItem, designTime: false); - RazorSourceGeneratorEventSource.Log.ParseRazorDocumentStop(sourceItem.RelativePhysicalPath); - return (projectEngine, sourceItem.RelativePhysicalPath, document); - }) + RazorSourceGeneratorEventSource.Log.ParseRazorDocumentStop(sourceItem.RelativePhysicalPath); + return (projectEngine, sourceItem.RelativePhysicalPath, document); + }) - // Add the tag helpers in, but ignore if they've changed or not, only reprocessing the actual document changed - .Combine(allTagHelpers) - .WithLambdaComparer((old, @new) => old.Left.Equals(@new.Left)) - .Select(static (pair, _) => - { - var ((projectEngine, filePath, codeDocument), allTagHelpers) = pair; - RazorSourceGeneratorEventSource.Log.RewriteTagHelpersStart(filePath); + // Add the tag helpers in, but ignore if they've changed or not, only reprocessing the actual document changed + .Combine(allTagHelpers) + .WithLambdaComparer((old, @new) => old.Left.Equals(@new.Left)) + .Select(static (pair, _) => + { + var ((projectEngine, filePath, codeDocument), allTagHelpers) = pair; + RazorSourceGeneratorEventSource.Log.RewriteTagHelpersStart(filePath); - codeDocument = projectEngine.ProcessTagHelpers(codeDocument, allTagHelpers, checkForIdempotency: false); + codeDocument = projectEngine.ProcessTagHelpers(codeDocument, allTagHelpers, checkForIdempotency: false); - RazorSourceGeneratorEventSource.Log.RewriteTagHelpersStop(filePath); - return (projectEngine, filePath, codeDocument); - }) + RazorSourceGeneratorEventSource.Log.RewriteTagHelpersStop(filePath); + return (projectEngine, filePath, codeDocument); + }) - // next we do a second parse, along with the helpers, but check for idempotency. If the tag helpers used on the previous parse match, the compiler can skip re-writing them - .Combine(allTagHelpers) - .Select(static (pair, _) => - { - var ((projectEngine, filePath, document), allTagHelpers) = pair; - RazorSourceGeneratorEventSource.Log.CheckAndRewriteTagHelpersStart(filePath); + // next we do a second parse, along with the helpers, but check for idempotency. If the tag helpers used on the previous parse match, the compiler can skip re-writing them + .Combine(allTagHelpers) + .Select(static (pair, _) => + { + var ((projectEngine, filePath, document), allTagHelpers) = pair; + RazorSourceGeneratorEventSource.Log.CheckAndRewriteTagHelpersStart(filePath); - document = projectEngine.ProcessTagHelpers(document, allTagHelpers, checkForIdempotency: true); + document = projectEngine.ProcessTagHelpers(document, allTagHelpers, checkForIdempotency: true); - RazorSourceGeneratorEventSource.Log.CheckAndRewriteTagHelpersStop(filePath); - return (projectEngine, filePath, document); - }) - .Select((pair, _) => - { - var (projectEngine, filePath, document) = pair; + RazorSourceGeneratorEventSource.Log.CheckAndRewriteTagHelpersStop(filePath); + return (projectEngine, filePath, document); + }) + .Select((pair, _) => + { + var (projectEngine, filePath, document) = pair; - var kind = designTime ? "DesignTime" : "Runtime"; - RazorSourceGeneratorEventSource.Log.RazorCodeGenerateStart(filePath, kind); - document = projectEngine.ProcessRemaining(document); + RazorSourceGeneratorEventSource.Log.RazorCodeGenerateStart(filePath, "Runtime"); + document = projectEngine.ProcessRemaining(document); - RazorSourceGeneratorEventSource.Log.RazorCodeGenerateStop(filePath, kind); - return (filePath, document); - }); - } + RazorSourceGeneratorEventSource.Log.RazorCodeGenerateStop(filePath, "Runtime"); + return (filePath, document); + }); - var csharpDocuments = processed(designTime: false) + var csharpDocuments = processed .Select(static (pair, _) => { var (filePath, document) = pair; From db30121714a2185b0a39b62e66d0de0d67323172 Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Wed, 19 Feb 2025 09:12:37 -0800 Subject: [PATCH 2/5] Remove static logger --- .../src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs | 2 +- .../src/SourceGenerators/RazorSourceGenerator.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs index 9cf4e699a3b..ea48caa54d1 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs @@ -27,7 +27,7 @@ public partial class RazorSourceGenerator return default; } - Log.ComputeRazorSourceGeneratorOptions(); + RazorSourceGeneratorEventSource.Log.ComputeRazorSourceGeneratorOptions(); globalOptions.TryGetValue("build_property.RazorConfiguration", out var configurationName); globalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs index 5f12716776c..23cecc019c6 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs @@ -17,8 +17,6 @@ namespace Microsoft.NET.Sdk.Razor.SourceGenerators #pragma warning restore RS1041 // This compiler extension should not be implemented in an assembly with target framework '.NET 8.0'. References to other target frameworks will cause the compiler to behave unpredictably. public partial class RazorSourceGenerator : IIncrementalGenerator { - private static RazorSourceGeneratorEventSource Log => RazorSourceGeneratorEventSource.Log; - internal static bool UseRazorCohostServer { get; set; } = false; // Testing usage only. From 1564060f88e3481153ba6d37c5a6bf1f2dc797b4 Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Wed, 19 Feb 2025 09:14:59 -0800 Subject: [PATCH 3/5] Simplify --- .../SourceGenerators/RazorSourceGenerator.RazorProviders.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs index ea48caa54d1..2661a147e70 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs @@ -49,9 +49,7 @@ public partial class RazorSourceGenerator .Where(r => r.Display is { } display && display.EndsWith("Microsoft.AspNetCore.Components.dll", StringComparison.Ordinal)) .ToImmutableArray(); - var isComponentParameterSupported = minimalReferences.Length == 0 - ? false - : CSharpCompilation.Create("components", references: minimalReferences).HasAddComponentParameter(); + var isComponentParameterSupported = minimalReferences.Length != 0 && CSharpCompilation.Create("components", references: minimalReferences).HasAddComponentParameter(); var razorConfiguration = new RazorConfiguration(razorLanguageVersion, configurationName ?? "default", Extensions: [], UseConsolidatedMvcViews: true, SuppressAddComponentParameter: !isComponentParameterSupported); From 4c333b5c0e2a44939b62828d70b21e16b69e8600 Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Wed, 19 Feb 2025 09:15:12 -0800 Subject: [PATCH 4/5] Pass through cancellation token to process --- .../src/SourceGenerators/RazorSourceGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs index 23cecc019c6..23570475040 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs @@ -81,14 +81,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Combine(importFiles.Collect()) .Combine(razorSourceGeneratorOptions) .WithLambdaComparer((old, @new) => old.Right.Equals(@new.Right) && old.Left.Left.Equals(@new.Left.Left) && old.Left.Right.SequenceEqual(@new.Left.Right)) - .Select(static (pair, _) => + .Select(static (pair, cancellationToken) => { var ((sourceItem, importFiles), razorSourceGeneratorOptions) = pair; RazorSourceGeneratorEventSource.Log.GenerateDeclarationCodeStart(sourceItem.FilePath); var projectEngine = GetDeclarationProjectEngine(sourceItem, importFiles, razorSourceGeneratorOptions); - var codeGen = projectEngine.Process(sourceItem); + var codeGen = projectEngine.Process(sourceItem, cancellationToken); var result = new SourceGeneratorText(codeGen.GetCSharpDocument().Text); From cbc728e11274257793fcf5cf9409b80386891cb2 Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Wed, 19 Feb 2025 13:45:34 -0800 Subject: [PATCH 5/5] Flatten nested tuples --- .../IncrementalValueProviderExtensions.cs | 18 +++++++++++++ .../RazorSourceGenerator.RazorProviders.cs | 8 +++--- .../SourceGenerators/RazorSourceGenerator.cs | 27 +++++++++---------- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/IncrementalValueProviderExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/IncrementalValueProviderExtensions.cs index b1745bbbf9a..15ce3305b53 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/IncrementalValueProviderExtensions.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/IncrementalValueProviderExtensions.cs @@ -105,6 +105,24 @@ internal static IncrementalValueProvider CheckGlobalFlagSet(this Increment { return optionsProvider.Select((provider, _) => provider.GlobalOptions.TryGetValue($"build_property.{flagName}", out var flagValue) && flagValue == "true"); } + + internal static IncrementalValuesProvider> Combine(this IncrementalValuesProvider> provider, IncrementalValueProvider target) + { + return IncrementalValueProviderExtensions.Combine(provider, target) + .Select((p, _) => (p.Left.Item1, p.Left.Item2, p.Right)); + } + + internal static IncrementalValuesProvider> Combine(this IncrementalValuesProvider> provider, IncrementalValueProvider target) + { + return IncrementalValueProviderExtensions.Combine(provider, target) + .Select((p, _) => (p.Left.Item1, p.Left.Item2, p.Left.Item3, p.Right)); + } + + internal static IncrementalValueProvider> Combine(this IncrementalValueProvider> provider, IncrementalValueProvider target) + { + return IncrementalValueProviderExtensions.Combine(provider, target) + .Select((p, _) => (p.Left.Item1, p.Left.Item2, p.Right)); + } } internal sealed class LambdaComparer : IEqualityComparer diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs index 2661a147e70..d4f7cfcdd2b 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs @@ -17,18 +17,16 @@ namespace Microsoft.NET.Sdk.Razor.SourceGenerators { public partial class RazorSourceGenerator { - private (RazorSourceGenerationOptions?, Diagnostic?) ComputeRazorSourceGeneratorOptions((((AnalyzerConfigOptionsProvider, ParseOptions), ImmutableArray), bool) pair, CancellationToken ct) + private (RazorSourceGenerationOptions?, Diagnostic?) ComputeRazorSourceGeneratorOptions(AnalyzerConfigOptionsProvider analyzerConfigOptions, ParseOptions parseOptions, ImmutableArray references, bool isGeneratorSuppressed) { - var (((options, parseOptions), references), isSuppressed) = pair; - var globalOptions = options.GlobalOptions; - - if (isSuppressed) + if (isGeneratorSuppressed) { return default; } RazorSourceGeneratorEventSource.Log.ComputeRazorSourceGeneratorOptions(); + var globalOptions = analyzerConfigOptions.GlobalOptions; globalOptions.TryGetValue("build_property.RazorConfiguration", out var configurationName); globalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace); globalOptions.TryGetValue("build_property.SupportLocalizedComponentNames", out var supportLocalizedComponentNames); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs index 23570475040..1f6c529fd38 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs @@ -46,7 +46,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Combine(parseOptions) .Combine(metadataRefs.Collect()) .SuppressIfNeeded(isGeneratorSuppressed) - .Select(ComputeRazorSourceGeneratorOptions) + .Select((p, _) => ComputeRazorSourceGeneratorOptions(p.Item1.Item1, p.Item1.Item2, p.Item1.Item3, p.Item2)) .ReportDiagnostics(context); var sourceItems = additionalTexts @@ -80,10 +80,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var generatedDeclarationText = componentFiles .Combine(importFiles.Collect()) .Combine(razorSourceGeneratorOptions) - .WithLambdaComparer((old, @new) => old.Right.Equals(@new.Right) && old.Left.Left.Equals(@new.Left.Left) && old.Left.Right.SequenceEqual(@new.Left.Right)) + .WithLambdaComparer((old, @new) => old.Item3.Equals(@new.Item3) && old.Item1.Equals(@new.Item1) && old.Item2.SequenceEqual(@new.Item2)) .Select(static (pair, cancellationToken) => { - var ((sourceItem, importFiles), razorSourceGeneratorOptions) = pair; + var (sourceItem, importFiles, razorSourceGeneratorOptions) = pair; RazorSourceGeneratorEventSource.Log.GenerateDeclarationCodeStart(sourceItem.FilePath); var projectEngine = GetDeclarationProjectEngine(sourceItem, importFiles, razorSourceGeneratorOptions); @@ -143,8 +143,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Combine(hasRazorFiles) .WithLambdaComparer(static (a, b) => { - var ((compilationA, razorSourceGeneratorOptionsA), hasRazorFilesA) = a; - var ((compilationB, razorSourceGeneratorOptionsB), hasRazorFilesB) = b; + var (compilationA, razorSourceGeneratorOptionsA, hasRazorFilesA) = a; + var (compilationB, razorSourceGeneratorOptionsB, hasRazorFilesB) = b; // When using the generator cache in the compiler it's possible to encounter metadata references that are different instances // but ultimately represent the same underlying assembly. We compare the module version ids to determine if the references are the same @@ -192,7 +192,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) }) .Select(static (pair, _) => { - var ((compilation, razorSourceGeneratorOptions), hasRazorFiles) = pair; + var (compilation, razorSourceGeneratorOptions, hasRazorFiles) = pair; if (!hasRazorFiles) { // If there's no razor code in this app, don't do anything. @@ -231,12 +231,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .WithLambdaComparer((old, @new) => old.Left.Equals(@new.Left) && old.Right.SequenceEqual(@new.Right)) .Combine(razorSourceGeneratorOptions); - var processed = withOptions .Select((pair, _) => { - var ((sourceItem, imports), razorSourceGeneratorOptions) = pair; + var (sourceItem, imports, razorSourceGeneratorOptions) = pair; RazorSourceGeneratorEventSource.Log.ParseRazorDocumentStart(sourceItem.RelativePhysicalPath); @@ -248,12 +247,12 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return (projectEngine, sourceItem.RelativePhysicalPath, document); }) - // Add the tag helpers in, but ignore if they've changed or not, only reprocessing the actual document changed + // Add the tag helpers in, but ignore if they've changed or not, only reprocessing if the actual document changed .Combine(allTagHelpers) - .WithLambdaComparer((old, @new) => old.Left.Equals(@new.Left)) + .WithLambdaComparer((old, @new) => old.Item3.Equals(@new.Item3)) .Select(static (pair, _) => { - var ((projectEngine, filePath, codeDocument), allTagHelpers) = pair; + var (projectEngine, filePath, codeDocument, allTagHelpers) = pair; RazorSourceGeneratorEventSource.Log.RewriteTagHelpersStart(filePath); codeDocument = projectEngine.ProcessTagHelpers(codeDocument, allTagHelpers, checkForIdempotency: false); @@ -266,7 +265,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Combine(allTagHelpers) .Select(static (pair, _) => { - var ((projectEngine, filePath, document), allTagHelpers) = pair; + var (projectEngine, filePath, document, allTagHelpers) = pair; RazorSourceGeneratorEventSource.Log.CheckAndRewriteTagHelpersStart(filePath); document = projectEngine.ProcessTagHelpers(document, allTagHelpers, checkForIdempotency: true); @@ -310,7 +309,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterImplementationSourceOutput(csharpDocumentsWithSuppressionFlag, static (context, pair) => { - var ((hintName, _, csharpDocument), isGeneratorSuppressed) = pair; + var (hintName, _, csharpDocument, isGeneratorSuppressed) = pair; // When the generator is suppressed, we may still have a lot of cached data for perf, but we don't want to actually add any of the files to the output if (!isGeneratorSuppressed) @@ -336,7 +335,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterHostOutput(hostOutputs, (context, pair) => #pragma warning restore RSEXPERIMENTAL004 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. { - var ((documents, tagHelpers), isGeneratorSuppressed) = pair; + var (documents, tagHelpers, isGeneratorSuppressed) = pair; if (!isGeneratorSuppressed) {