Kaynağa Gözat

Support visiting types in target assembly in Razor (#30274)

* Support visiting types in target assembly in Razor
* Remove TagHelperDiscoveryFilter concept
* Don't search through all assemblies if targetReference is not symbol
* Fix test on net46 targets
Safia Abdalla 5 yıl önce
ebeveyn
işleme
7a0e7fef3b

+ 8 - 5
src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/ViewComponentTagHelperDescriptorProvider.cs

@@ -37,15 +37,18 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
 
 
             var types = new List<INamedTypeSymbol>();
             var types = new List<INamedTypeSymbol>();
             var visitor = new ViewComponentTypeVisitor(vcAttribute, nonVCAttribute, types);
             var visitor = new ViewComponentTypeVisitor(vcAttribute, nonVCAttribute, types);
-            var discoveryMode = context.Items.GetTagHelperDiscoveryFilter();
 
 
-            if ((discoveryMode & TagHelperDiscoveryFilter.CurrentCompilation) == TagHelperDiscoveryFilter.CurrentCompilation)
+            var targetReference = context.Items.GetTargetMetadataReference();
+            if (targetReference is not null)
             {
             {
-                visitor.Visit(compilation.Assembly.GlobalNamespace);
+                if (compilation.GetAssemblyOrModuleSymbol(targetReference) is IAssemblySymbol targetAssembly && IsTagHelperAssembly(targetAssembly))
+                {
+                    visitor.Visit(targetAssembly.GlobalNamespace);
+                }
             }
             }
-
-            if ((discoveryMode & TagHelperDiscoveryFilter.ReferenceAssemblies) == TagHelperDiscoveryFilter.ReferenceAssemblies)
+            else
             {
             {
+                visitor.Visit(compilation.Assembly.GlobalNamespace);
                 foreach (var reference in compilation.References)
                 foreach (var reference in compilation.References)
                 {
                 {
                     if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly)
                     if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly)

+ 0 - 58
src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ViewComponentTagHelperDescriptorProviderTest.cs

@@ -65,63 +65,5 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
             // Assert
             // Assert
             Assert.Single(context.Results, d => TagHelperDescriptorComparer.Default.Equals(d, expectedDescriptor));
             Assert.Single(context.Results, d => TagHelperDescriptorComparer.Default.Equals(d, expectedDescriptor));
         }
         }
-
-        [Fact]
-        public void DescriptorProvider_WithCurrentCompilationFilter_FindsDescriptorFromCurrentCompilation()
-        {
-            // Arrange
-            var code = @"
-        public class StringParameterViewComponent
-        {
-            public string Invoke(string foo, string bar) => null;
-        }
-";
-
-            var compilation = MvcShim.BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code));
-
-            var context = TagHelperDescriptorProviderContext.Create();
-            context.SetCompilation(compilation);
-            context.Items.SetTagHelperDiscoveryFilter(TagHelperDiscoveryFilter.CurrentCompilation);
-
-            var provider = new ViewComponentTagHelperDescriptorProvider()
-            {
-                Engine = RazorProjectEngine.CreateEmpty().Engine,
-            };
-
-            // Act
-            provider.Execute(context);
-
-            // Assert
-            Assert.Single(context.Results);
-        }
-
-        [Fact]
-        public void DescriptorProvider_WithReferenceAssembliesFilter_DoesNotFindDescriptorFromCurrentCompilation()
-        {
-            // Arrange
-            var code = @"
-        public class StringParameterViewComponent
-        {
-            public string Invoke(string foo, string bar) => null;
-        }
-";
-
-            var compilation = MvcShim.BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code));
-
-            var context = TagHelperDescriptorProviderContext.Create();
-            context.SetCompilation(compilation);
-            context.Items.SetTagHelperDiscoveryFilter(TagHelperDiscoveryFilter.ReferenceAssemblies);
-
-            var provider = new ViewComponentTagHelperDescriptorProvider()
-            {
-                Engine = RazorProjectEngine.CreateEmpty().Engine,
-            };
-
-            // Act
-            provider.Execute(context);
-
-            // Assert
-            Assert.Empty(context.Results);
-        }
     }
     }
 }
 }

