diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ViewComponentTypeVisitorTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/IsViewComponentTest.cs similarity index 56% rename from src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ViewComponentTypeVisitorTest.cs rename to src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/IsViewComponentTest.cs index ccde499da84..4a897eb263f 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ViewComponentTypeVisitorTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/IsViewComponentTest.cs @@ -1,39 +1,42 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System; -using System.Collections.Generic; using System.Reflection; +using Microsoft.AspNetCore.Razor; +using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Xunit; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X; -public class ViewComponentTypeVisitorTest +public class IsViewComponentTest { - private static readonly Assembly _assembly = typeof(ViewComponentTypeVisitorTest).GetTypeInfo().Assembly; - - private static Compilation Compilation { get; } = TestCompilation.Create(_assembly); + private CSharpCompilation Compilation { get; } // In practice MVC will provide a marker attribute for ViewComponents. To prevent a circular reference between MVC and Razor // we can use a test class as a marker. - private static INamedTypeSymbol TestViewComponentAttributeSymbol { get; } = Compilation.GetTypeByMetadataName(typeof(TestViewComponentAttribute).FullName); - private static INamedTypeSymbol TestNonViewComponentAttributeSymbol { get; } = Compilation.GetTypeByMetadataName(typeof(TestNonViewComponentAttribute).FullName); + private INamedTypeSymbol TestViewComponentAttributeSymbol { get; } + private INamedTypeSymbol TestNonViewComponentAttributeSymbol { get; } + + public IsViewComponentTest() + { + var assembly = typeof(IsViewComponentTest).GetTypeInfo().Assembly; + Compilation = TestCompilation.Create(assembly); + TestViewComponentAttributeSymbol = Compilation.GetTypeByMetadataName(typeof(TestViewComponentAttribute).FullName).AssumeNotNull(); + TestNonViewComponentAttributeSymbol = Compilation.GetTypeByMetadataName(typeof(TestNonViewComponentAttribute).FullName).AssumeNotNull(); + } [Fact] public void IsViewComponent_PlainViewComponent_ReturnsTrue() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Valid_PlainViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.True(isViewComponent); @@ -43,14 +46,11 @@ public void IsViewComponent_PlainViewComponent_ReturnsTrue() public void IsViewComponent_DecoratedViewComponent_ReturnsTrue() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Valid_DecoratedVC).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.True(isViewComponent); @@ -60,14 +60,11 @@ public void IsViewComponent_DecoratedViewComponent_ReturnsTrue() public void IsViewComponent_InheritedViewComponent_ReturnsTrue() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Valid_InheritedVC).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.True(isViewComponent); @@ -77,14 +74,11 @@ public void IsViewComponent_InheritedViewComponent_ReturnsTrue() public void IsViewComponent_AbstractViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_AbstractViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -94,14 +88,11 @@ public void IsViewComponent_AbstractViewComponent_ReturnsFalse() public void IsViewComponent_GenericViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_GenericViewComponent<>).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -111,14 +102,11 @@ public void IsViewComponent_GenericViewComponent_ReturnsFalse() public void IsViewComponent_InternalViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_InternalViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -128,14 +116,11 @@ public void IsViewComponent_InternalViewComponent_ReturnsFalse() public void IsViewComponent_DecoratedNonViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_DecoratedViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -145,14 +130,11 @@ public void IsViewComponent_DecoratedNonViewComponent_ReturnsFalse() public void IsViewComponent_InheritedNonViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_InheritedViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ViewComponentTypeVisitorTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/IsViewComponentTest.cs similarity index 56% rename from src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ViewComponentTypeVisitorTest.cs rename to src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/IsViewComponentTest.cs index ba4af3ae78f..ed210515159 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ViewComponentTypeVisitorTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/IsViewComponentTest.cs @@ -1,40 +1,42 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System; -using System.Collections.Generic; using System.Reflection; +using Microsoft.AspNetCore.Razor; +using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Xunit; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X; -public class ViewComponentTypeVisitorTest +public class IsViewComponentTest { - private static readonly Assembly _assembly = typeof(ViewComponentTypeVisitorTest).GetTypeInfo().Assembly; - - private static CSharpCompilation Compilation { get; } = TestCompilation.Create(_assembly); + private CSharpCompilation Compilation { get; } // In practice MVC will provide a marker attribute for ViewComponents. To prevent a circular reference between MVC and Razor // we can use a test class as a marker. - private static INamedTypeSymbol TestViewComponentAttributeSymbol { get; } = Compilation.GetTypeByMetadataName(typeof(TestViewComponentAttribute).FullName); - private static INamedTypeSymbol TestNonViewComponentAttributeSymbol { get; } = Compilation.GetTypeByMetadataName(typeof(TestNonViewComponentAttribute).FullName); + private INamedTypeSymbol TestViewComponentAttributeSymbol { get; } + private INamedTypeSymbol TestNonViewComponentAttributeSymbol { get; } + + public IsViewComponentTest() + { + var assembly = typeof(IsViewComponentTest).GetTypeInfo().Assembly; + Compilation = TestCompilation.Create(assembly); + TestViewComponentAttributeSymbol = Compilation.GetTypeByMetadataName(typeof(TestViewComponentAttribute).FullName).AssumeNotNull(); + TestNonViewComponentAttributeSymbol = Compilation.GetTypeByMetadataName(typeof(TestNonViewComponentAttribute).FullName).AssumeNotNull(); + } [Fact] public void IsViewComponent_PlainViewComponent_ReturnsTrue() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Valid_PlainViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.True(isViewComponent); @@ -44,14 +46,11 @@ public void IsViewComponent_PlainViewComponent_ReturnsTrue() public void IsViewComponent_DecoratedViewComponent_ReturnsTrue() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Valid_DecoratedVC).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.True(isViewComponent); @@ -61,14 +60,11 @@ public void IsViewComponent_DecoratedViewComponent_ReturnsTrue() public void IsViewComponent_InheritedViewComponent_ReturnsTrue() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Valid_InheritedVC).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.True(isViewComponent); @@ -78,14 +74,11 @@ public void IsViewComponent_InheritedViewComponent_ReturnsTrue() public void IsViewComponent_AbstractViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_AbstractViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -95,14 +88,11 @@ public void IsViewComponent_AbstractViewComponent_ReturnsFalse() public void IsViewComponent_GenericViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_GenericViewComponent<>).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -112,14 +102,11 @@ public void IsViewComponent_GenericViewComponent_ReturnsFalse() public void IsViewComponent_InternalViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_InternalViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -129,14 +116,11 @@ public void IsViewComponent_InternalViewComponent_ReturnsFalse() public void IsViewComponent_DecoratedNonViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_DecoratedViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -146,14 +130,11 @@ public void IsViewComponent_DecoratedNonViewComponent_ReturnsFalse() public void IsViewComponent_InheritedNonViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_InheritedViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ViewComponentTypeVisitorTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/IsViewComponentTest.cs similarity index 56% rename from src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ViewComponentTypeVisitorTest.cs rename to src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/IsViewComponentTest.cs index db7f6659681..2a7ed874a28 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ViewComponentTypeVisitorTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/IsViewComponentTest.cs @@ -1,40 +1,42 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System; -using System.Collections.Generic; using System.Reflection; +using Microsoft.AspNetCore.Razor; +using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Xunit; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions; -public class ViewComponentTypeVisitorTest +public class IsViewComponentTest { - private static readonly Assembly _assembly = typeof(ViewComponentTypeVisitorTest).GetTypeInfo().Assembly; - - private static CSharpCompilation Compilation { get; } = TestCompilation.Create(_assembly); + private CSharpCompilation Compilation { get; } // In practice MVC will provide a marker attribute for ViewComponents. To prevent a circular reference between MVC and Razor // we can use a test class as a marker. - private static INamedTypeSymbol TestViewComponentAttributeSymbol { get; } = Compilation.GetTypeByMetadataName(typeof(TestViewComponentAttribute).FullName); - private static INamedTypeSymbol TestNonViewComponentAttributeSymbol { get; } = Compilation.GetTypeByMetadataName(typeof(TestNonViewComponentAttribute).FullName); + private INamedTypeSymbol TestViewComponentAttributeSymbol { get; } + private INamedTypeSymbol TestNonViewComponentAttributeSymbol { get; } + + public IsViewComponentTest() + { + var assembly = typeof(IsViewComponentTest).GetTypeInfo().Assembly; + Compilation = TestCompilation.Create(assembly); + TestViewComponentAttributeSymbol = Compilation.GetTypeByMetadataName(typeof(TestViewComponentAttribute).FullName).AssumeNotNull(); + TestNonViewComponentAttributeSymbol = Compilation.GetTypeByMetadataName(typeof(TestNonViewComponentAttribute).FullName).AssumeNotNull(); + } [Fact] public void IsViewComponent_PlainViewComponent_ReturnsTrue() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Valid_PlainViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.True(isViewComponent); @@ -44,14 +46,11 @@ public void IsViewComponent_PlainViewComponent_ReturnsTrue() public void IsViewComponent_DecoratedViewComponent_ReturnsTrue() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Valid_DecoratedVC).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.True(isViewComponent); @@ -61,14 +60,11 @@ public void IsViewComponent_DecoratedViewComponent_ReturnsTrue() public void IsViewComponent_InheritedViewComponent_ReturnsTrue() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Valid_InheritedVC).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.True(isViewComponent); @@ -78,14 +74,11 @@ public void IsViewComponent_InheritedViewComponent_ReturnsTrue() public void IsViewComponent_AbstractViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_AbstractViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -95,14 +88,11 @@ public void IsViewComponent_AbstractViewComponent_ReturnsFalse() public void IsViewComponent_GenericViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_GenericViewComponent<>).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -112,14 +102,11 @@ public void IsViewComponent_GenericViewComponent_ReturnsFalse() public void IsViewComponent_InternalViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_InternalViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -129,14 +116,11 @@ public void IsViewComponent_InternalViewComponent_ReturnsFalse() public void IsViewComponent_DecoratedNonViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_DecoratedViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); @@ -146,14 +130,11 @@ public void IsViewComponent_DecoratedNonViewComponent_ReturnsFalse() public void IsViewComponent_InheritedNonViewComponent_ReturnsFalse() { // Arrange - var testVisitor = new ViewComponentTypeVisitor( - TestViewComponentAttributeSymbol, - TestNonViewComponentAttributeSymbol, - new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_InheritedViewComponent).FullName); + Assert.NotNull(tagHelperSymbol); // Act - var isViewComponent = testVisitor.IsViewComponent(tagHelperSymbol); + var isViewComponent = tagHelperSymbol.IsViewComponent(TestViewComponentAttributeSymbol, TestNonViewComponentAttributeSymbol); // Assert Assert.False(isViewComponent); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/BindTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/BindTagHelperDescriptorProvider.cs index b15f54d4615..dc14b840fbe 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/BindTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/BindTagHelperDescriptorProvider.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Components; @@ -17,7 +18,7 @@ internal sealed class BindTagHelperDescriptorProvider() : TagHelperDescriptorPro { private static readonly Lazy s_fallbackBindTagHelper = new(CreateFallbackBindTagHelper); - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); @@ -94,7 +95,8 @@ public override void Execute(TagHelperDescriptorProviderContext context) return; } - if (context.TargetSymbol is { } targetSymbol && !SymbolEqualityComparer.Default.Equals(targetSymbol, bindMethods.ContainingAssembly)) + if (context.TargetAssembly is { } targetAssembly && + !SymbolEqualityComparer.Default.Equals(targetAssembly, bindMethods.ContainingAssembly)) { return; } @@ -114,7 +116,7 @@ public override void Execute(TagHelperDescriptorProviderContext context) // We want to walk the compilation and its references, not the target symbol. var collector = new Collector( compilation, bindElementAttribute, bindInputElementAttribute); - collector.Collect(context); + collector.Collect(context, cancellationToken); } private static TagHelperDescriptor CreateFallbackBindTagHelper() @@ -214,84 +216,29 @@ private static TagHelperDescriptor CreateFallbackBindTagHelper() } private class Collector( - Compilation compilation, INamedTypeSymbol bindElementAttribute, INamedTypeSymbol bindInputElementAttribute) - : TagHelperCollector(compilation, targetSymbol: null) + Compilation compilation, + INamedTypeSymbol bindElementAttribute, + INamedTypeSymbol bindInputElementAttribute) + : TagHelperCollector(compilation, targetAssembly: null) { - protected override void Collect(ISymbol symbol, ICollection results) - { - using var _ = ListPool.GetPooledObject(out var types); - var visitor = new BindElementDataVisitor(types); - - visitor.Visit(symbol); - - foreach (var type in types) - { - // Not handling duplicates here for now since we're the primary ones extending this. - // If we see users adding to the set of 'bind' constructs we will want to add deduplication - // and potentially diagnostics. - foreach (var attribute in type.GetAttributes()) - { - var constructorArguments = attribute.ConstructorArguments; - - TagHelperDescriptor? tagHelper = null; - - // For case #2 & #3 we have a whole bunch of attribute entries on BindMethods that we can use - // to data-drive the definitions of these tag helpers. - - // We need to check the constructor argument length here, because this can show up as 0 - // if the language service fails to initialize. This is an invalid case, so skip it. - if (constructorArguments.Length == 4 && SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, bindElementAttribute)) - { - tagHelper = CreateElementBindTagHelper( - typeName: type.GetDefaultDisplayString(), - typeNamespace: type.ContainingNamespace.GetFullName(), - typeNameIdentifier: type.Name, - element: (string?)constructorArguments[0].Value, - typeAttribute: null, - suffix: (string?)constructorArguments[1].Value, - valueAttribute: (string?)constructorArguments[2].Value, - changeAttribute: (string?)constructorArguments[3].Value); - } - else if (constructorArguments.Length == 4 && SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, bindInputElementAttribute)) - { - tagHelper = CreateElementBindTagHelper( - typeName: type.GetDefaultDisplayString(), - typeNamespace: type.ContainingNamespace.GetFullName(), - typeNameIdentifier: type.Name, - element: "input", - typeAttribute: (string?)constructorArguments[0].Value, - suffix: (string?)constructorArguments[1].Value, - valueAttribute: (string?)constructorArguments[2].Value, - changeAttribute: (string?)constructorArguments[3].Value); - } - else if (constructorArguments.Length == 6 && SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, bindInputElementAttribute)) - { - tagHelper = CreateElementBindTagHelper( - typeName: type.GetDefaultDisplayString(), - typeNamespace: type.ContainingNamespace.GetFullName(), - typeNameIdentifier: type.Name, - element: "input", - typeAttribute: (string?)constructorArguments[0].Value, - suffix: (string?)constructorArguments[1].Value, - valueAttribute: (string?)constructorArguments[2].Value, - changeAttribute: (string?)constructorArguments[3].Value, - isInvariantCulture: (bool?)constructorArguments[4].Value ?? false, - format: (string?)constructorArguments[5].Value); - } + protected override bool IsCandidateType(INamedTypeSymbol types) + => types.DeclaredAccessibility == Accessibility.Public && + types.Name == "BindAttributes"; - if (tagHelper is not null) - { - results.Add(tagHelper); - } - } - } + protected override void Collect(IAssemblySymbol assembly, ICollection results, CancellationToken cancellationToken) + { + // First, collect the initial set of tag helpers from this assembly. This calls + // the Collect(INamedTypeSymbol, ...) overload below for cases #2 & #3. + base.Collect(assembly, results, cancellationToken); - // For case #4 we look at the tag helpers that were already created corresponding to components + // Then, for case #4 we look at the tag helpers that were already created corresponding to components // and pattern match on properties. using var componentBindTagHelpers = new PooledArrayBuilder(capacity: results.Count); foreach (var tagHelper in results) { + cancellationToken.ThrowIfCancellationRequested(); + AddComponentBindTagHelpers(tagHelper, ref componentBindTagHelpers.AsRef()); } @@ -301,6 +248,71 @@ protected override void Collect(ISymbol symbol, ICollection } } + protected override void Collect( + INamedTypeSymbol type, + ICollection results, + CancellationToken cancellationToken) + { + // Not handling duplicates here for now since we're the primary ones extending this. + // If we see users adding to the set of 'bind' constructs we will want to add deduplication + // and potentially diagnostics. + foreach (var attribute in type.GetAttributes()) + { + var constructorArguments = attribute.ConstructorArguments; + + TagHelperDescriptor? tagHelper = null; + + // For case #2 & #3 we have a whole bunch of attribute entries on BindMethods that we can use + // to data-drive the definitions of these tag helpers. + + // We need to check the constructor argument length here, because this can show up as 0 + // if the language service fails to initialize. This is an invalid case, so skip it. + if (constructorArguments.Length == 4 && SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, bindElementAttribute)) + { + tagHelper = CreateElementBindTagHelper( + typeName: type.GetDefaultDisplayString(), + typeNamespace: type.ContainingNamespace.GetFullName(), + typeNameIdentifier: type.Name, + element: (string?)constructorArguments[0].Value, + typeAttribute: null, + suffix: (string?)constructorArguments[1].Value, + valueAttribute: (string?)constructorArguments[2].Value, + changeAttribute: (string?)constructorArguments[3].Value); + } + else if (constructorArguments.Length == 4 && SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, bindInputElementAttribute)) + { + tagHelper = CreateElementBindTagHelper( + typeName: type.GetDefaultDisplayString(), + typeNamespace: type.ContainingNamespace.GetFullName(), + typeNameIdentifier: type.Name, + element: "input", + typeAttribute: (string?)constructorArguments[0].Value, + suffix: (string?)constructorArguments[1].Value, + valueAttribute: (string?)constructorArguments[2].Value, + changeAttribute: (string?)constructorArguments[3].Value); + } + else if (constructorArguments.Length == 6 && SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, bindInputElementAttribute)) + { + tagHelper = CreateElementBindTagHelper( + typeName: type.GetDefaultDisplayString(), + typeNamespace: type.ContainingNamespace.GetFullName(), + typeNameIdentifier: type.Name, + element: "input", + typeAttribute: (string?)constructorArguments[0].Value, + suffix: (string?)constructorArguments[1].Value, + valueAttribute: (string?)constructorArguments[2].Value, + changeAttribute: (string?)constructorArguments[3].Value, + isInvariantCulture: (bool?)constructorArguments[4].Value ?? false, + format: (string?)constructorArguments[5].Value); + } + + if (tagHelper is not null) + { + results.Add(tagHelper); + } + } + } + private static TagHelperDescriptor CreateElementBindTagHelper( string typeName, string typeNamespace, @@ -669,32 +681,5 @@ private static void AddComponentBindTagHelpers(TagHelperDescriptor tagHelper, re results.Add(builder.Build()); } } - - private class BindElementDataVisitor(List results) : SymbolVisitor - { - private readonly List _results = results; - - public override void VisitNamedType(INamedTypeSymbol symbol) - { - if (symbol.DeclaredAccessibility == Accessibility.Public && - symbol.Name == "BindAttributes") - { - _results.Add(symbol); - } - } - - public override void VisitNamespace(INamespaceSymbol symbol) - { - foreach (var member in symbol.GetMembers()) - { - Visit(member); - } - } - - public override void VisitAssembly(IAssemblySymbol symbol) - { - Visit(symbol.GlobalNamespace); - } - } } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/CompilationTagHelperFeature.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/CompilationTagHelperFeature.cs index a8fa37135db..9e52ee57431 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/CompilationTagHelperFeature.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/CompilationTagHelperFeature.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Threading; using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis.CSharp; @@ -14,7 +15,7 @@ public sealed class CompilationTagHelperFeature : RazorEngineFeatureBase, ITagHe private ImmutableArray _providers; private IMetadataReferenceFeature? _referenceFeature; - public IReadOnlyList GetDescriptors() + public IReadOnlyList GetDescriptors(CancellationToken cancellationToken = default) { var compilation = CSharpCompilation.Create("__TagHelpers", references: _referenceFeature?.References); if (!IsValidCompilation(compilation)) @@ -25,9 +26,9 @@ public IReadOnlyList GetDescriptors() var results = new List(); var context = new TagHelperDescriptorProviderContext(compilation, results); - for (var i = 0; i < _providers.Length; i++) + foreach (var provider in _providers) { - _providers[i].Execute(context); + provider.Execute(context, cancellationToken); } return results; diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs index c466aab7518..3f1b93c1369 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Components; @@ -17,56 +18,56 @@ namespace Microsoft.CodeAnalysis.Razor; internal sealed class ComponentTagHelperDescriptorProvider : TagHelperDescriptorProviderBase { - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); var compilation = context.Compilation; - var targetSymbol = context.TargetSymbol; + var targetAssembly = context.TargetAssembly; - var collector = new Collector(compilation, targetSymbol); - collector.Collect(context); + var collector = new Collector(compilation, targetAssembly); + collector.Collect(context, cancellationToken); } - private sealed class Collector(Compilation compilation, ISymbol? targetSymbol) - : TagHelperCollector(compilation, targetSymbol) + private sealed class Collector( + Compilation compilation, + IAssemblySymbol? targetAssembly) + : TagHelperCollector(compilation, targetAssembly) { - protected override void Collect(ISymbol symbol, ICollection results) - { - using var _ = ListPool.GetPooledObject(out var types); - var visitor = new ComponentTypeVisitor(types); - - visitor.Visit(symbol); + protected override bool IsCandidateType(INamedTypeSymbol type) + => ComponentDetectionConventions.IsComponent(type, ComponentsApi.IComponent.MetadataName); - foreach (var type in types) - { - // Components have very simple matching rules. - // 1. The type name (short) matches the tag name. - // 2. The fully qualified name matches the tag name. + protected override void Collect( + INamedTypeSymbol type, + ICollection results, + CancellationToken cancellationToken) + { + // Components have very simple matching rules. + // 1. The type name (short) matches the tag name. + // 2. The fully qualified name matches the tag name. - // First, compute the relevant properties for this type so that we - // don't need to compute them twice. - var properties = GetProperties(type); + // First, compute the relevant properties for this type so that we + // don't need to compute them twice. + var properties = GetProperties(type); - var shortNameMatchingDescriptor = CreateShortNameMatchingDescriptor(type, properties); - results.Add(shortNameMatchingDescriptor); + var shortNameMatchingDescriptor = CreateShortNameMatchingDescriptor(type, properties); + results.Add(shortNameMatchingDescriptor); - // If the component is in the global namespace, skip adding this descriptor which will be the same as the short name one. - TagHelperDescriptor? fullyQualifiedNameMatchingDescriptor = null; - if (!type.ContainingNamespace.IsGlobalNamespace) - { - fullyQualifiedNameMatchingDescriptor = CreateFullyQualifiedNameMatchingDescriptor(type, properties); - results.Add(fullyQualifiedNameMatchingDescriptor); - } + // If the component is in the global namespace, skip adding this descriptor which will be the same as the short name one. + TagHelperDescriptor? fullyQualifiedNameMatchingDescriptor = null; + if (!type.ContainingNamespace.IsGlobalNamespace) + { + fullyQualifiedNameMatchingDescriptor = CreateFullyQualifiedNameMatchingDescriptor(type, properties); + results.Add(fullyQualifiedNameMatchingDescriptor); + } - foreach (var childContent in shortNameMatchingDescriptor.GetChildContentProperties()) + foreach (var childContent in shortNameMatchingDescriptor.GetChildContentProperties()) + { + // Synthesize a separate tag helper for each child content property that's declared. + results.Add(CreateChildContentDescriptor(shortNameMatchingDescriptor, childContent)); + if (fullyQualifiedNameMatchingDescriptor is not null) { - // Synthesize a separate tag helper for each child content property that's declared. - results.Add(CreateChildContentDescriptor(shortNameMatchingDescriptor, childContent)); - if (fullyQualifiedNameMatchingDescriptor is not null) - { - results.Add(CreateChildContentDescriptor(fullyQualifiedNameMatchingDescriptor, childContent)); - } + results.Add(CreateChildContentDescriptor(fullyQualifiedNameMatchingDescriptor, childContent)); } } } @@ -754,31 +755,5 @@ private enum PropertyKind Delegate, EventCallback, } - - private class ComponentTypeVisitor(List results) : SymbolVisitor - { - private readonly List _results = results; - - public override void VisitNamedType(INamedTypeSymbol symbol) - { - if (ComponentDetectionConventions.IsComponent(symbol, ComponentsApi.IComponent.MetadataName)) - { - _results.Add(symbol); - } - } - - public override void VisitNamespace(INamespaceSymbol symbol) - { - foreach (var member in symbol.GetMembers()) - { - Visit(member); - } - } - - public override void VisitAssembly(IAssemblySymbol symbol) - { - Visit(symbol.GlobalNamespace); - } - } } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/DefaultTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/DefaultTagHelperDescriptorProvider.cs index 5475940d88a..35dfbd5fcc9 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/DefaultTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/DefaultTagHelperDescriptorProvider.cs @@ -2,55 +2,56 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.PooledObjects; namespace Microsoft.CodeAnalysis.Razor; public sealed class DefaultTagHelperDescriptorProvider : TagHelperDescriptorProviderBase { - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); var compilation = context.Compilation; - var tagHelperTypeSymbol = compilation.GetTypeByMetadataName(TagHelperTypes.ITagHelper); - if (tagHelperTypeSymbol == null || tagHelperTypeSymbol.TypeKind == TypeKind.Error) + var iTagHelperType = compilation.GetTypeByMetadataName(TagHelperTypes.ITagHelper); + if (iTagHelperType == null || iTagHelperType.TypeKind == TypeKind.Error) { // Could not find attributes we care about in the compilation. Nothing to do. return; } - var targetSymbol = context.TargetSymbol; + var targetAssembly = context.TargetAssembly; var factory = new DefaultTagHelperDescriptorFactory(context.IncludeDocumentation, context.ExcludeHidden); - var collector = new Collector(compilation, targetSymbol, factory, tagHelperTypeSymbol); - collector.Collect(context); + var collector = new Collector(compilation, targetAssembly, factory, iTagHelperType); + collector.Collect(context, cancellationToken); } private class Collector( - Compilation compilation, ISymbol? targetSymbol, DefaultTagHelperDescriptorFactory factory, INamedTypeSymbol tagHelperTypeSymbol) - : TagHelperCollector(compilation, targetSymbol) + Compilation compilation, + IAssemblySymbol? targetAssembly, + DefaultTagHelperDescriptorFactory factory, + INamedTypeSymbol iTagHelperType) + : TagHelperCollector(compilation, targetAssembly) { private readonly DefaultTagHelperDescriptorFactory _factory = factory; - private readonly INamedTypeSymbol _tagHelperTypeSymbol = tagHelperTypeSymbol; + private readonly INamedTypeSymbol _iTagHelperType = iTagHelperType; - protected override void Collect(ISymbol symbol, ICollection results) - { - using var _ = ListPool.GetPooledObject(out var types); - var visitor = new TagHelperTypeVisitor(_tagHelperTypeSymbol, types); + protected override bool IsCandidateType(INamedTypeSymbol type) + => type.IsTagHelper(_iTagHelperType); - visitor.Visit(symbol); + protected override void Collect( + INamedTypeSymbol type, + ICollection results, + CancellationToken cancellationToken) + { + var descriptor = _factory.CreateDescriptor(type); - foreach (var type in types) + if (descriptor != null) { - var descriptor = _factory.CreateDescriptor(type); - - if (descriptor != null) - { - results.Add(descriptor); - } + results.Add(descriptor); } } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/EventHandlerTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/EventHandlerTagHelperDescriptorProvider.cs index 6c3443861a1..828c18ab326 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/EventHandlerTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/EventHandlerTagHelperDescriptorProvider.cs @@ -3,16 +3,16 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Components; -using Microsoft.AspNetCore.Razor.PooledObjects; namespace Microsoft.CodeAnalysis.Razor; internal sealed class EventHandlerTagHelperDescriptorProvider : TagHelperDescriptorProviderBase { - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); @@ -24,43 +24,45 @@ public override void Execute(TagHelperDescriptorProviderContext context) return; } - var targetSymbol = context.TargetSymbol; + var targetAssembly = context.TargetAssembly; - var collector = new Collector(compilation, targetSymbol, eventHandlerAttribute); - collector.Collect(context); + var collector = new Collector(compilation, targetAssembly, eventHandlerAttribute); + collector.Collect(context, cancellationToken); } - private class Collector(Compilation compilation, ISymbol? targetSymbol, INamedTypeSymbol eventHandlerAttribute) - : TagHelperCollector(compilation, targetSymbol) + private class Collector( + Compilation compilation, + IAssemblySymbol? targetAssembly, + INamedTypeSymbol eventHandlerAttribute) + : TagHelperCollector(compilation, targetAssembly) { private readonly INamedTypeSymbol _eventHandlerAttribute = eventHandlerAttribute; - protected override void Collect(ISymbol symbol, ICollection results) - { - using var _ = ListPool.GetPooledObject(out var types); - var visitor = new EventHandlerDataVisitor(types); - - visitor.Visit(symbol); + protected override bool IsCandidateType(INamedTypeSymbol type) + => type.DeclaredAccessibility == Accessibility.Public && + type.Name == "EventHandlers"; - foreach (var type in types) + protected override void Collect( + INamedTypeSymbol type, + ICollection results, + CancellationToken cancellationToken) + { + // Not handling duplicates here for now since we're the primary ones extending this. + // If we see users adding to the set of event handler constructs we will want to add deduplication + // and potentially diagnostics. + foreach (var attribute in type.GetAttributes()) { - // Not handling duplicates here for now since we're the primary ones extending this. - // If we see users adding to the set of event handler constructs we will want to add deduplication - // and potentially diagnostics. - foreach (var attribute in type.GetAttributes()) + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, _eventHandlerAttribute)) { - if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, _eventHandlerAttribute)) + if (!AttributeArgs.TryGet(attribute, out var args)) { - if (!AttributeArgs.TryGet(attribute, out var args)) - { - // If this occurs, the [EventHandler] was defined incorrectly, so we can't create a tag helper. - continue; - } - - var typeName = type.GetDefaultDisplayString(); - var namespaceName = type.ContainingNamespace.GetFullName(); - results.Add(CreateTagHelper(typeName, namespaceName, type.Name, args)); + // If this occurs, the [EventHandler] was defined incorrectly, so we can't create a tag helper. + continue; } + + var typeName = type.GetDefaultDisplayString(); + var namespaceName = type.ContainingNamespace.GetFullName(); + results.Add(CreateTagHelper(typeName, namespaceName, type.Name, args)); } } } @@ -247,32 +249,5 @@ private static TagHelperDescriptor CreateTagHelper( return builder.Build(); } - - private class EventHandlerDataVisitor(List results) : SymbolVisitor - { - private readonly List _results = results; - - public override void VisitNamedType(INamedTypeSymbol symbol) - { - if (symbol.DeclaredAccessibility == Accessibility.Public && - symbol.Name == "EventHandlers") - { - _results.Add(symbol); - } - } - - public override void VisitNamespace(INamespaceSymbol symbol) - { - foreach (var member in symbol.GetMembers()) - { - Visit(member); - } - } - - public override void VisitAssembly(IAssemblySymbol symbol) - { - Visit(symbol.GlobalNamespace); - } - } } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/FormNameTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/FormNameTagHelperDescriptorProvider.cs index ea2f195d37a..b5ee133963c 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/FormNameTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/FormNameTagHelperDescriptorProvider.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Components; @@ -14,12 +15,12 @@ internal sealed class FormNameTagHelperDescriptorProvider() : TagHelperDescripto { private static readonly Lazy s_formNameTagHelper = new(CreateFormNameTagHelper); - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); - var targetSymbol = context.TargetSymbol; - if (targetSymbol is not null && targetSymbol.Name != ComponentsApi.AssemblyName) + var targetAssembly = context.TargetAssembly; + if (targetAssembly is not null && targetAssembly.Name != ComponentsApi.AssemblyName) { return; } @@ -35,7 +36,8 @@ public override void Execute(TagHelperDescriptorProviderContext context) return; } - if (targetSymbol is not null && !SymbolEqualityComparer.Default.Equals(targetSymbol, renderTreeBuilder.ContainingAssembly)) + if (targetAssembly is not null && + !SymbolEqualityComparer.Default.Equals(targetAssembly, renderTreeBuilder.ContainingAssembly)) { return; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/KeyTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/KeyTagHelperDescriptorProvider.cs index b3c7fd493e6..832a2e12894 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/KeyTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/KeyTagHelperDescriptorProvider.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Components; @@ -13,7 +14,7 @@ internal sealed class KeyTagHelperDescriptorProvider() : TagHelperDescriptorProv { private static readonly Lazy s_keyTagHelper = new(CreateKeyTagHelper); - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); @@ -27,7 +28,8 @@ public override void Execute(TagHelperDescriptorProviderContext context) return; } - if (context.TargetSymbol is { } targetSymbol && !SymbolEqualityComparer.Default.Equals(targetSymbol, renderTreeBuilderType.ContainingAssembly)) + if (context.TargetAssembly is { } targetAssembly && + !SymbolEqualityComparer.Default.Equals(targetAssembly, renderTreeBuilderType.ContainingAssembly)) { return; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/RefTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/RefTagHelperDescriptorProvider.cs index a88c8360965..4a4d58c9616 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/RefTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/RefTagHelperDescriptorProvider.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Components; @@ -13,7 +14,7 @@ internal sealed class RefTagHelperDescriptorProvider() : TagHelperDescriptorProv { private static readonly Lazy s_refTagHelper = new(CreateRefTagHelper); - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); @@ -27,7 +28,8 @@ public override void Execute(TagHelperDescriptorProviderContext context) return; } - if (context.TargetSymbol is { } targetSymbol && !SymbolEqualityComparer.Default.Equals(targetSymbol, elementReference.ContainingAssembly)) + if (context.TargetAssembly is { } targetAssembly && + !SymbolEqualityComparer.Default.Equals(targetAssembly, elementReference.ContainingAssembly)) { return; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/RenderModeTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/RenderModeTagHelperDescriptorProvider.cs index 7b4acdb1192..393fcca4e5c 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/RenderModeTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/RenderModeTagHelperDescriptorProvider.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Components; @@ -13,7 +14,7 @@ internal sealed class RenderModeTagHelperDescriptorProvider() : TagHelperDescrip { private static readonly Lazy s_renderModeTagHelper = new(CreateRenderModeTagHelper); - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); @@ -27,7 +28,8 @@ public override void Execute(TagHelperDescriptorProviderContext context) return; } - if (context.TargetSymbol is { } targetSymbol && !SymbolEqualityComparer.Default.Equals(targetSymbol, iComponentRenderMode.ContainingAssembly)) + if (context.TargetAssembly is { } targetAssembly && + !SymbolEqualityComparer.Default.Equals(targetAssembly, iComponentRenderMode.ContainingAssembly)) { return; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/SplatTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/SplatTagHelperDescriptorProvider.cs index a767db03502..945f8587298 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/SplatTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/SplatTagHelperDescriptorProvider.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Components; @@ -12,7 +13,7 @@ internal sealed class SplatTagHelperDescriptorProvider : TagHelperDescriptorProv { private static readonly Lazy s_splatTagHelper = new(CreateSplatTagHelper); - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); @@ -26,7 +27,8 @@ public override void Execute(TagHelperDescriptorProviderContext context) return; } - if (context.TargetSymbol is { } targetSymbol && !SymbolEqualityComparer.Default.Equals(targetSymbol, renderTreeBuilder.ContainingAssembly)) + if (context.TargetAssembly is { } targetAssembly && + !SymbolEqualityComparer.Default.Equals(targetAssembly, renderTreeBuilder.ContainingAssembly)) { return; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/TagHelperTypeVisitor.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/TagHelperTypeVisitor.cs deleted file mode 100644 index f84914acebc..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/TagHelperTypeVisitor.cs +++ /dev/null @@ -1,57 +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 disable - -using System.Collections.Generic; - -namespace Microsoft.CodeAnalysis.Razor; - -// Visits top-level types and finds interface implementations. -internal class TagHelperTypeVisitor : SymbolVisitor -{ - private readonly INamedTypeSymbol _interface; - private readonly List _results; - - public TagHelperTypeVisitor(INamedTypeSymbol @interface, List results) - { - _interface = @interface; - _results = results; - } - - public override void VisitAssembly(IAssemblySymbol symbol) - { - Visit(symbol.GlobalNamespace); - } - - public override void VisitNamedType(INamedTypeSymbol symbol) - { - if (IsTagHelper(symbol)) - { - _results.Add(symbol); - } - } - - public override void VisitNamespace(INamespaceSymbol symbol) - { - foreach (var member in symbol.GetMembers()) - { - Visit(member); - } - } - - internal bool IsTagHelper(INamedTypeSymbol symbol) - { - if (_interface == null) - { - return false; - } - - return - symbol.TypeKind != TypeKind.Error && - symbol.DeclaredAccessibility == Accessibility.Public && - !symbol.IsAbstract && - !symbol.IsGenericType && - symbol.AllInterfaces.Contains(_interface); - } -} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorTagHelperContextDiscoveryPhase.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorTagHelperContextDiscoveryPhase.cs index 58d3b80d3b6..9f1749b860b 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorTagHelperContextDiscoveryPhase.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorTagHelperContextDiscoveryPhase.cs @@ -30,7 +30,7 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, Cancellation return; } - tagHelpers = tagHelperFeature.GetDescriptors(); + tagHelpers = tagHelperFeature.GetDescriptors(cancellationToken); } using var _ = GetPooledVisitor(codeDocument, tagHelpers, out var visitor); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/INamedTypeSymbolExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/INamedTypeSymbolExtensions.cs index 5bdc31bdd8e..9e8c300d928 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/INamedTypeSymbolExtensions.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/INamedTypeSymbolExtensions.cs @@ -7,6 +7,16 @@ namespace Microsoft.AspNetCore.Razor.Language; internal static partial class INamedTypeSymbolExtensions { - public static bool IsViewComponent(this INamedTypeSymbol symbol, INamedTypeSymbol viewComponentAttribute, INamedTypeSymbol? nonViewComponentAttribute) + public static bool IsTagHelper(this INamedTypeSymbol symbol, INamedTypeSymbol iTagHelperType) + => symbol.TypeKind != TypeKind.Error && + symbol.DeclaredAccessibility == Accessibility.Public && + !symbol.IsAbstract && + !symbol.IsGenericType && + symbol.AllInterfaces.Contains(iTagHelperType); + + public static bool IsViewComponent( + this INamedTypeSymbol symbol, + INamedTypeSymbol viewComponentAttribute, + INamedTypeSymbol? nonViewComponentAttribute) => SymbolCache.GetNamedTypeSymbolData(symbol).IsViewComponent(viewComponentAttribute, nonViewComponentAttribute); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/ITagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/ITagHelperDescriptorProvider.cs index d964f15fba2..1ce96e5a161 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/ITagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/ITagHelperDescriptorProvider.cs @@ -1,11 +1,13 @@ // 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; + namespace Microsoft.AspNetCore.Razor.Language; public interface ITagHelperDescriptorProvider : IRazorEngineFeature { int Order { get; } - void Execute(TagHelperDescriptorProviderContext context); + void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/ITagHelperFeature.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/ITagHelperFeature.cs index 2a1e940ee49..d69f3fcf62c 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/ITagHelperFeature.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/ITagHelperFeature.cs @@ -1,13 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System.Collections.Generic; +using System.Threading; namespace Microsoft.AspNetCore.Razor.Language; public interface ITagHelperFeature : IRazorEngineFeature { - IReadOnlyList GetDescriptors(); + IReadOnlyList GetDescriptors(CancellationToken cancellationToken = default); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperCollector.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperCollector.cs index f2c0c7f3d8e..1d9baaadab7 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperCollector.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperCollector.cs @@ -3,40 +3,46 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Threading; using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.CodeAnalysis; namespace Microsoft.AspNetCore.Razor.Language; -public abstract partial class TagHelperCollector +public abstract partial class TagHelperCollector( + Compilation compilation, + IAssemblySymbol? targetAssembly) where T : TagHelperCollector { // This type is generic to ensure that each descendent gets its own instance of this field. private static readonly ConditionalWeakTable s_perAssemblyCaches = new(); - private readonly Compilation _compilation; - private readonly ISymbol? _targetSymbol; + private readonly Compilation _compilation = compilation; + private readonly IAssemblySymbol? _targetAssembly = targetAssembly; - protected TagHelperCollector(Compilation compilation, ISymbol? targetSymbol) - { - _compilation = compilation; - _targetSymbol = targetSymbol; - } + protected virtual bool IncludeNestedTypes => false; - protected abstract void Collect(ISymbol symbol, ICollection results); + protected abstract bool IsCandidateType(INamedTypeSymbol type); - public void Collect(TagHelperDescriptorProviderContext context) + protected abstract void Collect( + INamedTypeSymbol type, + ICollection results, + CancellationToken cancellationToken); + + public void Collect(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken) { - if (_targetSymbol is not null) + if (_targetAssembly is not null) { - Collect(_targetSymbol, context.Results); + Collect(_targetAssembly, context.Results, cancellationToken); } else { - Collect(_compilation.Assembly.GlobalNamespace, context.Results); + Collect(_compilation.Assembly, context.Results, cancellationToken); foreach (var reference in _compilation.References) { + cancellationToken.ThrowIfCancellationRequested(); + if (_compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) { // Check to see if we already have tag helpers cached for this assembly @@ -57,7 +63,7 @@ public void Collect(TagHelperDescriptorProviderContext context) if (!cache.TryGet(includeDocumentation, excludeHidden, out var tagHelpers)) { using var _ = ListPool.GetPooledObject(out var referenceTagHelpers); - Collect(assembly.GlobalNamespace, referenceTagHelpers); + Collect(assembly, referenceTagHelpers, cancellationToken); tagHelpers = cache.Add(referenceTagHelpers.ToArrayOrEmpty(), includeDocumentation, excludeHidden); } @@ -70,4 +76,68 @@ public void Collect(TagHelperDescriptorProviderContext context) } } } + + protected virtual void Collect( + IAssemblySymbol assembly, + ICollection results, + CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var includeNestedTypes = IncludeNestedTypes; + + using var stack = new PooledArrayBuilder(); + + stack.Push(assembly.GlobalNamespace); + + while (stack.Count > 0) + { + cancellationToken.ThrowIfCancellationRequested(); + + var namespaceOrType = stack.Pop(); + + switch (namespaceOrType.Kind) + { + case SymbolKind.Namespace: + var members = namespaceOrType.GetMembers(); + + // Note: Push members onto the stack in reverse to ensure + // that they're popped off and processed in the correct order. + for (var i = members.Length - 1; i >= 0; i--) + { + cancellationToken.ThrowIfCancellationRequested(); + + // Namespaces members are only ever namespaces or types. + stack.Push((INamespaceOrTypeSymbol)members[i]); + } + + break; + + case SymbolKind.NamedType: + var typeSymbol = (INamedTypeSymbol)namespaceOrType; + + if (IsCandidateType(typeSymbol)) + { + // We have a candidate. Collect it. + Collect(typeSymbol, results, cancellationToken); + } + + if (includeNestedTypes && namespaceOrType.DeclaredAccessibility == Accessibility.Public) + { + var typeMembers = namespaceOrType.GetTypeMembers(); + + // Note: Push members onto the stack in reverse to ensure + // that they're popped off and processed in the correct order. + for (var i = typeMembers.Length - 1; i >= 0; i--) + { + cancellationToken.ThrowIfCancellationRequested(); + + stack.Push(typeMembers[i]); + } + } + + break; + } + } + } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperDescriptorProviderBase.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperDescriptorProviderBase.cs index 0ab7a8355cb..9f0fe345aba 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperDescriptorProviderBase.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperDescriptorProviderBase.cs @@ -1,11 +1,13 @@ // 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; + namespace Microsoft.AspNetCore.Razor.Language; public abstract class TagHelperDescriptorProviderBase(int order = 0) : RazorEngineFeatureBase, ITagHelperDescriptorProvider { public int Order { get; } = order; - public abstract void Execute(TagHelperDescriptorProviderContext context); + public abstract void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperDescriptorProviderContext.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperDescriptorProviderContext.cs index 1305d7888af..7a755aaab8e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperDescriptorProviderContext.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperDescriptorProviderContext.cs @@ -6,22 +6,25 @@ namespace Microsoft.AspNetCore.Razor.Language; -public sealed class TagHelperDescriptorProviderContext(Compilation compilation, ISymbol? targetSymbol, ICollection results) +public sealed class TagHelperDescriptorProviderContext( + Compilation compilation, + IAssemblySymbol? targetAssembly, + ICollection results) { public Compilation Compilation { get; } = compilation; - public ISymbol? TargetSymbol { get; } = targetSymbol; + public IAssemblySymbol? TargetAssembly { get; } = targetAssembly; public ICollection Results { get; } = results; public bool ExcludeHidden { get; init; } public bool IncludeDocumentation { get; init; } - public TagHelperDescriptorProviderContext(Compilation compilation, ISymbol? targetSymbol = null) - : this(compilation, targetSymbol, results: []) + public TagHelperDescriptorProviderContext(Compilation compilation, IAssemblySymbol? targetAssembly = null) + : this(compilation, targetAssembly, results: []) { } public TagHelperDescriptorProviderContext(Compilation compilation, ICollection results) - : this(compilation, targetSymbol: null, results) + : this(compilation, targetAssembly: null, results) { } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperDescriptorProvider.cs index abddce93827..e411ec8aeb9 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperDescriptorProvider.cs @@ -2,16 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.CodeAnalysis; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X; public sealed class ViewComponentTagHelperDescriptorProvider : TagHelperDescriptorProviderBase { - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); @@ -28,7 +28,7 @@ public override void Execute(TagHelperDescriptorProviderContext context) var factory = new ViewComponentTagHelperDescriptorFactory(compilation); var collector = new Collector(compilation, factory, vcAttribute, nonVCAttribute); - collector.Collect(context); + collector.Collect(context, cancellationToken); } private class Collector( @@ -36,27 +36,27 @@ private class Collector( ViewComponentTagHelperDescriptorFactory factory, INamedTypeSymbol vcAttribute, INamedTypeSymbol? nonVCAttribute) - : TagHelperCollector(compilation, targetSymbol: null) + : TagHelperCollector(compilation, targetAssembly: null) { private readonly ViewComponentTagHelperDescriptorFactory _factory = factory; private readonly INamedTypeSymbol _vcAttribute = vcAttribute; private readonly INamedTypeSymbol? _nonVCAttribute = nonVCAttribute; - protected override void Collect(ISymbol symbol, ICollection results) - { - using var _ = ListPool.GetPooledObject(out var types); - var visitor = new ViewComponentTypeVisitor(_vcAttribute, _nonVCAttribute, types); + protected override bool IncludeNestedTypes => true; - visitor.Visit(symbol); + protected override bool IsCandidateType(INamedTypeSymbol type) + => type.IsViewComponent(_vcAttribute, _nonVCAttribute); - foreach (var type in types) - { - var descriptor = _factory.CreateDescriptor(type); + protected override void Collect( + INamedTypeSymbol type, + ICollection results, + CancellationToken cancellationToken) + { + var descriptor = _factory.CreateDescriptor(type); - if (descriptor != null) - { - results.Add(descriptor); - } + if (descriptor != null) + { + results.Add(descriptor); } } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperDescriptorProvider.cs index 6058ae7ae52..3423015b5af 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperDescriptorProvider.cs @@ -2,16 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.CodeAnalysis; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X; public sealed class ViewComponentTagHelperDescriptorProvider : TagHelperDescriptorProviderBase { - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); @@ -28,7 +28,7 @@ public override void Execute(TagHelperDescriptorProviderContext context) var factory = new ViewComponentTagHelperDescriptorFactory(compilation); var collector = new Collector(compilation, factory, vcAttribute, nonVCAttribute); - collector.Collect(context); + collector.Collect(context, cancellationToken); } private class Collector( @@ -36,27 +36,27 @@ private class Collector( ViewComponentTagHelperDescriptorFactory factory, INamedTypeSymbol vcAttribute, INamedTypeSymbol? nonVCAttribute) - : TagHelperCollector(compilation, targetSymbol: null) + : TagHelperCollector(compilation, targetAssembly: null) { private readonly ViewComponentTagHelperDescriptorFactory _factory = factory; private readonly INamedTypeSymbol _vcAttribute = vcAttribute; private readonly INamedTypeSymbol? _nonVCAttribute = nonVCAttribute; - protected override void Collect(ISymbol symbol, ICollection results) - { - using var _ = ListPool.GetPooledObject(out var types); - var visitor = new ViewComponentTypeVisitor(_vcAttribute, _nonVCAttribute, types); + protected override bool IncludeNestedTypes => true; - visitor.Visit(symbol); + protected override bool IsCandidateType(INamedTypeSymbol type) + => type.IsViewComponent(_vcAttribute, _nonVCAttribute); - foreach (var type in types) - { - var descriptor = _factory.CreateDescriptor(type); + protected override void Collect( + INamedTypeSymbol type, + ICollection results, + CancellationToken cancellationToken) + { + var descriptor = _factory.CreateDescriptor(type); - if (descriptor != null) - { - results.Add(descriptor); - } + if (descriptor != null) + { + results.Add(descriptor); } } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperDescriptorProvider.cs index 2159ce7a07b..5b75ed3cc0a 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperDescriptorProvider.cs @@ -2,16 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Threading; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.CodeAnalysis; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions; public sealed class ViewComponentTagHelperDescriptorProvider : TagHelperDescriptorProviderBase { - public override void Execute(TagHelperDescriptorProviderContext context) + public override void Execute(TagHelperDescriptorProviderContext context, CancellationToken cancellationToken = default) { ArgHelper.ThrowIfNull(context); @@ -28,7 +28,7 @@ public override void Execute(TagHelperDescriptorProviderContext context) var factory = new ViewComponentTagHelperDescriptorFactory(compilation); var collector = new Collector(compilation, factory, vcAttribute, nonVCAttribute); - collector.Collect(context); + collector.Collect(context, cancellationToken); } private class Collector( @@ -36,27 +36,27 @@ private class Collector( ViewComponentTagHelperDescriptorFactory factory, INamedTypeSymbol vcAttribute, INamedTypeSymbol? nonVCAttribute) - : TagHelperCollector(compilation, targetSymbol: null) + : TagHelperCollector(compilation, targetAssembly: null) { private readonly ViewComponentTagHelperDescriptorFactory _factory = factory; private readonly INamedTypeSymbol _vcAttribute = vcAttribute; private readonly INamedTypeSymbol? _nonVCAttribute = nonVCAttribute; - protected override void Collect(ISymbol symbol, ICollection results) - { - using var _ = ListPool.GetPooledObject(out var types); - var visitor = new ViewComponentTypeVisitor(_vcAttribute, _nonVCAttribute, types); + protected override bool IncludeNestedTypes => true; - visitor.Visit(symbol); + protected override bool IsCandidateType(INamedTypeSymbol type) + => type.IsViewComponent(_vcAttribute, _nonVCAttribute); - foreach (var type in types) - { - var descriptor = _factory.CreateDescriptor(type); + protected override void Collect( + INamedTypeSymbol type, + ICollection results, + CancellationToken cancellationToken) + { + var descriptor = _factory.CreateDescriptor(type); - if (descriptor != null) - { - results.Add(descriptor); - } + if (descriptor != null) + { + results.Add(descriptor); } } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTypeVisitor.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTypeVisitor.cs deleted file mode 100644 index aa525927d40..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTypeVisitor.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.CodeAnalysis; - -namespace Microsoft.AspNetCore.Mvc.Razor.Extensions; - -internal class ViewComponentTypeVisitor : SymbolVisitor -{ - private readonly INamedTypeSymbol _viewComponentAttribute; - private readonly INamedTypeSymbol? _nonViewComponentAttribute; - private readonly List _results; - - public ViewComponentTypeVisitor( - INamedTypeSymbol viewComponentAttribute, - INamedTypeSymbol? nonViewComponentAttribute, - List results) - { - _viewComponentAttribute = viewComponentAttribute; - _nonViewComponentAttribute = nonViewComponentAttribute; - _results = results; - } - - public override void VisitAssembly(IAssemblySymbol symbol) - { - Visit(symbol.GlobalNamespace); - } - - public override void VisitNamedType(INamedTypeSymbol symbol) - { - if (IsViewComponent(symbol)) - { - _results.Add(symbol); - } - - if (symbol.DeclaredAccessibility != Accessibility.Public) - { - return; - } - - foreach (var member in symbol.GetTypeMembers()) - { - Visit(member); - } - } - - public override void VisitNamespace(INamespaceSymbol symbol) - { - foreach (var member in symbol.GetMembers()) - { - Visit(member); - } - } - - internal bool IsViewComponent(INamedTypeSymbol symbol) - { - if (_viewComponentAttribute == null) - { - return false; - } - - return symbol.IsViewComponent(_viewComponentAttribute, _nonViewComponentAttribute); - } -} 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 17d6309cf5f..a400289baaf 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs @@ -122,7 +122,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var tagHelpersFromCompilation = declCompilation .Combine(razorSourceGeneratorOptions) .SuppressIfNeeded(isGeneratorSuppressed) - .Select(static (pair, _) => + .Select(static (pair, cancellationToken) => { var ((compilation, razorSourceGeneratorOptions), isGeneratorSuppressed) = pair; var results = new List(); @@ -135,7 +135,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) RazorSourceGeneratorEventSource.Log.DiscoverTagHelpersFromCompilationStart(); var tagHelperFeature = GetStaticTagHelperFeature(compilation); - tagHelperFeature.CollectDescriptors(compilation.Assembly, results); + tagHelperFeature.CollectDescriptors(compilation.Assembly, results, cancellationToken); RazorSourceGeneratorEventSource.Log.DiscoverTagHelpersFromCompilationStop(); @@ -195,7 +195,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return hasRazorFilesA == hasRazorFilesB; }) - .Select(static (pair, _) => + .Select(static (pair, cancellationToken) => { var ((compilation, razorSourceGeneratorOptions), hasRazorFiles) = pair; if (!hasRazorFiles) @@ -215,7 +215,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) { if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) { - tagHelperFeature.CollectDescriptors(assembly, results); + tagHelperFeature.CollectDescriptors(assembly, results, cancellationToken); } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/StaticCompilationTagHelperFeature.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/StaticCompilationTagHelperFeature.cs index cfc9aeede2b..5f5a67e224a 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/StaticCompilationTagHelperFeature.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/StaticCompilationTagHelperFeature.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; +using System.Threading; using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis; @@ -14,25 +14,28 @@ internal sealed class StaticCompilationTagHelperFeature(Compilation compilation) { private ImmutableArray _providers; - public void CollectDescriptors(ISymbol? targetSymbol, List results) + public void CollectDescriptors( + IAssemblySymbol? targetAssembly, + List results, + CancellationToken cancellationToken) { - if (_providers.IsDefault) + if (_providers.IsDefaultOrEmpty) { return; } - var context = new TagHelperDescriptorProviderContext(compilation, targetSymbol, results); + var context = new TagHelperDescriptorProviderContext(compilation, targetAssembly, results); foreach (var provider in _providers) { - provider.Execute(context); + provider.Execute(context, cancellationToken); } } - IReadOnlyList ITagHelperFeature.GetDescriptors() + IReadOnlyList ITagHelperFeature.GetDescriptors(CancellationToken cancellationToken) { var results = new List(); - CollectDescriptors(targetSymbol: null, results); + CollectDescriptors(targetAssembly: null, results, cancellationToken); return results; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/CompilationTagHelperFeatureTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/CompilationTagHelperFeatureTest.cs index fdf5a47b5ec..bb4fd061db3 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/CompilationTagHelperFeatureTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/CompilationTagHelperFeatureTest.cs @@ -4,6 +4,7 @@ #nullable disable using System.Linq; +using System.Threading; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.CodeAnalysis.CSharp; @@ -71,7 +72,7 @@ public void GetDescriptors_DoesNotSetCompilation_IfCompilationIsInvalid() { // Arrange var provider = new Mock(); - provider.Setup(c => c.Execute(It.IsAny())); + provider.Setup(c => c.Execute(It.IsAny(), It.IsAny())); var engine = RazorProjectEngine.Create( configure => @@ -93,7 +94,7 @@ public void GetDescriptors_DoesNotSetCompilation_IfCompilationIsInvalid() // Assert Assert.Empty(result); - provider.Verify(c => c.Execute(It.IsAny()), Times.Never); + provider.Verify(c => c.Execute(It.IsAny(), It.IsAny()), Times.Never); } [Fact] @@ -103,8 +104,8 @@ public void GetDescriptors_SetsCompilation_IfCompilationIsValid() Compilation compilation = null; var provider = new Mock(); provider - .Setup(c => c.Execute(It.IsAny())) - .Callback(c => compilation = c.Compilation) + .Setup(c => c.Execute(It.IsAny(), It.IsAny())) + .Callback((TagHelperDescriptorProviderContext c, CancellationToken ct) => compilation = c.Compilation) .Verifiable(); var references = new[] diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/ComponentTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/ComponentTagHelperDescriptorProviderTest.cs index 7f6132256d2..88038cefa21 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/ComponentTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/ComponentTagHelperDescriptorProviderTest.cs @@ -1607,10 +1607,10 @@ public Task SetParametersAsync(ParameterView parameters) Assert.Empty(compilation.GetDiagnostics()); - var targetSymbol = (IAssemblySymbol)compilation.GetAssemblyOrModuleSymbol( + var targetAssembly = (IAssemblySymbol)compilation.GetAssemblyOrModuleSymbol( compilation.References.First(static r => r.Display.Contains("Microsoft.CodeAnalysis.Razor.Test"))); - var context = new TagHelperDescriptorProviderContext(compilation, targetSymbol); + var context = new TagHelperDescriptorProviderContext(compilation, targetAssembly); var provider = new ComponentTagHelperDescriptorProvider(); // Act diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorProviderTest.cs index f713ce3fcbc..39e8febc96d 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorProviderTest.cs @@ -83,10 +83,10 @@ public override void Process(TagHelperContext context, TagHelperOutput output) { var compilation = BaseCompilation.AddSyntaxTrees(Parse(csharp)); var descriptorProvider = new DefaultTagHelperDescriptorProvider(); - var targetSymbol = (IAssemblySymbol)compilation.GetAssemblyOrModuleSymbol( + var targetAssembly = (IAssemblySymbol)compilation.GetAssemblyOrModuleSymbol( compilation.References.First(static r => r.Display.Contains("Microsoft.CodeAnalysis.Razor.Test"))); - var context = new TagHelperDescriptorProviderContext(compilation, targetSymbol); + var context = new TagHelperDescriptorProviderContext(compilation, targetAssembly); // Act descriptorProvider.Execute(context); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/TagHelperTypeVisitorTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/IsTagHelperTest.cs similarity index 72% rename from src/Compiler/Microsoft.CodeAnalysis.Razor/test/TagHelperTypeVisitorTest.cs rename to src/Compiler/Microsoft.CodeAnalysis.Razor/test/IsTagHelperTest.cs index b02afeda409..a78c37a4527 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/TagHelperTypeVisitorTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/IsTagHelperTest.cs @@ -1,19 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System.Collections.Generic; +using Microsoft.AspNetCore.Razor; +using Microsoft.AspNetCore.Razor.Language; using Xunit; namespace Microsoft.CodeAnalysis.Razor.Workspaces; -public class TagHelperTypeVisitorTest : TagHelperDescriptorProviderTestBase +public class IsTagHelperTest : TagHelperDescriptorProviderTestBase { - public TagHelperTypeVisitorTest() : base(AdditionalCode) + public IsTagHelperTest() : base(AdditionalCode) { Compilation = BaseCompilation; - ITagHelperSymbol = Compilation.GetTypeByMetadataName(TagHelperTypes.ITagHelper); + ITagHelperSymbol = Compilation.GetTypeByMetadataName(TagHelperTypes.ITagHelper).AssumeNotNull(); } private Compilation Compilation { get; } @@ -24,11 +23,11 @@ public TagHelperTypeVisitorTest() : base(AdditionalCode) public void IsTagHelper_PlainTagHelper_ReturnsTrue() { // Arrange - var testVisitor = new TagHelperTypeVisitor(ITagHelperSymbol, new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName("TestNamespace.Valid_PlainTagHelper"); + Assert.NotNull(tagHelperSymbol); // Act - var isTagHelper = testVisitor.IsTagHelper(tagHelperSymbol); + var isTagHelper = tagHelperSymbol.IsTagHelper(ITagHelperSymbol); // Assert Assert.True(isTagHelper); @@ -38,11 +37,11 @@ public void IsTagHelper_PlainTagHelper_ReturnsTrue() public void IsTagHelper_InheritedTagHelper_ReturnsTrue() { // Arrange - var testVisitor = new TagHelperTypeVisitor(ITagHelperSymbol, new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName("TestNamespace.Valid_InheritedTagHelper"); + Assert.NotNull(tagHelperSymbol); // Act - var isTagHelper = testVisitor.IsTagHelper(tagHelperSymbol); + var isTagHelper = tagHelperSymbol.IsTagHelper(ITagHelperSymbol); // Assert Assert.True(isTagHelper); @@ -52,11 +51,11 @@ public void IsTagHelper_InheritedTagHelper_ReturnsTrue() public void IsTagHelper_AbstractTagHelper_ReturnsFalse() { // Arrange - var testVisitor = new TagHelperTypeVisitor(ITagHelperSymbol, new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName("TestNamespace.Invalid_AbstractTagHelper"); + Assert.NotNull(tagHelperSymbol); // Act - var isTagHelper = testVisitor.IsTagHelper(tagHelperSymbol); + var isTagHelper = tagHelperSymbol.IsTagHelper(ITagHelperSymbol); // Assert Assert.False(isTagHelper); @@ -66,11 +65,11 @@ public void IsTagHelper_AbstractTagHelper_ReturnsFalse() public void IsTagHelper_GenericTagHelper_ReturnsFalse() { // Arrange - var testVisitor = new TagHelperTypeVisitor(ITagHelperSymbol, new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName("TestNamespace.Invalid_GenericTagHelper`1"); + Assert.NotNull(tagHelperSymbol); // Act - var isTagHelper = testVisitor.IsTagHelper(tagHelperSymbol); + var isTagHelper = tagHelperSymbol.IsTagHelper(ITagHelperSymbol); // Assert Assert.False(isTagHelper); @@ -80,11 +79,11 @@ public void IsTagHelper_GenericTagHelper_ReturnsFalse() public void IsTagHelper_InternalTagHelper_ReturnsFalse() { // Arrange - var testVisitor = new TagHelperTypeVisitor(ITagHelperSymbol, new List()); var tagHelperSymbol = Compilation.GetTypeByMetadataName("TestNamespace.Invalid_InternalTagHelper"); + Assert.NotNull(tagHelperSymbol); // Act - var isTagHelper = testVisitor.IsTagHelper(tagHelperSymbol); + var isTagHelper = tagHelperSymbol.IsTagHelper(ITagHelperSymbol); // Assert Assert.False(isTagHelper); diff --git a/src/Compiler/perf/Microbenchmarks/RazorTagHelperParsingBenchmark.cs b/src/Compiler/perf/Microbenchmarks/RazorTagHelperParsingBenchmark.cs index 158e6f92ced..98d6415caf5 100644 --- a/src/Compiler/perf/Microbenchmarks/RazorTagHelperParsingBenchmark.cs +++ b/src/Compiler/perf/Microbenchmarks/RazorTagHelperParsingBenchmark.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.IO; +using System.Threading; using BenchmarkDotNet.Attributes; using Microsoft.AspNetCore.Mvc.Razor.Extensions; using Microsoft.AspNetCore.Razor.Language; @@ -68,12 +69,12 @@ private static ImmutableArray ReadTagHelpers(string filePat return JsonDataConvert.DeserializeTagHelperArray(reader); } - private sealed class StaticTagHelperFeature : RazorEngineFeatureBase, ITagHelperFeature + private sealed class StaticTagHelperFeature(IReadOnlyList descriptors) + : RazorEngineFeatureBase, ITagHelperFeature { - public StaticTagHelperFeature(IReadOnlyList descriptors) => Descriptors = descriptors; + public IReadOnlyList Descriptors { get; } = descriptors; - public IReadOnlyList Descriptors { get; } - - public IReadOnlyList GetDescriptors() => Descriptors; + public IReadOnlyList GetDescriptors(CancellationToken cancellationToken = default) + => Descriptors; } } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Extensions/ProjectExtensions.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Extensions/ProjectExtensions.cs index fd20a50ff14..1901acc7784 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Extensions/ProjectExtensions.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Extensions/ProjectExtensions.cs @@ -63,7 +63,7 @@ public static async ValueTask> GetTagHelpers foreach (var provider in providers) { watch.Restart(); - provider.Execute(context); + provider.Execute(context, cancellationToken); watch.Stop(); writeProperties[0] = new(provider.GetType().Name + PropertySuffix, watch.ElapsedMilliseconds); diff --git a/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/VisualStudioRazorParser.cs b/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/VisualStudioRazorParser.cs index 1a74f6c71e9..8945148d6de 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/VisualStudioRazorParser.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/VisualStudioRazorParser.cs @@ -590,9 +590,9 @@ public VisualStudioTagHelperFeature(IReadOnlyList? tagHelpe _tagHelpers = tagHelpers; } - public IReadOnlyList? GetDescriptors() + public IReadOnlyList GetDescriptors(CancellationToken cancellationToken = default) { - return _tagHelpers; + return _tagHelpers ?? []; } } diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/TestTagHelperFeature.cs b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/TestTagHelperFeature.cs index 534e324af45..1c025f604ae 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/TestTagHelperFeature.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/TestTagHelperFeature.cs @@ -1,9 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System.Collections.Generic; +using System.Threading; namespace Microsoft.AspNetCore.Razor.Language; @@ -11,18 +10,16 @@ public class TestTagHelperFeature : RazorEngineFeatureBase, ITagHelperFeature { public TestTagHelperFeature() { - TagHelpers = new List(); + TagHelpers = []; } public TestTagHelperFeature(IEnumerable tagHelpers) { - TagHelpers = new List(tagHelpers); + TagHelpers = [.. tagHelpers]; } public List TagHelpers { get; } - public IReadOnlyList GetDescriptors() - { - return TagHelpers.ToArray(); - } + public IReadOnlyList GetDescriptors(CancellationToken cancellationToken = default) + => [.. TagHelpers]; }