From a6a61fdfa748eaa65aab53dab583276e26af4a3e Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Mon, 22 May 2023 18:07:23 +0200 Subject: [PATCH 1/3] [17.6] Revert "Perf/generator (#8212)" (#8742) * Revert "Perf/generator (#8212)" This reverts commit 4ab512e7f0690cafa6e1515667d88625dc24f015. * Add more tests --- ...aultRazorTagHelperContextDiscoveryPhase.cs | 3 +- .../src/DefaultRazorTagHelperRewritePhase.cs | 21 +- ...soft.NET.Sdk.Razor.SourceGenerators.csproj | 1 - .../RazorSourceGenerator.Helpers.cs | 10 +- .../RazorSourceGenerator.cs | 160 ++--- .../RazorSourceGeneratorEventSource.cs | 32 - .../SourceGeneratorProjectEngine.cs | 115 ---- .../SourceGeneratorRazorCodeDocument.cs | 31 - .../StaticCompilationTagHelperFeature.cs | 12 +- .../StaticTagHelperFeature.cs | 25 + .../RazorSourceGeneratorComponentTests.cs | 141 ++++ .../RazorSourceGeneratorTagHelperTests.cs | 104 +++ .../RazorSourceGeneratorTests.cs | 605 ++++++++++-------- .../RazorSourceGeneratorTestsBase.cs | 35 +- .../ComponentInheritsFromComponent.html | 2 + .../Views_Home_Index.html | 2 + .../PartialClass.html | 2 + .../PartialClass/Views_Home_Index.html | 2 + .../PartialClass_NoBaseInCSharp.html | 2 + .../Views_Home_Index.html | 2 + .../ComponentAndTagHelper.html | 2 + ...mponentAndTagHelper_HtmlTargetElement.html | 3 + 22 files changed, 711 insertions(+), 601 deletions(-) delete mode 100644 src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/SourceGeneratorProjectEngine.cs delete mode 100644 src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/SourceGeneratorRazorCodeDocument.cs create mode 100644 src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticTagHelperFeature.cs create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorComponentTests.cs create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/ComponentInheritsFromComponent.html create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/ComponentInheritsFromComponent/Views_Home_Index.html create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass.html create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass/Views_Home_Index.html create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass_NoBaseInCSharp.html create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass_NoBaseInCSharp/Views_Home_Index.html create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper.html create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper_HtmlTargetElement.html diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorTagHelperContextDiscoveryPhase.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorTagHelperContextDiscoveryPhase.cs index 21fbe74bc71..370d39e130b 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorTagHelperContextDiscoveryPhase.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorTagHelperContextDiscoveryPhase.cs @@ -17,7 +17,7 @@ internal sealed class DefaultRazorTagHelperContextDiscoveryPhase : RazorEnginePh { protected override void ExecuteCore(RazorCodeDocument codeDocument) { - var syntaxTree = codeDocument.GetPreTagHelperSyntaxTree() ?? codeDocument.GetSyntaxTree(); + var syntaxTree = codeDocument.GetSyntaxTree(); ThrowForMissingDocumentDependency(syntaxTree); var descriptors = codeDocument.GetTagHelpers(); @@ -69,6 +69,7 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument) var context = TagHelperDocumentContext.Create(tagHelperPrefix, descriptors); codeDocument.SetTagHelperContext(context); + codeDocument.SetPreTagHelperSyntaxTree(syntaxTree); } private static bool MatchesDirective(TagHelperDescriptor descriptor, string typePattern, string assemblyName) diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorTagHelperRewritePhase.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorTagHelperRewritePhase.cs index cb8632d70b0..402020d1afc 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorTagHelperRewritePhase.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorTagHelperRewritePhase.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. #nullable enable -using System.Collections.Generic; using Microsoft.AspNetCore.Razor.Language.Legacy; namespace Microsoft.AspNetCore.Razor.Language; @@ -10,19 +9,17 @@ internal sealed class DefaultRazorTagHelperRewritePhase : RazorEnginePhaseBase { protected override void ExecuteCore(RazorCodeDocument codeDocument) { - var syntaxTree = codeDocument.GetPreTagHelperSyntaxTree() ?? codeDocument.GetSyntaxTree(); - ThrowForMissingDocumentDependency(syntaxTree); - + var syntaxTree = codeDocument.GetPreTagHelperSyntaxTree(); var context = codeDocument.GetTagHelperContext(); - if (context?.TagHelpers.Count > 0) - { - var rewrittenSyntaxTree = TagHelperParseTreeRewriter.Rewrite(syntaxTree, context.Prefix, context.TagHelpers, out var usedHelpers); - codeDocument.SetSyntaxTree(rewrittenSyntaxTree); - codeDocument.SetReferencedTagHelpers(usedHelpers); - } - else + if (syntaxTree is null || context.TagHelpers.Count == 0) { - codeDocument.SetReferencedTagHelpers(new HashSet()); + // No descriptors, no-op. + return; } + + var rewrittenSyntaxTree = TagHelperParseTreeRewriter.Rewrite(syntaxTree, context.Prefix, context.TagHelpers, out var usedHelpers); + + codeDocument.SetReferencedTagHelpers(usedHelpers); + codeDocument.SetSyntaxTree(rewrittenSyntaxTree); } } diff --git a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/Microsoft.NET.Sdk.Razor.SourceGenerators.csproj b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/Microsoft.NET.Sdk.Razor.SourceGenerators.csproj index b5a9a50af34..682739d6978 100644 --- a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/Microsoft.NET.Sdk.Razor.SourceGenerators.csproj +++ b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/Microsoft.NET.Sdk.Razor.SourceGenerators.csproj @@ -19,7 +19,6 @@ - diff --git a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.Helpers.cs b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.Helpers.cs index 7dfece6c432..d7cb91e4910 100644 --- a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.Helpers.cs +++ b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.Helpers.cs @@ -83,7 +83,8 @@ private static RazorProjectEngine GetDiscoveryProjectEngine( return discoveryProjectEngine; } - private static SourceGeneratorProjectEngine GetGenerationProjectEngine( + private static RazorProjectEngine GetGenerationProjectEngine( + IReadOnlyList tagHelpers, SourceGeneratorProjectItem item, IEnumerable imports, RazorSourceGenerationOptions razorSourceGeneratorOptions) @@ -95,7 +96,7 @@ private static SourceGeneratorProjectEngine GetGenerationProjectEngine( fileSystem.Add(import); } - var projectEngine = (DefaultRazorProjectEngine)RazorProjectEngine.Create(razorSourceGeneratorOptions.Configuration, fileSystem, b => + var projectEngine = RazorProjectEngine.Create(razorSourceGeneratorOptions.Configuration, fileSystem, b => { b.Features.Add(new DefaultTypeNameFeature()); b.SetRootNamespace(razorSourceGeneratorOptions.RootNamespace); @@ -106,13 +107,16 @@ private static SourceGeneratorProjectEngine GetGenerationProjectEngine( options.SupportLocalizedComponentNames = razorSourceGeneratorOptions.SupportLocalizedComponentNames; })); + b.Features.Add(new StaticTagHelperFeature { TagHelpers = tagHelpers }); + b.Features.Add(new DefaultTagHelperDescriptorProvider()); + CompilerFeatures.Register(b); RazorExtensions.Register(b); b.SetCSharpLanguageVersion(razorSourceGeneratorOptions.CSharpLanguageVersion); }); - return new SourceGeneratorProjectEngine(projectEngine); + return projectEngine; } } } diff --git a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.cs b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.cs index b813fdba645..4fd80d7e3b6 100644 --- a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.cs +++ b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.cs @@ -2,15 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.IO; using System.Linq; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.NET.Sdk.Razor.SourceGenerators { @@ -68,12 +66,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var generatedDeclarationCode = 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)), (a) => a.GetHashCode()) .Select(static (pair, _) => { var ((sourceItem, importFiles), razorSourceGeneratorOptions) = pair; - RazorSourceGeneratorEventSource.Log.GenerateDeclarationCodeStart(sourceItem.RelativePhysicalPath); + RazorSourceGeneratorEventSource.Log.GenerateDeclarationCodeStart(sourceItem.FilePath); var projectEngine = GetDeclarationProjectEngine(sourceItem, importFiles, razorSourceGeneratorOptions); @@ -81,69 +78,57 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var result = codeGen.GetCSharpDocument().GeneratedCode; - RazorSourceGeneratorEventSource.Log.GenerateDeclarationCodeStop(sourceItem.RelativePhysicalPath); + RazorSourceGeneratorEventSource.Log.GenerateDeclarationCodeStop(sourceItem.FilePath); - return (result, sourceItem.RelativePhysicalPath); + return result; }); var generatedDeclarationSyntaxTrees = generatedDeclarationCode .Combine(parseOptions) - .Select(static (pair, ct) => + .Select(static (pair, _) => { - var ((generatedDeclarationCode, filePath), parseOptions) = pair; - return CSharpSyntaxTree.ParseText(generatedDeclarationCode, (CSharpParseOptions)parseOptions, filePath, cancellationToken: ct); + var (generatedDeclarationCode, parseOptions) = pair; + return CSharpSyntaxTree.ParseText(generatedDeclarationCode, (CSharpParseOptions)parseOptions); }); - var tagHelpersFromComponents = generatedDeclarationSyntaxTrees - .Combine(compilation) + var tagHelpersFromCompilation = compilation + .Combine(generatedDeclarationSyntaxTrees.Collect()) .Combine(razorSourceGeneratorOptions) - .SelectMany(static (pair, ct) => + .Select(static (pair, _) => { + RazorSourceGeneratorEventSource.Log.DiscoverTagHelpersFromCompilationStart(); - var ((generatedDeclarationSyntaxTree, compilation), razorSourceGeneratorOptions) = pair; - RazorSourceGeneratorEventSource.Log.DiscoverTagHelpersFromComponentStart(generatedDeclarationSyntaxTree.FilePath); + var ((compilation, generatedDeclarationSyntaxTrees), razorSourceGeneratorOptions) = pair; var tagHelperFeature = new StaticCompilationTagHelperFeature(); var discoveryProjectEngine = GetDiscoveryProjectEngine(compilation.References.ToImmutableArray(), tagHelperFeature); - var compilationWithDeclarations = compilation.AddSyntaxTrees(generatedDeclarationSyntaxTree); - - // try and find the specific root class this component is declaring, falling back to the assembly if for any reason the code is not in the shape we expect - ISymbol targetSymbol = compilationWithDeclarations.Assembly; - var root = generatedDeclarationSyntaxTree.GetRoot(ct); - if (root is CompilationUnitSyntax { Members: [NamespaceDeclarationSyntax { Members: [ClassDeclarationSyntax classSyntax, ..] }, ..] }) - { - var declaredClass = compilationWithDeclarations.GetSemanticModel(generatedDeclarationSyntaxTree).GetDeclaredSymbol(classSyntax, ct); - Debug.Assert(declaredClass is null || declaredClass is { AllInterfaces: [{ Name: "IComponent" }, ..] }); - targetSymbol = declaredClass ?? targetSymbol; - } + var compilationWithDeclarations = compilation.AddSyntaxTrees(generatedDeclarationSyntaxTrees); tagHelperFeature.Compilation = compilationWithDeclarations; - tagHelperFeature.TargetSymbol = targetSymbol; + tagHelperFeature.TargetSymbol = compilationWithDeclarations.Assembly; - var result = tagHelperFeature.GetDescriptors(); - RazorSourceGeneratorEventSource.Log.DiscoverTagHelpersFromComponentStop(generatedDeclarationSyntaxTree.FilePath); + var result = (IList)tagHelperFeature.GetDescriptors(); + RazorSourceGeneratorEventSource.Log.DiscoverTagHelpersFromCompilationStop(); return result; - }); - - var tagHelpersFromCompilation = compilation - .Combine(razorSourceGeneratorOptions) - .Select(static (pair, _) => + }) + .WithLambdaComparer(static (a, b) => { - RazorSourceGeneratorEventSource.Log.DiscoverTagHelpersFromCompilationStart(); - - var (compilation, razorSourceGeneratorOptions) = pair; - - var tagHelperFeature = new StaticCompilationTagHelperFeature(); - var discoveryProjectEngine = GetDiscoveryProjectEngine(compilation.References.ToImmutableArray(), tagHelperFeature); + if (a.Count != b.Count) + { + return false; + } - tagHelperFeature.Compilation = compilation; - tagHelperFeature.TargetSymbol = compilation.Assembly; + for (var i = 0; i < a.Count; i++) + { + if (!a[i].Equals(b[i])) + { + return false; + } + } - var result = tagHelperFeature.GetDescriptors(); - RazorSourceGeneratorEventSource.Log.DiscoverTagHelpersFromCompilationStop(); - return result; - }); + return true; + }, getHashCode: static a => a.Count); var tagHelpersFromReferences = compilation .Combine(razorSourceGeneratorOptions) @@ -186,7 +171,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var tagHelperFeature = new StaticCompilationTagHelperFeature(); var discoveryProjectEngine = GetDiscoveryProjectEngine(compilation.References.ToImmutableArray(), tagHelperFeature); - using var pool = ArrayBuilderPool.GetPooledObject(out var descriptors); + List descriptors = new(); tagHelperFeature.Compilation = compilation; foreach (var reference in compilation.References) { @@ -198,84 +183,47 @@ public void Initialize(IncrementalGeneratorInitializationContext context) } RazorSourceGeneratorEventSource.Log.DiscoverTagHelpersFromReferencesStop(); - return descriptors.ToImmutable(); + return (ICollection)descriptors; }); - var allTagHelpers = tagHelpersFromComponents.Collect() - .Combine(tagHelpersFromCompilation) + var allTagHelpers = tagHelpersFromCompilation .Combine(tagHelpersFromReferences) .Select(static (pair, _) => { - var ((tagHelpersFromComponents, tagHelpersFromCompilation), tagHelpersFromReferences) = pair; - var count = tagHelpersFromCompilation.Length + tagHelpersFromReferences.Length + tagHelpersFromComponents.Length; + var (tagHelpersFromCompilation, tagHelpersFromReferences) = pair; + var count = tagHelpersFromCompilation.Count + tagHelpersFromReferences.Count; if (count == 0) { - return ImmutableArray.Empty; + return Array.Empty(); } - using var pool = ArrayBuilderPool.GetPooledObject(out var allTagHelpers); - allTagHelpers.AddRange(tagHelpersFromCompilation); - allTagHelpers.AddRange(tagHelpersFromReferences); - allTagHelpers.AddRange(tagHelpersFromComponents); + var allTagHelpers = new TagHelperDescriptor[count]; + tagHelpersFromCompilation.CopyTo(allTagHelpers, 0); + tagHelpersFromReferences.CopyTo(allTagHelpers, tagHelpersFromCompilation.Count); - return allTagHelpers.ToImmutable(); + return allTagHelpers; }); var generatedOutput = sourceItems .Combine(importFiles.Collect()) - .WithLambdaComparer((old, @new) => old.Left.Equals(@new.Left) && old.Right.SequenceEqual(@new.Right), (a) => a.GetHashCode()) + .Combine(allTagHelpers) .Combine(razorSourceGeneratorOptions) .Select(static (pair, _) => { - var ((sourceItem, imports), razorSourceGeneratorOptions) = pair; - - RazorSourceGeneratorEventSource.Log.ParseRazorDocumentStart(sourceItem.RelativePhysicalPath); - - var projectEngine = GetGenerationProjectEngine(sourceItem, imports, razorSourceGeneratorOptions); - - var document = projectEngine.ProcessInitialParse(sourceItem); - - 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), (item) => item.GetHashCode()) - .Select((pair, _) => - { - var ((projectEngine, filePath, codeDocument), allTagHelpers) = pair; - RazorSourceGeneratorEventSource.Log.RewriteTagHelpersStart(filePath); + var (((sourceItem, imports), allTagHelpers), razorSourceGeneratorOptions) = pair; - codeDocument = projectEngine.ProcessTagHelpers(codeDocument, allTagHelpers, checkForIdempotency: false); + RazorSourceGeneratorEventSource.Log.RazorCodeGenerateStart(sourceItem.FilePath); - 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-computing them - .Combine(allTagHelpers) - .Select((pair, _) => - { + // Add a generated suffix so tools, such as coverlet, consider the file to be generated + var hintName = GetIdentifierFromPath(sourceItem.RelativePhysicalPath) + ".g.cs"; - var ((projectEngine, filePath, document), allTagHelpers) = pair; - RazorSourceGeneratorEventSource.Log.CheckAndRewriteTagHelpersStart(filePath); + var projectEngine = GetGenerationProjectEngine(allTagHelpers, sourceItem, imports, razorSourceGeneratorOptions); - document = projectEngine.ProcessTagHelpers(document, allTagHelpers, checkForIdempotency: true); + var codeDocument = projectEngine.Process(sourceItem); + var csharpDocument = codeDocument.GetCSharpDocument(); - RazorSourceGeneratorEventSource.Log.CheckAndRewriteTagHelpersStop(filePath); - return (projectEngine, filePath, document); - }) - - .Select((pair, _) => - { - var (projectEngine, filePath, document) = pair; - RazorSourceGeneratorEventSource.Log.RazorCodeGenerateStart(filePath); - document = projectEngine.ProcessRemaining(document); - var csharpDocument = document.CodeDocument.GetCSharpDocument(); - - RazorSourceGeneratorEventSource.Log.RazorCodeGenerateStop(filePath); - return (filePath, csharpDocument); + RazorSourceGeneratorEventSource.Log.RazorCodeGenerateStop(sourceItem.FilePath); + return (hintName, csharpDocument); }) .WithLambdaComparer(static (a, b) => { @@ -290,11 +238,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterSourceOutput(generatedOutput, static (context, pair) => { - var (filePath, csharpDocument) = pair; - - // Add a generated suffix so tools, such as coverlet, consider the file to be generated - var hintName = GetIdentifierFromPath(filePath) + ".g.cs"; - + var (hintName, csharpDocument) = pair; RazorSourceGeneratorEventSource.Log.AddSyntaxTrees(hintName); for (var i = 0; i < csharpDocument.Diagnostics.Count; i++) { diff --git a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGeneratorEventSource.cs b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGeneratorEventSource.cs index b94646cc81a..758d0069b8b 100644 --- a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGeneratorEventSource.cs +++ b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGeneratorEventSource.cs @@ -59,37 +59,5 @@ private RazorSourceGeneratorEventSource() { } private const int GenerateDeclarationSyntaxTreeStopId = 14; [Event(GenerateDeclarationSyntaxTreeStopId, Level = EventLevel.Informational)] public void GenerateDeclarationSyntaxTreeStop() => WriteEvent(GenerateDeclarationSyntaxTreeStopId); - - private const int DiscoverTagHelpersFromComponentStartId = 15; - [Event(DiscoverTagHelpersFromComponentStartId, Level = EventLevel.Informational)] - public void DiscoverTagHelpersFromComponentStart(string filePath) => WriteEvent(DiscoverTagHelpersFromComponentStartId, filePath); - - private const int DiscoverTagHelpersFromComponentStopId = 16; - [Event(DiscoverTagHelpersFromComponentStopId, Level = EventLevel.Informational)] - public void DiscoverTagHelpersFromComponentStop(string filePath) => WriteEvent(DiscoverTagHelpersFromComponentStopId, filePath); - - private const int ParseRazorDocumentStartId = 17; - [Event(ParseRazorDocumentStartId, Level = EventLevel.Informational)] - public void ParseRazorDocumentStart(string file) => WriteEvent(ParseRazorDocumentStartId, file); - - private const int ParseRazorDocumentStopId = 18; - [Event(ParseRazorDocumentStopId, Level = EventLevel.Informational)] - public void ParseRazorDocumentStop(string file) => WriteEvent(ParseRazorDocumentStopId, file); - - private const int RewriteTagHelpersStartId = 19; - [Event(RewriteTagHelpersStartId, Level = EventLevel.Informational)] - public void RewriteTagHelpersStart(string file) => WriteEvent(RewriteTagHelpersStartId, file); - - private const int RewriteTagHelpersStopId = 20; - [Event(RewriteTagHelpersStopId, Level = EventLevel.Informational)] - public void RewriteTagHelpersStop(string file) => WriteEvent(RewriteTagHelpersStopId, file); - - private const int CheckAndRewriteTagHelpersStartId = 21; - [Event(CheckAndRewriteTagHelpersStartId, Level = EventLevel.Informational)] - public void CheckAndRewriteTagHelpersStart(string file) => WriteEvent(CheckAndRewriteTagHelpersStartId, file); - - private const int CheckAndRewriteTagHelpersStopId = 22; - [Event(CheckAndRewriteTagHelpersStopId, Level = EventLevel.Informational)] - public void CheckAndRewriteTagHelpersStop(string file) => WriteEvent(CheckAndRewriteTagHelpersStopId, file); } } diff --git a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/SourceGeneratorProjectEngine.cs b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/SourceGeneratorProjectEngine.cs deleted file mode 100644 index 4acb91159b3..00000000000 --- a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/SourceGeneratorProjectEngine.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Microsoft.AspNetCore.Razor.Language; - -namespace Microsoft.NET.Sdk.Razor.SourceGenerators; - -internal class SourceGeneratorProjectEngine : DefaultRazorProjectEngine -{ - private readonly int discoveryPhaseIndex = -1; - - private readonly int rewritePhaseIndex = -1; - - public SourceGeneratorProjectEngine(DefaultRazorProjectEngine projectEngine) - : base(projectEngine.Configuration, projectEngine.Engine, projectEngine.FileSystem, projectEngine.ProjectFeatures) - { - for (int i = 0; i < Engine.Phases.Count; i++) - { - if (Engine.Phases[i] is DefaultRazorTagHelperContextDiscoveryPhase) - { - discoveryPhaseIndex = i; - } - else if (Engine.Phases[i] is DefaultRazorTagHelperRewritePhase) - { - rewritePhaseIndex = i; - } - else if (discoveryPhaseIndex >= 0 && rewritePhaseIndex >= 0) - { - break; - } - } - Debug.Assert(discoveryPhaseIndex >= 0); - Debug.Assert(rewritePhaseIndex >= 0); - } - - public SourceGeneratorRazorCodeDocument ProcessInitialParse(RazorProjectItem projectItem) - { - var codeDocument = CreateCodeDocumentCore(projectItem); - ProcessPartial(codeDocument, 0, discoveryPhaseIndex); - - // record the syntax tree, before the tag helper re-writing occurs - codeDocument.SetPreTagHelperSyntaxTree(codeDocument.GetSyntaxTree()); - return new SourceGeneratorRazorCodeDocument(codeDocument); - } - - public SourceGeneratorRazorCodeDocument ProcessTagHelpers(SourceGeneratorRazorCodeDocument sgDocument, IReadOnlyList tagHelpers, bool checkForIdempotency) - { - Debug.Assert(sgDocument.CodeDocument.GetPreTagHelperSyntaxTree() is not null); - - int startIndex = discoveryPhaseIndex; - var codeDocument = sgDocument.CodeDocument; - var previousTagHelpers = codeDocument.GetTagHelpers(); - if (checkForIdempotency && previousTagHelpers is not null) - { - // compare the tag helpers with the ones the document last used - if (Enumerable.SequenceEqual(tagHelpers, previousTagHelpers)) - { - // tag helpers are the same, nothing to do! - return sgDocument; - } - else - { - // tag helpers have changed, figure out if we need to re-write - var oldContextHelpers = codeDocument.GetTagHelperContext().TagHelpers; - - // re-run the scope check to figure out which tag helpers this document can see - codeDocument.SetTagHelpers(tagHelpers); - Engine.Phases[discoveryPhaseIndex].Execute(codeDocument); - - // Check if any new tag helpers were added or ones we previously used were removed - var newContextHelpers = codeDocument.GetTagHelperContext().TagHelpers; - var added = newContextHelpers.Except(oldContextHelpers); - var referencedByRemoved = codeDocument.GetReferencedTagHelpers().Except(newContextHelpers); - if (!added.Any() && !referencedByRemoved.Any()) - { - // Either nothing new, or any that got removed weren't used by this document anyway - return sgDocument; - } - - // We need to re-write the document, but can skip the scoping as we just performed it - startIndex = rewritePhaseIndex; - } - } - else - { - codeDocument.SetTagHelpers(tagHelpers); - } - - ProcessPartial(codeDocument, startIndex, rewritePhaseIndex + 1); - return new SourceGeneratorRazorCodeDocument(codeDocument); - } - - public SourceGeneratorRazorCodeDocument ProcessRemaining(SourceGeneratorRazorCodeDocument sgDocument) - { - var codeDocument = sgDocument.CodeDocument; - Debug.Assert(codeDocument.GetReferencedTagHelpers() is not null); - - ProcessPartial(sgDocument.CodeDocument, rewritePhaseIndex, Engine.Phases.Count); - return new SourceGeneratorRazorCodeDocument(codeDocument); - } - - private void ProcessPartial(RazorCodeDocument codeDocument, int startIndex, int endIndex) - { - Debug.Assert(startIndex >= 0 && startIndex <= endIndex && endIndex <= Engine.Phases.Count); - for (var i = startIndex; i < endIndex; i++) - { - Engine.Phases[i].Execute(codeDocument); - } - } -} diff --git a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/SourceGeneratorRazorCodeDocument.cs b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/SourceGeneratorRazorCodeDocument.cs deleted file mode 100644 index 707e4345e63..00000000000 --- a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/SourceGeneratorRazorCodeDocument.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -using Microsoft.AspNetCore.Razor.Language; - -namespace Microsoft.NET.Sdk.Razor.SourceGenerators; - -/// -/// A wrapper for -/// -/// -/// The razor compiler modifies the in place during the various phases, -/// meaning object identity is maintained even when the contents have changed. -/// -/// We need to be able to identify from the source generator if a given code document was modified or -/// returned unchanged. Rather than implementing deep equality on the -/// which can get expensive when the is large, we instead use a wrapper class. -/// If the underlying document is unchanged we return the original wrapper class. If the underlying -/// document is changed, we return a new instance of the wrapper. -/// -internal class SourceGeneratorRazorCodeDocument -{ - public RazorCodeDocument CodeDocument { get; } - - public SourceGeneratorRazorCodeDocument(RazorCodeDocument razorCodeDocument) - { - this.CodeDocument = razorCodeDocument; - } -} diff --git a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticCompilationTagHelperFeature.cs b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticCompilationTagHelperFeature.cs index 4828c20dd5d..15d54b36587 100644 --- a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticCompilationTagHelperFeature.cs +++ b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticCompilationTagHelperFeature.cs @@ -2,10 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Razor; @@ -13,16 +11,18 @@ namespace Microsoft.NET.Sdk.Razor.SourceGenerators { internal sealed class StaticCompilationTagHelperFeature : RazorEngineFeatureBase, ITagHelperFeature { + private static readonly List EmptyList = new(); + private ITagHelperDescriptorProvider[]? _providers; - public ImmutableArray GetDescriptors() + public List GetDescriptors() { if (Compilation is null) { - return ImmutableArray.Empty; + return EmptyList; } - using var pool = ArrayBuilderPool.GetPooledObject(out var results); + var results = new List(); var context = TagHelperDescriptorProviderContext.Create(results); context.SetCompilation(Compilation); if (TargetSymbol is not null) @@ -35,7 +35,7 @@ public ImmutableArray GetDescriptors() _providers[i].Execute(context); } - return results.ToImmutable(); + return results; } IReadOnlyList ITagHelperFeature.GetDescriptors() => GetDescriptors(); diff --git a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticTagHelperFeature.cs b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticTagHelperFeature.cs new file mode 100644 index 00000000000..89207e40b8a --- /dev/null +++ b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticTagHelperFeature.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNetCore.Razor.Language; + +namespace Microsoft.NET.Sdk.Razor.SourceGenerators +{ + internal sealed class StaticTagHelperFeature : RazorEngineFeatureBase, ITagHelperFeature + { + public IReadOnlyList TagHelpers { get; set; } + + public IReadOnlyList GetDescriptors() => TagHelpers; + + public StaticTagHelperFeature() + { + TagHelpers = new List(); + } + + public StaticTagHelperFeature(IEnumerable tagHelpers) + { + TagHelpers = new List(tagHelpers); + } + } +} diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorComponentTests.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorComponentTests.cs new file mode 100644 index 00000000000..cae3777ccaf --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorComponentTests.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Test.Common; +using Xunit; + +namespace Microsoft.NET.Sdk.Razor.SourceGenerators; + +public sealed class RazorSourceGeneratorComponentTests : RazorSourceGeneratorTestsBase +{ + [Fact, WorkItem("https://github.com/dotnet/razor/issues/8718")] + public async Task PartialClass() + { + // Arrange + var project = CreateTestProject(new() + { + ["Views/Home/Index.cshtml"] = """ + @(await Html.RenderComponentAsync(RenderMode.Static)) + """, + ["Shared/Component1.razor"] = """ + + """, + ["Shared/Component2.razor"] = """ + @inherits ComponentBase + + Value: @(Param + 1) + + @code { + [Parameter] + public int Param { get; set; } + } + """ + }, new() + { + ["Component2.razor.cs"] = """ + using Microsoft.AspNetCore.Components; + + namespace MyApp.Shared; + + public partial class Component2 : ComponentBase { } + """ + }); + var compilation = await project.GetCompilationAsync(); + var driver = await GetDriverAsync(project, options => + { + options.TestGlobalOptions["build_property.RazorLangVersion"] = "7.0"; + }); + + // Act + var result = RunGenerator(compilation!, ref driver, out compilation); + + // Assert + Assert.Empty(result.Diagnostics); + Assert.Equal(3, result.GeneratedSources.Length); + await VerifyRazorPageMatchesBaselineAsync(compilation, "Views_Home_Index"); + } + + [Fact, WorkItem("https://github.com/dotnet/razor/issues/8718")] + public async Task PartialClass_NoBaseInCSharp() + { + // Arrange + var project = CreateTestProject(new() + { + ["Views/Home/Index.cshtml"] = """ + @(await Html.RenderComponentAsync(RenderMode.Static)) + """, + ["Shared/Component1.razor"] = """ + + """, + ["Shared/Component2.razor"] = """ + @inherits ComponentBase + + Value: @(Param + 1) + + @code { + [Parameter] + public int Param { get; set; } + } + """ + }, new() + { + ["Component2.razor.cs"] = """ + using Microsoft.AspNetCore.Components; + + namespace MyApp.Shared; + + public partial class Component2 { } + """ + }); + var compilation = await project.GetCompilationAsync(); + var driver = await GetDriverAsync(project, options => + { + options.TestGlobalOptions["build_property.RazorLangVersion"] = "7.0"; + }); + + // Act + var result = RunGenerator(compilation!, ref driver, out compilation); + + // Assert + Assert.Empty(result.Diagnostics); + Assert.Equal(3, result.GeneratedSources.Length); + await VerifyRazorPageMatchesBaselineAsync(compilation, "Views_Home_Index"); + } + + [Fact, WorkItem("https://github.com/dotnet/razor/issues/8718")] + public async Task ComponentInheritsFromComponent() + { + // Arrange + var project = CreateTestProject(new() + { + ["Views/Home/Index.cshtml"] = """ + @(await Html.RenderComponentAsync(RenderMode.Static)) + """, + ["Shared/Component1.razor"] = """ + Hello from Component1 + + """, + ["Shared/BaseComponent.razor"] = """ + Hello from Base + """, + ["Shared/DerivedComponent.razor"] = """ + @inherits BaseComponent + Hello from Derived + """ + }); + var compilation = await project.GetCompilationAsync(); + var driver = await GetDriverAsync(project, options => + { + options.TestGlobalOptions["build_property.RazorLangVersion"] = "7.0"; + }); + + // Act + var result = RunGenerator(compilation!, ref driver, out compilation); + + // Assert + Assert.Empty(result.Diagnostics); + Assert.Equal(4, result.GeneratedSources.Length); + await VerifyRazorPageMatchesBaselineAsync(compilation, "Views_Home_Index"); + } +} diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTagHelperTests.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTagHelperTests.cs index 4e670933787..5191ba3d19d 100644 --- a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTagHelperTests.cs +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTagHelperTests.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Test.Common; using Xunit; namespace Microsoft.NET.Sdk.Razor.SourceGenerators; @@ -88,4 +89,107 @@ public string Invoke(string text, int number, bool flag) result.VerifyOutputsMatchBaseline(); await VerifyRazorPageMatchesBaselineAsync(compilation, "Views_Home_Index"); } + + [Fact, WorkItem("https://github.com/dotnet/razor/issues/8718")] + public async Task ComponentAndTagHelper() + { + // Arrange + var project = CreateTestProject(new() + { + ["Views/Home/Index.cshtml"] = """ + @addTagHelper *, TestProject + + custom tag helper + """, + ["Shared/EmailTagHelper.razor"] = """ + @inherits ComponentAndTagHelper + @code { + public string? Mail { get; set; } + } + """, + }, new() + { + ["EmailTagHelper.cs"] = """ + using Microsoft.AspNetCore.Razor.TagHelpers; + namespace MyApp.Shared; + + public abstract class ComponentAndTagHelper : TagHelper + { + protected abstract void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder); + } + + public partial class EmailTagHelper : ComponentAndTagHelper + { + public override void Process(TagHelperContext context, TagHelperOutput output) + { + output.TagName = "a"; + output.Attributes.SetAttribute("href", $"mailto:{Mail}"); + } + } + """ + }); + var compilation = await project.GetCompilationAsync(); + var driver = await GetDriverAsync(project); + + // Act + var result = RunGenerator(compilation!, ref driver, out compilation); + + // Assert + Assert.Empty(result.Diagnostics); + Assert.Equal(2, result.GeneratedSources.Length); + await VerifyRazorPageMatchesBaselineAsync(compilation, "Views_Home_Index"); + } + + [Fact, WorkItem("https://github.com/dotnet/razor/issues/8718")] + public async Task ComponentAndTagHelper_HtmlTargetElement() + { + // Arrange + var project = CreateTestProject(new() + { + ["Views/Home/Index.cshtml"] = """ + @addTagHelper *, TestProject + + inside email + inside mail + """, + ["Shared/EmailTagHelper.razor"] = """ + @using Microsoft.AspNetCore.Razor.TagHelpers; + @attribute [HtmlTargetElement("mail")] + @inherits ComponentAndTagHelper + @code { + public string? Mail { get; set; } + } + """, + }, new() + { + ["EmailTagHelper.cs"] = """ + using Microsoft.AspNetCore.Razor.TagHelpers; + namespace MyApp.Shared; + + public abstract class ComponentAndTagHelper : TagHelper + { + protected abstract void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder); + } + + public partial class EmailTagHelper : ComponentAndTagHelper + { + public override void Process(TagHelperContext context, TagHelperOutput output) + { + output.TagName = "a"; + output.Attributes.SetAttribute("href", $"mailto:{Mail}"); + } + } + """ + }); + var compilation = await project.GetCompilationAsync(); + var driver = await GetDriverAsync(project); + + // Act + var result = RunGenerator(compilation!, ref driver, out compilation); + + // Assert + Assert.Empty(result.Diagnostics); + Assert.Equal(2, result.GeneratedSources.Length); + await VerifyRazorPageMatchesBaselineAsync(compilation, "Views_Home_Index"); + } } diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs index caf1a74d5ea..891c7433a86 100644 --- a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.CodeAnalysis; @@ -60,6 +61,42 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components. Assert.Single(result.GeneratedSources); } + [Fact, WorkItem("https://github.com/dotnet/razor/issues/8610")] + public async Task SourceGenerator_RazorFiles_UsingAlias_NestedClass() + { + // Arrange + var project = CreateTestProject(new() + { + ["Pages/Index.razor"] = """ + @code { + public class MyModel { } + } + """, + ["Shared/MyComponent.razor"] = """ + @using MyAlias = Pages.Index.MyModel; + + + + @code { + [Parameter] + public MyAlias? Data { get; set; } + } + """, + }); + var compilation = await project.GetCompilationAsync(); + var driver = await GetDriverAsync(project, options => + { + options.TestGlobalOptions["build_property.RazorLangVersion"] = "7.0"; + }); + + // Act + var result = RunGenerator(compilation!, ref driver); + + // Assert + Assert.Empty(result.Diagnostics); + Assert.Equal(2, result.GeneratedSources.Length); + } + [Fact] public async Task SourceGeneratorEvents_RazorFiles_Works() { @@ -127,41 +164,74 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components. Assert.Collection(eventListener.Events, e => Assert.Equal("ComputeRazorSourceGeneratorOptions", e.EventName), - e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Index.razor"), - e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Index.razor"), - e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("GenerateDeclarationCodeStart", "Pages/Index.razor"), - e => e.AssertSingleItem("GenerateDeclarationCodeStop", "Pages/Index.razor"), - e => e.AssertSingleItem("GenerateDeclarationCodeStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("GenerateDeclarationCodeStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStart", "Pages/Index.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStop", "Pages/Index.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStop", "Pages/Counter.razor"), + e => + { + Assert.Equal("GenerateDeclarationCodeStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.razor", file); + }, + e => + { + Assert.Equal("GenerateDeclarationCodeStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.razor", file); + }, + e => + { + Assert.Equal("GenerateDeclarationCodeStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("GenerateDeclarationCodeStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName), e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName), e => Assert.Equal("DiscoverTagHelpersFromReferencesStart", e.EventName), e => Assert.Equal("DiscoverTagHelpersFromReferencesStop", e.EventName), - e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Index.razor"), - e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Index.razor"), - e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Pages/Index.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Pages/Index.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_razor.g.cs"), - e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs") - ); + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("AddSyntaxTrees", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("Pages_Index_razor.g.cs", file); + }, + e => + { + Assert.Equal("AddSyntaxTrees", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("Pages_Counter_razor.g.cs", file); + }); } [Fact] - public async Task IncrementalCompilation_DoesNotReExecuteSteps_WhenRazorFilesAreUnchanged() + public async Task IncrementalCompilation_DoesNotReexecuteSteps_WhenRazorFilesAreUnchanged() { // Arrange using var eventListener = new RazorEventListener(); @@ -343,18 +413,36 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components. Assert.Equal(2, result.GeneratedSources.Length); Assert.Collection(eventListener.Events, - e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("GenerateDeclarationCodeStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("GenerateDeclarationCodeStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs") - ); + e => + { + Assert.Equal("GenerateDeclarationCodeStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("GenerateDeclarationCodeStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("AddSyntaxTrees", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("Pages_Counter_razor.g.cs", file); + }); } [Fact] @@ -447,13 +535,8 @@ public class Person Assert.Equal(2, result.GeneratedSources.Length); Assert.Collection(eventListener.Events, - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStart", "Pages/Index.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStop", "Pages/Index.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStop", "Pages/Counter.razor"), - e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName), - e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName) - ); + e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName), + e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName)); } [Fact] @@ -551,8 +634,6 @@ public class Person }", Encoding.UTF8)).Project; compilation = await project.GetCompilationAsync(); - eventListener.Events.Clear(); - result = RunGenerator(compilation!, ref driver, expectedDiagnostics) .VerifyOutputsMatch(result); @@ -560,13 +641,8 @@ public class Person Assert.Equal(2, result.GeneratedSources.Length); Assert.Collection(eventListener.Events, - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStart", "Pages/Index.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStop", "Pages/Index.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStop", "Pages/Counter.razor"), e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName), - e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName) - ); + e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName)); } [Fact] @@ -711,20 +787,38 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components. Assert.Equal(2, result.GeneratedSources.Length); Assert.Collection(eventListener.Events, - e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("GenerateDeclarationCodeStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("GenerateDeclarationCodeStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs") - ); + e => + { + Assert.Equal("GenerateDeclarationCodeStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("GenerateDeclarationCodeStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName), + e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName), + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("AddSyntaxTrees", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("Pages_Counter_razor.g.cs", file); + }); } [Fact] @@ -873,24 +967,50 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components. Assert.Equal(2, result.GeneratedSources.Length); Assert.Collection(eventListener.Events, - e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("GenerateDeclarationCodeStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("GenerateDeclarationCodeStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Pages/Index.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Pages/Index.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Counter_razor.g.cs") - ); + e => + { + Assert.Equal("GenerateDeclarationCodeStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("GenerateDeclarationCodeStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName), + e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName), + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("AddSyntaxTrees", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("Pages_Counter_razor.g.cs", file); + }); } [Fact] @@ -1021,24 +1141,40 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components. Assert.Equal(2, result.GeneratedSources.Length); Assert.Collection(eventListener.Events, - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStart", "Pages/Index.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStop", "Pages/Index.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("DiscoverTagHelpersFromComponentStop", "Pages/Counter.razor"), e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName), e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName), e => Assert.Equal("DiscoverTagHelpersFromReferencesStart", e.EventName), e => Assert.Equal("DiscoverTagHelpersFromReferencesStop", e.EventName), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Pages/Index.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Pages/Index.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Pages/Counter.razor"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Pages/Counter.razor"), - e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_razor.g.cs") - ); + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Counter.razor", file); + }, + e => + { + Assert.Equal("AddSyntaxTrees", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("Pages_Index_razor.g.cs", file); + }); // Verify caching eventListener.Events.Clear(); @@ -1190,29 +1326,46 @@ internal sealed class Views_Shared__Layout : global::Microsoft.AspNetCore.Mvc.Ra Assert.Collection(eventListener.Events, e => Assert.Equal("ComputeRazorSourceGeneratorOptions", e.EventName), - e => e.AssertSingleItem("ParseRazorDocumentStart", "Pages/Index.cshtml"), - e => e.AssertSingleItem("ParseRazorDocumentStop", "Pages/Index.cshtml"), - e => e.AssertSingleItem("ParseRazorDocumentStart", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("ParseRazorDocumentStop", "Views/Shared/_Layout.cshtml"), e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName), e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName), e => Assert.Equal("DiscoverTagHelpersFromReferencesStart", e.EventName), e => Assert.Equal("DiscoverTagHelpersFromReferencesStop", e.EventName), - e => e.AssertSingleItem("RewriteTagHelpersStart", "Pages/Index.cshtml"), - e => e.AssertSingleItem("RewriteTagHelpersStop", "Pages/Index.cshtml"), - e => e.AssertSingleItem("RewriteTagHelpersStart", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("RewriteTagHelpersStop", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.cshtml"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.cshtml"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Pages/Index.cshtml"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Pages/Index.cshtml"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_cshtml.g.cs"), - e => e.AssertSingleItem("AddSyntaxTrees", "Views_Shared__Layout_cshtml.g.cs") - ); + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.cshtml", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.cshtml", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Views/Shared/_Layout.cshtml", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Views/Shared/_Layout.cshtml", file); + }, + e => + { + Assert.Equal("AddSyntaxTrees", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("Pages_Index_cshtml.g.cs", file); + }, + e => + { + Assert.Equal("AddSyntaxTrees", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("Views_Shared__Layout_cshtml.g.cs", file); + }); } [Fact] @@ -1404,16 +1557,24 @@ internal sealed class Views_Shared__Layout : global::Microsoft.AspNetCore.Mvc.Ra Assert.Equal(2, result.GeneratedSources.Length); Assert.Collection(eventListener.Events, - e => e.AssertSingleItem("ParseRazorDocumentStart", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("ParseRazorDocumentStop", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("RewriteTagHelpersStart", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("RewriteTagHelpersStop", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("AddSyntaxTrees", "Views_Shared__Layout_cshtml.g.cs") - ); + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Views/Shared/_Layout.cshtml", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Views/Shared/_Layout.cshtml", file); + }, + e => + { + Assert.Equal("AddSyntaxTrees", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("Views_Shared__Layout_cshtml.g.cs", file); + }); } [Fact] @@ -1572,8 +1733,8 @@ public class Person Assert.Collection(eventListener.Events, e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName), - e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName) - ); + e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName)); + } [Fact] @@ -1723,92 +1884,7 @@ public override void Process(TagHelperContext context, TagHelperOutput output) }", Encoding.UTF8)).Project; compilation = await project.GetCompilationAsync(); - result = RunGenerator(compilation!, ref driver) - .VerifyOutputsMatch(result, (0, @" -#pragma checksum ""Pages/Index.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""5d59ecd7b7cf7355d7f60234988be34b81a8b614"" -// -#pragma warning disable 1591 -[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Pages_Index), @""mvc.1.0.view"", @""/Pages/Index.cshtml"")] -namespace AspNetCoreGeneratedDocument -{ - #line hidden - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Mvc; - using Microsoft.AspNetCore.Mvc.Rendering; - using Microsoft.AspNetCore.Mvc.ViewFeatures; - [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute(""Identifier"", ""/Pages/Index.cshtml"")] - [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute] - #nullable restore - internal sealed class Pages_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage - #nullable disable - { - #line hidden - #pragma warning disable 0649 - private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext __tagHelperExecutionContext; - #pragma warning restore 0649 - private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner __tagHelperRunner = new global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner(); - #pragma warning disable 0169 - private string __tagHelperStringValueBuffer; - #pragma warning restore 0169 - private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager __backed__tagHelperScopeManager = null; - private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager __tagHelperScopeManager - { - get - { - if (__backed__tagHelperScopeManager == null) - { - __backed__tagHelperScopeManager = new global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager(StartTagHelperWritingScope, EndTagHelperWritingScope); - } - return __backed__tagHelperScopeManager; - } - } - private global::MyApp.HeaderTagHelper __MyApp_HeaderTagHelper; - #pragma warning disable 1998 - public async override global::System.Threading.Tasks.Task ExecuteAsync() - { - WriteLiteral(""\r\n""); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin(""h2"", global::Microsoft.AspNetCore.Razor.TagHelpers.TagMode.StartTagAndEndTag, ""5d59ecd7b7cf7355d7f60234988be34b81a8b6142529"", async() => { - WriteLiteral(""Hello world""); - } - ); - __MyApp_HeaderTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__MyApp_HeaderTagHelper); - await __tagHelperRunner.RunAsync(__tagHelperExecutionContext); - if (!__tagHelperExecutionContext.Output.IsContentModified) - { - await __tagHelperExecutionContext.SetOutputContentAsync(); - } - Write(__tagHelperExecutionContext.Output); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - } - #pragma warning restore 1998 - #nullable restore - [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] - public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; } = default!; - #nullable disable - #nullable restore - [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] - public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; } = default!; - #nullable disable - #nullable restore - [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] - public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; } = default!; - #nullable disable - #nullable restore - [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] - public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; } = default!; - #nullable disable - #nullable restore - [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] - public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html { get; private set; } = default!; - #nullable disable - } -} -#pragma warning restore 1591 -")); + result = RunGenerator(compilation!, ref driver); Assert.Empty(result.Diagnostics); Assert.Equal(2, result.GeneratedSources.Length); @@ -1816,14 +1892,36 @@ internal sealed class Pages_Index : global::Microsoft.AspNetCore.Mvc.Razor.Razor Assert.Collection(eventListener.Events, e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName), e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Pages/Index.cshtml"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Pages/Index.cshtml"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStart", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("CheckAndRewriteTagHelpersStop", "Views/Shared/_Layout.cshtml"), - e => e.AssertSingleItem("RazorCodeGenerateStart", "Pages/Index.cshtml"), - e => e.AssertSingleItem("RazorCodeGenerateStop", "Pages/Index.cshtml"), - e => e.AssertSingleItem("AddSyntaxTrees", "Pages_Index_cshtml.g.cs") - ); + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.cshtml", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Pages/Index.cshtml", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStart", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Views/Shared/_Layout.cshtml", file); + }, + e => + { + Assert.Equal("RazorCodeGenerateStop", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("/Views/Shared/_Layout.cshtml", file); + }, + e => + { + Assert.Equal("AddSyntaxTrees", e.EventName); + var file = Assert.Single(e.Payload); + Assert.Equal("Pages_Index_cshtml.g.cs", file); + }); } [Fact] @@ -2490,60 +2588,5 @@ public async Task SourceGenerator_EmptyTargetPath(string targetPath) Assert.Empty(result.GeneratedSources); } - - [Fact] - public async Task SourceGenerator_Class_Inside_CodeBlock() - { - var project = CreateTestProject(new() - { - ["Component.Razor"] = -""" -

Hello world

- -@code -{ - public class X {} -} -"""}); - - var compilation = await project.GetCompilationAsync(); - var driver = await GetDriverAsync(project); - - var result = RunGenerator(compilation!, ref driver).VerifyPageOutput( -@"#pragma checksum ""Component.Razor"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""20b14071a74e1fd554d7b3dff6ff41722270ebee"" -// -#pragma warning disable 1591 -namespace MyApp -{ - #line hidden - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Components; - public partial class Component : global::Microsoft.AspNetCore.Components.ComponentBase - { - #pragma warning disable 1998 - protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder) - { - __builder.AddMarkupContent(0, ""

Hello world

""); - } - #pragma warning restore 1998 -#nullable restore -#line 4 ""Component.Razor"" - - public class X {} - -#line default -#line hidden -#nullable disable - } -} -#pragma warning restore 1591 -"); - - Assert.Empty(result.Diagnostics); - Assert.Single(result.GeneratedSources); - } } } diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTestsBase.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTestsBase.cs index 0639753e1e7..4fc35d77065 100644 --- a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTestsBase.cs +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTestsBase.cs @@ -18,8 +18,10 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Rendering; @@ -43,9 +45,9 @@ namespace Microsoft.NET.Sdk.Razor.SourceGenerators; [Collection(nameof(RazorSourceGenerator))] public abstract class RazorSourceGeneratorTestsBase { - protected static async ValueTask GetDriverAsync(Project project) + protected static async ValueTask GetDriverAsync(Project project, Action? configureGlobalOptions = null) { - var (driver, _) = await GetDriverWithAdditionalTextAsync(project); + var (driver, _) = await GetDriverWithAdditionalTextAsync(project, configureGlobalOptions); return driver; } @@ -134,12 +136,27 @@ protected static async Task RenderRazorPageAsync(Compilation compilation // Create ViewContext. var appBuilder = WebApplication.CreateBuilder(); - appBuilder.Services.AddMvc().AddApplicationPart(assembly); + appBuilder.Services.AddMvc().ConfigureApplicationPartManager(manager => + { + var partFactory = new ConsolidatedAssemblyApplicationPartFactory(); + foreach (var applicationPart in partFactory.GetApplicationParts(assembly)) + { + manager.ApplicationParts.Add(applicationPart); + } + }); var app = appBuilder.Build(); var httpContext = new DefaultHttpContext { RequestServices = app.Services }; + var requestFeature = new HttpRequestFeature + { + Method = HttpMethods.Get, + Protocol = HttpProtocol.Http2, + Scheme = "http" + }; + requestFeature.Headers.Host = "localhost"; + httpContext.Features.Set(requestFeature); var actionContext = new ActionContext( httpContext, new AspNetCore.Routing.RouteData(), @@ -158,7 +175,10 @@ protected static async Task RenderRazorPageAsync(Compilation compilation page.HtmlEncoder = HtmlEncoder.Default; // Render the page. - await page.ExecuteAsync(); + var view = ActivatorUtilities.CreateInstance(app.Services, + /* IReadOnlyList viewStartPages */ Array.Empty(), + /* IRazorPage razorPage */ page); + await view.RenderAsync(viewContext); assemblyLoadContext.Unload(); @@ -469,11 +489,4 @@ private static string TrimChecksum(string text) Assert.StartsWith("#pragma", trimmed); return trimmed.Substring(trimmed.IndexOf('\n') + 1); } - - public static void AssertSingleItem(this RazorEventListener.RazorEvent e, string expectedEventName, string expectedFileName) - { - Assert.Equal(expectedEventName, e.EventName); - var file = Assert.Single(e.Payload); - Assert.Equal(expectedFileName, file); - } } diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/ComponentInheritsFromComponent.html b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/ComponentInheritsFromComponent.html new file mode 100644 index 00000000000..e3c00fbe3e8 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/ComponentInheritsFromComponent.html @@ -0,0 +1,2 @@ +Hello from Component1 +Hello from Derived \ No newline at end of file diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/ComponentInheritsFromComponent/Views_Home_Index.html b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/ComponentInheritsFromComponent/Views_Home_Index.html new file mode 100644 index 00000000000..e3c00fbe3e8 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/ComponentInheritsFromComponent/Views_Home_Index.html @@ -0,0 +1,2 @@ +Hello from Component1 +Hello from Derived \ No newline at end of file diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass.html b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass.html new file mode 100644 index 00000000000..c7d1331f403 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass.html @@ -0,0 +1,2 @@ + +Value: 43 \ No newline at end of file diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass/Views_Home_Index.html b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass/Views_Home_Index.html new file mode 100644 index 00000000000..c7d1331f403 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass/Views_Home_Index.html @@ -0,0 +1,2 @@ + +Value: 43 \ No newline at end of file diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass_NoBaseInCSharp.html b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass_NoBaseInCSharp.html new file mode 100644 index 00000000000..c7d1331f403 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass_NoBaseInCSharp.html @@ -0,0 +1,2 @@ + +Value: 43 \ No newline at end of file diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass_NoBaseInCSharp/Views_Home_Index.html b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass_NoBaseInCSharp/Views_Home_Index.html new file mode 100644 index 00000000000..c7d1331f403 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/PartialClass_NoBaseInCSharp/Views_Home_Index.html @@ -0,0 +1,2 @@ + +Value: 43 \ No newline at end of file diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper.html b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper.html new file mode 100644 index 00000000000..1fd9163e620 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper.html @@ -0,0 +1,2 @@ + +custom tag helper \ No newline at end of file diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper_HtmlTargetElement.html b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper_HtmlTargetElement.html new file mode 100644 index 00000000000..5c6739edc45 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper_HtmlTargetElement.html @@ -0,0 +1,3 @@ + +inside email +inside mail \ No newline at end of file From db54a0ff48bf1253d2ad4ced3854e7bf3575a4a0 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 25 May 2023 12:16:01 -0700 Subject: [PATCH 2/3] Update baseline. --- .../RazorSourceGeneratorTests.cs | 4 ++-- .../RazorSourceGeneratorTestsBase.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs index c17231add8b..891c7433a86 100644 --- a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs @@ -770,7 +770,7 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components. #pragma warning restore 1998 #nullable restore #line 7 ""Pages/Counter.razor"" - + private int count; public void Click() => count++; @@ -948,7 +948,7 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components. #pragma warning restore 1998 #nullable restore #line 7 ""Pages/Counter.razor"" - + private int count; public void Click() => count++; diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTestsBase.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTestsBase.cs index 4fc35d77065..04d48d6b90f 100644 --- a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTestsBase.cs +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTestsBase.cs @@ -474,7 +474,7 @@ public static GeneratorRunResult VerifyOutputsMatch(this GeneratorRunResult actu } else { - Assert.Equal(TrimChecksum(diff), TrimChecksum(actual.GeneratedSources[i].SourceText.ToString())); + AssertEx.EqualOrDiff(TrimChecksum(diff), TrimChecksum(actual.GeneratedSources[i].SourceText.ToString())); } } From f5d5f1b4bbe020a42e032852bd161495d22d1a92 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 31 May 2023 14:33:01 -0700 Subject: [PATCH 3/3] Update baselines --- .../ComponentAndTagHelper/Views_Home_Index.html | 2 ++ .../Views_Home_Index.html | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper/Views_Home_Index.html create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper_HtmlTargetElement/Views_Home_Index.html diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper/Views_Home_Index.html b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper/Views_Home_Index.html new file mode 100644 index 00000000000..1fd9163e620 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper/Views_Home_Index.html @@ -0,0 +1,2 @@ + +custom tag helper \ No newline at end of file diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper_HtmlTargetElement/Views_Home_Index.html b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper_HtmlTargetElement/Views_Home_Index.html new file mode 100644 index 00000000000..5c6739edc45 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorTagHelperTests/ComponentAndTagHelper_HtmlTargetElement/Views_Home_Index.html @@ -0,0 +1,3 @@ + +inside email +inside mail \ No newline at end of file