+ 10 - 10
src/Razor/Microsoft.CodeAnalysis.Razor/src/ComponentTagHelperDescriptorProvider.cs

@@ -40,23 +40,23 @@ namespace Microsoft.CodeAnalysis.Razor
             var types = new List<INamedTypeSymbol>();
             var types = new List<INamedTypeSymbol>();
             var visitor = new ComponentTypeVisitor(symbols, types);
             var visitor = new ComponentTypeVisitor(symbols, types);
 
 
-            var discoveryMode = context.Items.GetTagHelperDiscoveryFilter();
-
-            if ((discoveryMode & TagHelperDiscoveryFilter.CurrentCompilation) == TagHelperDiscoveryFilter.CurrentCompilation)
+            var targetReference = context.Items.GetTargetMetadataReference();
+            if (targetReference is not null)
             {
             {
-                // Visit the primary output of this compilation
-                visitor.Visit(compilation.Assembly);
-            }
+                if (compilation.GetAssemblyOrModuleSymbol(targetReference) is IAssemblySymbol targetAssembly)
+                {
+                    visitor.Visit(targetAssembly.GlobalNamespace);
+                }
 
 
-            if ((discoveryMode & TagHelperDiscoveryFilter.ReferenceAssemblies) == TagHelperDiscoveryFilter.ReferenceAssemblies)
+            }
+            else
             {
             {
+                visitor.Visit(compilation.Assembly.GlobalNamespace);
                 foreach (var reference in compilation.References)
                 foreach (var reference in compilation.References)
                 {
                 {
-                    // We ignore .netmodules here - there really isn't a case where they are used by user code
-                    // even though the Roslyn APIs all support them.
                     if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly)
                     if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly)
                     {
                     {
-                        visitor.Visit(assembly);
+                        visitor.Visit(assembly.GlobalNamespace);
                     }
                     }
                 }
                 }
             }
             }

+ 9 - 5
src/Razor/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorProvider.cs

@@ -34,15 +34,18 @@ namespace Microsoft.CodeAnalysis.Razor
 
 
             var types = new List<INamedTypeSymbol>();
             var types = new List<INamedTypeSymbol>();
             var visitor = new TagHelperTypeVisitor(iTagHelper, types);
             var visitor = new TagHelperTypeVisitor(iTagHelper, types);
-            var discoveryMode = context.Items.GetTagHelperDiscoveryFilter();
 
 
-            if ((discoveryMode & TagHelperDiscoveryFilter.CurrentCompilation) == TagHelperDiscoveryFilter.CurrentCompilation)
+            var targetReference = context.Items.GetTargetMetadataReference();
+            if (targetReference is not null)
             {
             {
-                visitor.Visit(compilation.Assembly.GlobalNamespace);
+                if (compilation.GetAssemblyOrModuleSymbol(targetReference) is IAssemblySymbol targetAssembly && IsTagHelperAssembly(targetAssembly))
+                {
+                    visitor.Visit(targetAssembly.GlobalNamespace);
+                }
             }
             }
-
-            if ((discoveryMode & TagHelperDiscoveryFilter.ReferenceAssemblies) == TagHelperDiscoveryFilter.ReferenceAssemblies)
+            else
             {
             {
+                visitor.Visit(compilation.Assembly.GlobalNamespace);
                 foreach (var reference in compilation.References)
                 foreach (var reference in compilation.References)
                 {
                 {
                     if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly)
                     if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly)
@@ -55,6 +58,7 @@ namespace Microsoft.CodeAnalysis.Razor
                 }
                 }
             }
             }
 
 
+
             var factory = new DefaultTagHelperDescriptorFactory(compilation, context.IncludeDocumentation, context.ExcludeHidden);
             var factory = new DefaultTagHelperDescriptorFactory(compilation, context.IncludeDocumentation, context.ExcludeHidden);
             for (var i = 0; i < types.Count; i++)
             for (var i = 0; i < types.Count; i++)
             {
             {

+ 11 - 8
src/Razor/Microsoft.CodeAnalysis.Razor/src/EventHandlerTagHelperDescriptorProvider.cs

@@ -47,21 +47,24 @@ namespace Microsoft.CodeAnalysis.Razor
             var types = new List<INamedTypeSymbol>();
             var types = new List<INamedTypeSymbol>();
             var visitor = new EventHandlerDataVisitor(types);
             var visitor = new EventHandlerDataVisitor(types);
 
 
-            var discoveryMode = context.Items.GetTagHelperDiscoveryFilter();
-            if ((discoveryMode & TagHelperDiscoveryFilter.CurrentCompilation) == TagHelperDiscoveryFilter.CurrentCompilation)
+
+            var targetReference = context.Items.GetTargetMetadataReference();
+            if (targetReference is not null)
             {
             {
-                visitor.Visit(compilation.Assembly);
-            }
+                if (compilation.GetAssemblyOrModuleSymbol(targetReference) is IAssemblySymbol targetAssembly)
+                {
+                    visitor.Visit(targetAssembly.GlobalNamespace);
+                }
 
 
-            if ((discoveryMode & TagHelperDiscoveryFilter.ReferenceAssemblies) == TagHelperDiscoveryFilter.ReferenceAssemblies)
+            }
+            else
             {
             {
+                visitor.Visit(compilation.Assembly.GlobalNamespace);
                 foreach (var reference in compilation.References)
                 foreach (var reference in compilation.References)
                 {
                 {
-                    // We ignore .netmodules here - there really isn't a case where they are used by user code
-                    // even though the Roslyn APIs all support them.
                     if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly)
                     if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly)
                     {
                     {
-                        visitor.Visit(assembly);
+                        visitor.Visit(assembly.GlobalNamespace);
                     }
                     }
                 }
                 }
             }
             }

+ 0 - 17
src/Razor/Microsoft.CodeAnalysis.Razor/src/TagHelperDiscoveryFilter.cs

@@ -1,17 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-#nullable enable
-
-using System;
-
-namespace Microsoft.CodeAnalysis.Razor
-{
-    [Flags]
-    internal enum TagHelperDiscoveryFilter
-    {
-        CurrentCompilation = 1,
-        ReferenceAssemblies = 2,
-        Default = CurrentCompilation | ReferenceAssemblies,
-    };
-}

+ 0 - 29
src/Razor/Microsoft.CodeAnalysis.Razor/src/TagHelperDiscoveryFilterExtensions.cs

@@ -1,29 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-#nullable enable
-
-using Microsoft.AspNetCore.Razor.Language;
-
-namespace Microsoft.CodeAnalysis.Razor
-{
-    internal static class TagHelperDiscoveryFilterExtensions
-    {
-        private static readonly object TagHelperDiscoveryModeKey = new object();
-
-        public static TagHelperDiscoveryFilter GetTagHelperDiscoveryFilter(this ItemCollection items)
-        {
-            if (items.Count == 0 || items[TagHelperDiscoveryModeKey] is not TagHelperDiscoveryFilter filter)
-            {
-                return TagHelperDiscoveryFilter.Default;
-            }
-
-            return filter;
-        }
-
-        public static void SetTagHelperDiscoveryFilter(this ItemCollection items, TagHelperDiscoveryFilter filter)
-        {
-            items[TagHelperDiscoveryModeKey] = filter;
-        }
-    }
-}

+ 29 - 0
src/Razor/Microsoft.CodeAnalysis.Razor/src/TagHelperTargetReferenceExtensions.cs

@@ -0,0 +1,29 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#nullable enable
+
+using Microsoft.AspNetCore.Razor.Language;
+
+namespace Microsoft.CodeAnalysis.Razor
+{
+    internal static class TagHelperTargetReferenceExtensions
+    {
+        private static readonly object TargetAssemblyKey = new object();
+
+        public static MetadataReference? GetTargetMetadataReference(this ItemCollection items)
+        {
+            if (items.Count == 0 || items[TargetAssemblyKey] is not MetadataReference reference)
+            {
+                return null;
+            }
+
+            return reference;
+        }
+
+        public static void SetTargetMetadataReference(this ItemCollection items, MetadataReference reference)
+        {
+            items[TargetAssemblyKey] = reference;
+        }
+    }
+}

+ 5 - 50
src/Razor/Microsoft.CodeAnalysis.Razor/test/ComponentTagHelperDescriptorProviderTest.cs

@@ -1281,7 +1281,7 @@ namespace Test
                     Assert.Equal("System.Type", a.TypeName);
                     Assert.Equal("System.Type", a.TypeName);
                     Assert.True(a.IsTypeParameterProperty());
                     Assert.True(a.IsTypeParameterProperty());
                 });
                 });
-            
+
             var childContent = Assert.Single(components, c => c.IsChildContentTagHelper());
             var childContent = Assert.Single(components, c => c.IsChildContentTagHelper());
 
 
             Assert.Equal("TestAssembly", childContent.AssemblyName);
             Assert.Equal("TestAssembly", childContent.AssemblyName);
@@ -1586,51 +1586,7 @@ namespace Test
         }
         }
 
 
         [Fact]
         [Fact]
-        public void Execute_WithFilterAssemblyDoesNotDiscoverTagHelpersFromReferences()
-        {
-            // Arrange
-            var testComponent = "Test.MyComponent";
-            var routerComponent = "Microsoft.AspNetCore.Components.Routing.Router";
-            var compilation = BaseCompilation.AddSyntaxTrees(Parse(@"
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Components;
-
-namespace Test
-{
-    public class MyComponent : IComponent
-    {
-        public void Attach(RenderHandle renderHandle) { }
-
-        public Task SetParametersAsync(ParameterView parameters)
-        {
-            return Task.CompletedTask;
-        }
-
-        [Parameter]
-        public string MyProperty { get; set; }
-    }
-}
-
-"));
-
-            Assert.Empty(compilation.GetDiagnostics());
-
-            var context = TagHelperDescriptorProviderContext.Create();
-            context.SetCompilation(compilation);
-            context.Items.SetTagHelperDiscoveryFilter(TagHelperDiscoveryFilter.CurrentCompilation);
-            var provider = new ComponentTagHelperDescriptorProvider();
-
-            // Act
-            provider.Execute(context);
-
-            // Assert
-            Assert.NotNull(compilation.GetTypeByMetadataName(testComponent));
-            Assert.NotEmpty(context.Results.Where(f => f.GetTypeName() == testComponent));
-            Assert.Empty(context.Results.Where(f => f.GetTypeName() == routerComponent));
-        }
-
-        [Fact]
-        public void Execute_WithFilterReferenceDoesNotDiscoverTagHelpersFromAssembly()
+        public void Execute_WithTargetAssembly_Works()
         {
         {
             // Arrange
             // Arrange
             var testComponent = "Test.MyComponent";
             var testComponent = "Test.MyComponent";
@@ -1661,7 +1617,7 @@ namespace Test
 
 
             var context = TagHelperDescriptorProviderContext.Create();
             var context = TagHelperDescriptorProviderContext.Create();
             context.SetCompilation(compilation);
             context.SetCompilation(compilation);
-            context.Items.SetTagHelperDiscoveryFilter(TagHelperDiscoveryFilter.ReferenceAssemblies);
+            context.Items.SetTargetMetadataReference(compilation.References.Single(r => r.Display.Contains("Microsoft.CodeAnalysis.Razor.Test.dll")));
             var provider = new ComponentTagHelperDescriptorProvider();
             var provider = new ComponentTagHelperDescriptorProvider();
 
 
             // Act
             // Act
@@ -1669,9 +1625,9 @@ namespace Test
 
 
             // Assert
             // Assert
             Assert.NotNull(compilation.GetTypeByMetadataName(testComponent));
             Assert.NotNull(compilation.GetTypeByMetadataName(testComponent));
-            Assert.NotEmpty(context.Results);
+            Assert.Empty(context.Results); // Target assembly contains no components
             Assert.Empty(context.Results.Where(f => f.GetTypeName() == testComponent));
             Assert.Empty(context.Results.Where(f => f.GetTypeName() == testComponent));
-            Assert.NotEmpty(context.Results.Where(f => f.GetTypeName() == routerComponent));
+            Assert.Empty(context.Results.Where(f => f.GetTypeName() == routerComponent));
         }
         }
 
 
         [Fact]
         [Fact]
@@ -1707,7 +1663,6 @@ namespace Test
 
 
             var context = TagHelperDescriptorProviderContext.Create();
             var context = TagHelperDescriptorProviderContext.Create();
             context.SetCompilation(compilation);
             context.SetCompilation(compilation);
-            context.Items.SetTagHelperDiscoveryFilter(TagHelperDiscoveryFilter.Default);
             var provider = new ComponentTagHelperDescriptorProvider();
             var provider = new ComponentTagHelperDescriptorProvider();
 
 
             // Act
             // Act

+ 9 - 38
src/Razor/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorProviderTest.cs

@@ -2,6 +2,8 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
 using System.Linq;
 using System.Linq;
+using System.Diagnostics;
+using System.Threading;
 using System.Reflection;
 using System.Reflection;
 using Microsoft.AspNetCore.Razor.Language;
 using Microsoft.AspNetCore.Razor.Language;
 using Microsoft.CodeAnalysis.CSharp;
 using Microsoft.CodeAnalysis.CSharp;
@@ -25,7 +27,7 @@ namespace Microsoft.CodeAnalysis.Razor
             context.SetCompilation(compilation);
             context.SetCompilation(compilation);
             context.ExcludeHidden = true;
             context.ExcludeHidden = true;
 
 
-            // Act 
+            // Act
             descriptorProvider.Execute(context);
             descriptorProvider.Execute(context);
 
 
             // Assert
             // Assert
@@ -44,45 +46,15 @@ namespace Microsoft.CodeAnalysis.Razor
 
 
             var context = TagHelperDescriptorProviderContext.Create();
             var context = TagHelperDescriptorProviderContext.Create();
 
 
-            // Act 
-            descriptorProvider.Execute(context);
-
-            // Assert
-            Assert.Empty(context.Results);
-        }
-
-        [Fact]
-        public void Execute_WithFilterAssemblyDoesNotDiscoverTagHelpersFromReferences()
-        {
-            // Arrange
-            var typeName = "TestAssembly.TestTagHelper";
-            var csharp = @"
-using Microsoft.AspNetCore.Razor.TagHelpers;
-namespace TestAssembly
-{
-    public class TestTagHelper : TagHelper
-    {
-        public override void Process(TagHelperContext context, TagHelperOutput output) {}
-    }
-}";
-            var compilation = TestCompilation.Create(_assembly, CSharpSyntaxTree.ParseText(csharp));
-            var descriptorProvider = new DefaultTagHelperDescriptorProvider();
-
-            var context = TagHelperDescriptorProviderContext.Create();
-            context.SetCompilation(compilation);
-            context.Items.SetTagHelperDiscoveryFilter(TagHelperDiscoveryFilter.CurrentCompilation);
-
             // Act
             // Act
             descriptorProvider.Execute(context);
             descriptorProvider.Execute(context);
 
 
             // Assert
             // Assert
-            Assert.NotNull(compilation.GetTypeByMetadataName(typeName));
-            var descriptor = Assert.Single(context.Results);
-            Assert.Equal(typeName, descriptor.GetTypeName());
+            Assert.Empty(context.Results);
         }
         }
 
 
         [Fact]
         [Fact]
-        public void Execute_WithFilterReferenceDoesNotDiscoverTagHelpersFromAssembly()
+        public void Execute_WithDefaultDiscoversTagHelpersFromAssemblyAndReference()
         {
         {
             // Arrange
             // Arrange
             var testTagHelper = "TestAssembly.TestTagHelper";
             var testTagHelper = "TestAssembly.TestTagHelper";
@@ -101,7 +73,6 @@ namespace TestAssembly
 
 
             var context = TagHelperDescriptorProviderContext.Create();
             var context = TagHelperDescriptorProviderContext.Create();
             context.SetCompilation(compilation);
             context.SetCompilation(compilation);
-            context.Items.SetTagHelperDiscoveryFilter(TagHelperDiscoveryFilter.ReferenceAssemblies);
 
 
             // Act
             // Act
             descriptorProvider.Execute(context);
             descriptorProvider.Execute(context);
@@ -109,12 +80,12 @@ namespace TestAssembly
             // Assert
             // Assert
             Assert.NotNull(compilation.GetTypeByMetadataName(testTagHelper));
             Assert.NotNull(compilation.GetTypeByMetadataName(testTagHelper));
             Assert.NotEmpty(context.Results);
             Assert.NotEmpty(context.Results);
-            Assert.Empty(context.Results.Where(f => f.GetTypeName() == testTagHelper));
+            Assert.NotEmpty(context.Results.Where(f => f.GetTypeName() == testTagHelper));
             Assert.NotEmpty(context.Results.Where(f => f.GetTypeName() == enumTagHelper));
             Assert.NotEmpty(context.Results.Where(f => f.GetTypeName() == enumTagHelper));
         }
         }
 
 
         [Fact]
         [Fact]
-        public void Execute_WithDefaultDiscoversTagHelpersFromAssemblyAndReference()
+        public void Execute_WithTargetMetadataReference_Works()
         {
         {
             // Arrange
             // Arrange
             var testTagHelper = "TestAssembly.TestTagHelper";
             var testTagHelper = "TestAssembly.TestTagHelper";
@@ -133,7 +104,7 @@ namespace TestAssembly
 
 
             var context = TagHelperDescriptorProviderContext.Create();
             var context = TagHelperDescriptorProviderContext.Create();
             context.SetCompilation(compilation);
             context.SetCompilation(compilation);
-            context.Items.SetTagHelperDiscoveryFilter(TagHelperDiscoveryFilter.Default);
+            context.Items.SetTargetMetadataReference(compilation.References.First(r => r.Display.Contains("Microsoft.CodeAnalysis.Razor.Test.dll")));
 
 
             // Act
             // Act
             descriptorProvider.Execute(context);
             descriptorProvider.Execute(context);
@@ -141,7 +112,7 @@ namespace TestAssembly
             // Assert
             // Assert
             Assert.NotNull(compilation.GetTypeByMetadataName(testTagHelper));
             Assert.NotNull(compilation.GetTypeByMetadataName(testTagHelper));
             Assert.NotEmpty(context.Results);
             Assert.NotEmpty(context.Results);
-            Assert.NotEmpty(context.Results.Where(f => f.GetTypeName() == testTagHelper));
+            Assert.Empty(context.Results.Where(f => f.GetTypeName() == testTagHelper));
             Assert.NotEmpty(context.Results.Where(f => f.GetTypeName() == enumTagHelper));
             Assert.NotEmpty(context.Results.Where(f => f.GetTypeName() == enumTagHelper));
         }
         }
     }
     }