diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ViewComponentTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ViewComponentTagHelperDescriptorProviderTest.cs index ba263154902..472cf919cb5 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ViewComponentTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ViewComponentTagHelperDescriptorProviderTest.cs @@ -30,7 +30,7 @@ public class StringParameterViewComponent var compilation = MvcShim.BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code)); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ViewComponentTagHelperDescriptorProvider() { diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ViewComponentTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ViewComponentTagHelperDescriptorProviderTest.cs index 065025367d0..46ffb1fb713 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ViewComponentTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ViewComponentTagHelperDescriptorProviderTest.cs @@ -31,7 +31,7 @@ public class StringParameterViewComponent var compilation = MvcShim.BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code)); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ViewComponentTagHelperDescriptorProvider() { diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ViewComponentTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ViewComponentTagHelperDescriptorProviderTest.cs index c2e72ff3e72..fd3f3ec388b 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ViewComponentTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ViewComponentTagHelperDescriptorProviderTest.cs @@ -28,7 +28,7 @@ public class StringParameterViewComponent var compilation = MvcShim.BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code)); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ViewComponentTagHelperDescriptorProvider() { diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentsApi.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentsApi.cs index 4578e1b42d6..3df3d577247 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentsApi.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentsApi.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - namespace Microsoft.AspNetCore.Razor.Language.Components; // Constants for method names used in code-generation @@ -16,16 +14,16 @@ internal static class ComponentsApi public static class ComponentBase { public const string Namespace = "Microsoft.AspNetCore.Components"; - public const string FullTypeName = Namespace + ".ComponentBase"; - public const string MetadataName = FullTypeName; + public const string MetadataName = WellKnownTypeNames.MicrosoftAspNetCoreComponentsComponentBase; + public const string FullTypeName = MetadataName; public const string BuildRenderTree = nameof(BuildRenderTree); } public static class ParameterAttribute { - public const string FullTypeName = "Microsoft.AspNetCore.Components.ParameterAttribute"; - public const string MetadataName = FullTypeName; + public const string MetadataName = WellKnownTypeNames.MicrosoftAspNetCoreComponentsParameterAttribute; + public const string FullTypeName = MetadataName; } public static class LayoutAttribute @@ -40,9 +38,8 @@ public static class InjectAttribute public static class IComponent { - public const string FullTypeName = "Microsoft.AspNetCore.Components.IComponent"; - - public const string MetadataName = FullTypeName; + public const string MetadataName = WellKnownTypeNames.MicrosoftAspNetCoreComponentsIComponent; + public const string FullTypeName = MetadataName; } public static class IDictionary @@ -53,15 +50,15 @@ public static class IDictionary public static class RenderFragment { public const string Namespace = "Microsoft.AspNetCore.Components"; - public const string FullTypeName = Namespace + ".RenderFragment"; - public const string MetadataName = FullTypeName; + public const string MetadataName = WellKnownTypeNames.MicrosoftAspNetCoreComponentsRenderFragment; + public const string FullTypeName = MetadataName; } public static class RenderFragmentOfT { public const string Namespace = "Microsoft.AspNetCore.Components"; public const string FullTypeName = Namespace + ".RenderFragment<>"; - public const string MetadataName = Namespace + ".RenderFragment`1"; + public const string MetadataName = WellKnownTypeNames.MicrosoftAspNetCoreComponentsRenderFragment1; public const string DisplayName = Namespace + ".RenderFragment"; } @@ -141,15 +138,15 @@ public static class ElementReference public static class EventCallback { - public const string FullTypeName = "Microsoft.AspNetCore.Components.EventCallback"; - public const string MetadataName = FullTypeName; + public const string MetadataName = WellKnownTypeNames.MicrosoftAspNetCoreComponentsEventCallback; + public const string FullTypeName = MetadataName; public const string FactoryAccessor = FullTypeName + ".Factory"; } public static class EventCallbackOfT { - public const string MetadataName = "Microsoft.AspNetCore.Components.EventCallback`1"; + public const string MetadataName = WellKnownTypeNames.MicrosoftAspNetCoreComponentsEventCallback1; public const string DisplayName = "Microsoft.AspNetCore.Components.EventCallback"; } @@ -167,6 +164,6 @@ public static class BindConverter public static class CascadingTypeParameterAttribute { - public const string MetadataName = "Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute"; + public const string MetadataName = WellKnownTypeNames.MicrosoftAspNetCoreComponentsCascadingTypeParameterAttribute; } } diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/WellKnownTypeNames.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/WellKnownTypeNames.cs new file mode 100644 index 00000000000..83c2965c849 --- /dev/null +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/WellKnownTypeNames.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; + +namespace Microsoft.AspNetCore.Razor.Language; + +public static class WellKnownTypeNames +{ + public const string MicrosoftAspNetCoreComponentsCascadingTypeParameterAttribute = "Microsoft.AspNetCore.Components.CascadingTypeParameterAttribute"; + public const string MicrosoftAspNetCoreComponentsComponentBase = "Microsoft.AspNetCore.Components.ComponentBase"; + public const string MicrosoftAspNetCoreComponentsEditorRequiredAttribute = "Microsoft.AspNetCore.Components.EditorRequiredAttribute"; + public const string MicrosoftAspNetCoreComponentsEventCallback = "Microsoft.AspNetCore.Components.EventCallback"; + public const string MicrosoftAspNetCoreComponentsEventCallback1 = "Microsoft.AspNetCore.Components.EventCallback`1"; + public const string MicrosoftAspNetCoreComponentsIComponent = "Microsoft.AspNetCore.Components.IComponent"; + public const string MicrosoftAspNetCoreComponentsParameterAttribute = "Microsoft.AspNetCore.Components.ParameterAttribute"; + public const string MicrosoftAspNetCoreComponentsRenderFragment = "Microsoft.AspNetCore.Components.RenderFragment"; + public const string MicrosoftAspNetCoreComponentsRenderFragment1 = "Microsoft.AspNetCore.Components.RenderFragment`1"; + public const string MicrosoftAspNetCoreRazorTagHelpersHtmlAttributeNameAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeNameAttribute"; + public const string MicrosoftAspNetCoreRazorTagHelpersHtmlAttributeNotBoundAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeNotBoundAttribute"; + public const string MicrosoftAspNetCoreRazorTagHelpersHtmlTargetElementAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute"; + public const string MicrosoftAspNetCoreRazorTagHelpersITagHelper = "Microsoft.AspNetCore.Razor.TagHelpers.ITagHelper"; + public const string MicrosoftAspNetCoreRazorTagHelpersOutputElementHintAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.OutputElementHintAttribute"; + public const string MicrosoftAspNetCoreRazorTagHelpersRestrictChildrenAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.RestrictChildrenAttribute"; + public const string SystemCollectionsGenericIDictionary2 = "System.Collections.Generic.IDictionary`2"; + public const string SystemComponentModelEditorBrowsableAttribute = $"{nameof(System)}.{nameof(System.ComponentModel)}.{nameof(EditorBrowsableAttribute)}"; + public const string SystemThreadingTasksTask1 = "System.Threading.Tasks.Task`1"; +} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/CompilationTagHelperFeature.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/CompilationTagHelperFeature.cs index 09ca7e652ef..ebaec74cbed 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/CompilationTagHelperFeature.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/CompilationTagHelperFeature.cs @@ -4,6 +4,7 @@ #nullable disable using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis.CSharp; @@ -23,7 +24,7 @@ public IReadOnlyList GetDescriptors() var compilation = CSharpCompilation.Create("__TagHelpers", references: _referenceFeature.References); if (IsValidCompilation(compilation)) { - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); } for (var i = 0; i < _providers.Length; i++) @@ -40,7 +41,7 @@ protected override void OnInitialized() _providers = Engine.Features.OfType().OrderBy(f => f.Order).ToArray(); } - internal static bool IsValidCompilation(Compilation compilation) + internal static bool IsValidCompilation([NotNullWhen(true)] Compilation compilation) { var @string = compilation.GetSpecialType(SpecialType.System_String); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/ComponentDetectionConventions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/ComponentDetectionConventions.cs index 0d633ae690b..56799d80d8e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/ComponentDetectionConventions.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/ComponentDetectionConventions.cs @@ -1,10 +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 enable - using System; -using System.Linq; +using System.Collections.Immutable; namespace Microsoft.CodeAnalysis.Razor; @@ -25,24 +23,6 @@ public static bool IsComponent(INamedTypeSymbol symbol, INamedTypeSymbol icompon return symbol.DeclaredAccessibility == Accessibility.Public && !symbol.IsAbstract && - symbol.AllInterfaces.Contains(icomponentSymbol); - } - - public static bool IsComponent(INamedTypeSymbol symbol, string icomponentSymbolName) - { - if (symbol is null) - { - throw new ArgumentNullException(nameof(symbol)); - } - - if (icomponentSymbolName is null) - { - throw new ArgumentNullException(nameof(icomponentSymbolName)); - } - - return - symbol.DeclaredAccessibility == Accessibility.Public && - !symbol.IsAbstract && - symbol.AllInterfaces.Any(s => s.HasFullName(icomponentSymbolName)); + symbol.AllInterfaces.Contains(icomponentSymbol, SymbolEqualityComparer.Default); } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/ComponentTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/ComponentTagHelperDescriptorProvider.cs index 8932cd22a08..e968e006057 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/ComponentTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/ComponentTagHelperDescriptorProvider.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using Microsoft.AspNetCore.Razor.Language; @@ -30,15 +32,15 @@ public void Execute(TagHelperDescriptorProviderContext context) throw new ArgumentNullException(nameof(context)); } - var compilation = context.GetCompilation(); - if (compilation == null) + var typeProvider = context.GetTypeProvider(); + if (!ComponentSymbols.TryCreate(typeProvider, out var symbols)) { // No compilation, nothing to do. return; } var types = new List(); - var visitor = new ComponentTypeVisitor(types); + var visitor = new ComponentTypeVisitor(types, symbols.IComponent); var targetSymbol = context.Items.GetTargetSymbol(); if (targetSymbol is not null) @@ -47,6 +49,7 @@ public void Execute(TagHelperDescriptorProviderContext context) } else { + var compilation = typeProvider.Compilation; visitor.Visit(compilation.Assembly.GlobalNamespace); foreach (var reference in compilation.References) { @@ -64,9 +67,9 @@ public void Execute(TagHelperDescriptorProviderContext context) // Components have very simple matching rules. // 1. The type name (short) matches the tag name. // 2. The fully qualified name matches the tag name. - var shortNameMatchingDescriptor = CreateShortNameMatchingDescriptor(type); + var shortNameMatchingDescriptor = CreateShortNameMatchingDescriptor(symbols, type); context.Results.Add(shortNameMatchingDescriptor); - var fullyQualifiedNameMatchingDescriptor = CreateFullyQualifiedNameMatchingDescriptor(type); + var fullyQualifiedNameMatchingDescriptor = CreateFullyQualifiedNameMatchingDescriptor(symbols, type); context.Results.Add(fullyQualifiedNameMatchingDescriptor); foreach (var childContent in shortNameMatchingDescriptor.GetChildContentProperties()) @@ -78,9 +81,9 @@ public void Execute(TagHelperDescriptorProviderContext context) } } - private TagHelperDescriptor CreateShortNameMatchingDescriptor(INamedTypeSymbol type) + private TagHelperDescriptor CreateShortNameMatchingDescriptor(ComponentSymbols symbols, INamedTypeSymbol type) { - var builder = CreateDescriptorBuilder(type); + var builder = CreateDescriptorBuilder(symbols, type); builder.TagMatchingRule(r => { r.TagName = type.Name; @@ -89,9 +92,9 @@ private TagHelperDescriptor CreateShortNameMatchingDescriptor(INamedTypeSymbol t return builder.Build(); } - private TagHelperDescriptor CreateFullyQualifiedNameMatchingDescriptor(INamedTypeSymbol type) + private TagHelperDescriptor CreateFullyQualifiedNameMatchingDescriptor(ComponentSymbols symbols, INamedTypeSymbol type) { - var builder = CreateDescriptorBuilder(type); + var builder = CreateDescriptorBuilder(symbols, type); var containingNamespace = type.ContainingNamespace.ToDisplayString(); var fullName = $"{containingNamespace}.{type.Name}"; builder.TagMatchingRule(r => @@ -103,7 +106,7 @@ private TagHelperDescriptor CreateFullyQualifiedNameMatchingDescriptor(INamedTyp return builder.Build(); } - private TagHelperDescriptorBuilder CreateDescriptorBuilder(INamedTypeSymbol type) + private TagHelperDescriptorBuilder CreateDescriptorBuilder(ComponentSymbols symbols, INamedTypeSymbol type) { var typeName = type.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat); var assemblyName = type.ContainingAssembly.Identity.Name; @@ -122,11 +125,20 @@ private TagHelperDescriptorBuilder CreateDescriptorBuilder(INamedTypeSymbol type { builder.Metadata[ComponentMetadata.Component.GenericTypedKey] = bool.TrueString; - var cascadeGenericTypeAttributes = type - .GetAttributes() - .Where(a => a.AttributeClass.HasFullName(ComponentsApi.CascadingTypeParameterAttribute.MetadataName)) - .Select(attribute => attribute.ConstructorArguments.FirstOrDefault().Value as string) - .ToList(); + List cascadeGenericTypeAttributes; + if (symbols.CascadingTypeParameterAttribute is { } cascadingTypeParameterAttribute) + { + cascadeGenericTypeAttributes = type + .GetAttributes() + .Where(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, cascadingTypeParameterAttribute)) + .Select(attribute => attribute.ConstructorArguments.FirstOrDefault().Value as string) + .ToList(); + } + else + { + cascadeGenericTypeAttributes = new List(); + } + for (var i = 0; i < type.TypeArguments.Length; i++) { @@ -145,14 +157,14 @@ private TagHelperDescriptorBuilder CreateDescriptorBuilder(INamedTypeSymbol type builder.Documentation = xml; } - foreach (var property in GetProperties(type)) + foreach (var property in GetProperties(symbols, type)) { if (property.kind == PropertyKind.Ignored) { continue; } - CreateProperty(builder, property.property, property.kind); + CreateProperty(symbols, builder, property.property, property.kind); } if (builder.BoundAttributes.Any(a => a.IsParameterizedChildContentProperty()) && @@ -167,14 +179,14 @@ private TagHelperDescriptorBuilder CreateDescriptorBuilder(INamedTypeSymbol type return builder; } - private void CreateProperty(TagHelperDescriptorBuilder builder, IPropertySymbol property, PropertyKind kind) + private void CreateProperty(ComponentSymbols symbols, TagHelperDescriptorBuilder builder, IPropertySymbol property, PropertyKind kind) { builder.BindAttribute(pb => { pb.Name = property.Name; pb.TypeName = property.Type.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat); pb.SetPropertyName(property.Name); - pb.IsEditorRequired = property.GetAttributes().Any(a => a.AttributeClass.HasFullName("Microsoft.AspNetCore.Components.EditorRequiredAttribute")); + pb.IsEditorRequired = property.GetAttributes().Any(static (a, symbols) => SymbolEqualityComparer.Default.Equals(a.AttributeClass, symbols.EditorRequiredAttribute), symbols); pb.SetGloballyQualifiedTypeName(property.Type.ToDisplayString(GloballyQualifiedFullNameTypeDisplayFormat)); if (kind == PropertyKind.Enum) { @@ -445,12 +457,12 @@ private void CreateContextParameter(TagHelperDescriptorBuilder builder, string c // - have the [Parameter] attribute // - have a setter, even if private // - are not indexers - private IEnumerable<(IPropertySymbol property, PropertyKind kind)> GetProperties(INamedTypeSymbol type) + private IEnumerable<(IPropertySymbol property, PropertyKind kind)> GetProperties(ComponentSymbols symbols, INamedTypeSymbol type) { var properties = new Dictionary(StringComparer.Ordinal); do { - if (type.HasFullName(ComponentsApi.ComponentBase.MetadataName)) + if (SymbolEqualityComparer.Default.Equals(type, symbols.ComponentBase)) { // The ComponentBase base class doesn't have any [Parameter]. // Bail out now to avoid walking through its many members, plus the members @@ -503,7 +515,7 @@ private void CreateContextParameter(TagHelperDescriptorBuilder builder, string c kind = PropertyKind.Ignored; } - if (!property.GetAttributes().Any(a => a.AttributeClass.HasFullName(ComponentsApi.ParameterAttribute.MetadataName))) + if (!property.GetAttributes().Any(static (a, symbols) => SymbolEqualityComparer.Default.Equals(a.AttributeClass, symbols.ParameterAttribute), symbols)) { if (property.IsOverride) { @@ -521,7 +533,7 @@ private void CreateContextParameter(TagHelperDescriptorBuilder builder, string c kind = PropertyKind.Enum; } - if (kind == PropertyKind.Default && property.Type.HasFullName(ComponentsApi.RenderFragment.MetadataName)) + if (kind == PropertyKind.Default && SymbolEqualityComparer.Default.Equals(property.Type, symbols.RenderFragment)) { kind = PropertyKind.ChildContent; } @@ -529,12 +541,12 @@ private void CreateContextParameter(TagHelperDescriptorBuilder builder, string c if (kind == PropertyKind.Default && property.Type is INamedTypeSymbol namedType && namedType.IsGenericType && - namedType.ConstructedFrom.HasFullName(ComponentsApi.RenderFragmentOfT.DisplayName)) + SymbolEqualityComparer.Default.Equals(namedType.ConstructedFrom, symbols.RenderFragmentOfT)) { kind = PropertyKind.ChildContent; } - if (kind == PropertyKind.Default && property.Type.HasFullName(ComponentsApi.EventCallback.MetadataName)) + if (kind == PropertyKind.Default && SymbolEqualityComparer.Default.Equals(property.Type, symbols.EventCallback)) { kind = PropertyKind.EventCallback; } @@ -542,7 +554,7 @@ property.Type is INamedTypeSymbol namedType && if (kind == PropertyKind.Default && property.Type is INamedTypeSymbol namedType2 && namedType2.IsGenericType && - namedType2.ConstructedFrom.HasFullName(ComponentsApi.EventCallbackOfT.DisplayName)) + SymbolEqualityComparer.Default.Equals(namedType2.ConstructedFrom, symbols.EventCallbackOfT)) { kind = PropertyKind.EventCallback; } @@ -572,18 +584,145 @@ private enum PropertyKind EventCallback, } +#nullable restore + private class ComponentSymbols + { + private readonly WellKnownTypeProvider _typeProvider; + private INamedTypeSymbol? _editorRequiredAttribute; + + private ComponentSymbols( + WellKnownTypeProvider typeProvider, + INamedTypeSymbol componentBase, + INamedTypeSymbol icomponent, + INamedTypeSymbol parameterAttribute, + INamedTypeSymbol renderFragment, + INamedTypeSymbol renderFragment1, + INamedTypeSymbol eventCallback, + INamedTypeSymbol eventCallback1, + INamedTypeSymbol? cascadingTypeParameterAttribute) + { + _typeProvider = typeProvider; + ComponentBase = componentBase; + IComponent = icomponent; + ParameterAttribute = parameterAttribute; + RenderFragment = renderFragment; + RenderFragmentOfT = renderFragment1; + EventCallback = eventCallback; + EventCallbackOfT = eventCallback1; + CascadingTypeParameterAttribute = cascadingTypeParameterAttribute; + } + + public static bool TryCreate([NotNullWhen(true)] WellKnownTypeProvider? typeProvider, [NotNullWhen(true)] out ComponentSymbols? symbols) + { + // We find a bunch of important and fundamental types here that are needed to discover + // components. If one of these isn't defined then we just bail, because the results will + // be unpredictable. + symbols = null; + if (typeProvider is null) + { + return false; + } + + if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreComponentsComponentBase, out var componentBase)) + { + // No definition for ComponentBase, nothing to do. + return false; + } + + if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreComponentsIComponent, out var icomponent)) + { + // No definition for IComponent, nothing to do. + return false; + } + + if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreComponentsParameterAttribute, out var parameterAttribute)) + { + // No definition for [Parameter], nothing to do. + return false; + } + + if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreComponentsRenderFragment, out var renderFragment)) + { + // No definition for RenderFragment, nothing to do. + return false; + } + + if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreComponentsRenderFragment1, out var renderFragment1)) + { + // No definition for RenderFragment, nothing to do. + return false; + } + + if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreComponentsEventCallback, out var eventCallback)) + { + // No definition for EventCallback, nothing to do. + return false; + } + + if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreComponentsEventCallback1, out var eventCallback1)) + { + // No definition for EventCallback, nothing to do. + return false; + } + + if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreComponentsCascadingTypeParameterAttribute, out var cascadingTypeParameterAttribute)) + { + // No definition for [CascadingTypeParameter]. For back-compat, just don't activate this feature. + } + + symbols = new ComponentSymbols( + typeProvider, + componentBase: componentBase, + icomponent: icomponent, + parameterAttribute: parameterAttribute, + renderFragment: renderFragment, + renderFragment1: renderFragment1, + eventCallback: eventCallback, + eventCallback1: eventCallback1, + cascadingTypeParameterAttribute: cascadingTypeParameterAttribute); + return true; + } + + public INamedTypeSymbol ComponentBase { get; } + + public INamedTypeSymbol IComponent { get; } + + public INamedTypeSymbol ParameterAttribute { get; } + + public INamedTypeSymbol RenderFragment { get; } + + public INamedTypeSymbol RenderFragmentOfT { get; } + + public INamedTypeSymbol EventCallback { get; } + + public INamedTypeSymbol EventCallbackOfT { get; } + + public INamedTypeSymbol? CascadingTypeParameterAttribute { get; } + + public INamedTypeSymbol? EditorRequiredAttribute + { + get + { + return _editorRequiredAttribute ??= _typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreComponentsEditorRequiredAttribute); + } + } + } +#nullable disable + private class ComponentTypeVisitor : SymbolVisitor { private readonly List _results; + private readonly INamedTypeSymbol _icomponentSymbol; - public ComponentTypeVisitor(List results) + public ComponentTypeVisitor(List results, INamedTypeSymbol icomponentSymbol) { _results = results; + _icomponentSymbol = icomponentSymbol; } public override void VisitNamedType(INamedTypeSymbol symbol) { - if (ComponentDetectionConventions.IsComponent(symbol, ComponentsApi.IComponent.MetadataName)) + if (ComponentDetectionConventions.IsComponent(symbol, _icomponentSymbol)) { _results.Add(symbol); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorFactory.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorFactory.cs index f64290b7237..1d46da8db3d 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorFactory.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorFactory.cs @@ -1,14 +1,13 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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.Collections.Immutable; using System.ComponentModel; -using System.Linq; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.CodeAnalysis; namespace Microsoft.CodeAnalysis.Razor; @@ -16,10 +15,26 @@ internal class DefaultTagHelperDescriptorFactory { private const string TagHelperNameEnding = "TagHelper"; - public DefaultTagHelperDescriptorFactory(Compilation compilation, bool includeDocumentation, bool excludeHidden) + private readonly INamedTypeSymbol _htmlAttributeNameAttributeSymbol; + private readonly INamedTypeSymbol _htmlAttributeNotBoundAttributeSymbol; + private readonly INamedTypeSymbol _htmlTargetElementAttributeSymbol; + private readonly INamedTypeSymbol _outputElementHintAttributeSymbol; + private readonly INamedTypeSymbol _iDictionarySymbol; + private readonly INamedTypeSymbol _restrictChildrenAttributeSymbol; + private readonly INamedTypeSymbol _editorBrowsableAttributeSymbol; + + public DefaultTagHelperDescriptorFactory(WellKnownTypeProvider typeProvider, bool includeDocumentation, bool excludeHidden) { IncludeDocumentation = includeDocumentation; ExcludeHidden = excludeHidden; + + _htmlAttributeNameAttributeSymbol = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersHtmlAttributeNameAttribute); + _htmlAttributeNotBoundAttributeSymbol = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersHtmlAttributeNotBoundAttribute); + _htmlTargetElementAttributeSymbol = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersHtmlTargetElementAttribute); + _outputElementHintAttributeSymbol = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersOutputElementHintAttribute); + _restrictChildrenAttributeSymbol = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersRestrictChildrenAttribute); + _editorBrowsableAttributeSymbol = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemComponentModelEditorBrowsableAttribute); + _iDictionarySymbol = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericIDictionary2); } protected bool ExcludeHidden { get; } @@ -60,31 +75,16 @@ public virtual TagHelperDescriptor CreateDescriptor(INamedTypeSymbol type) private void AddTagMatchingRules(INamedTypeSymbol type, TagHelperDescriptorBuilder descriptorBuilder) { - var targetElementAttributes = type - .GetAttributes() - .Where(attribute => attribute.AttributeClass.HasFullName(TagHelperTypes.HtmlTargetElementAttribute)); - - // If there isn't an attribute specifying the tag name derive it from the name - if (!targetElementAttributes.Any()) + var hasAnyTargetElementAttribute = false; + foreach (var attribute in type.GetAttributes()) { - var name = type.Name; - - if (name.EndsWith(TagHelperNameEnding, StringComparison.OrdinalIgnoreCase)) + if (!SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, _htmlTargetElementAttributeSymbol)) { - name = name.Substring(0, name.Length - TagHelperNameEnding.Length); + continue; } - descriptorBuilder.TagMatchingRule(ruleBuilder => - { - var htmlCasedName = HtmlConventions.ToHtmlCase(name); - ruleBuilder.TagName = htmlCasedName; - }); - - return; - } - - foreach (var targetElementAttribute in targetElementAttributes) - { + hasAnyTargetElementAttribute = true; + var targetElementAttribute = attribute; descriptorBuilder.TagMatchingRule(ruleBuilder => { var tagName = HtmlTargetElementAttribute_Tag(targetElementAttribute); @@ -100,6 +100,25 @@ private void AddTagMatchingRules(INamedTypeSymbol type, TagHelperDescriptorBuild RequiredAttributeParser.AddRequiredAttributes(requiredAttributeString, ruleBuilder); }); } + + // If there isn't an attribute specifying the tag name derive it from the name + if (!hasAnyTargetElementAttribute) + { + var name = type.Name; + + if (name.EndsWith(TagHelperNameEnding, StringComparison.OrdinalIgnoreCase)) + { + name = name.Substring(0, name.Length - TagHelperNameEnding.Length); + } + + descriptorBuilder.TagMatchingRule(ruleBuilder => + { + var htmlCasedName = HtmlConventions.ToHtmlCase(name); + ruleBuilder.TagName = htmlCasedName; + }); + + return; + } } private void AddBoundAttributes(INamedTypeSymbol type, TagHelperDescriptorBuilder builder) @@ -121,7 +140,7 @@ private void AddBoundAttributes(INamedTypeSymbol type, TagHelperDescriptorBuilde private void AddAllowedChildren(INamedTypeSymbol type, TagHelperDescriptorBuilder builder) { - var restrictChildrenAttribute = type.GetAttributes().FirstOrDefault(a => a.AttributeClass.HasFullName(TagHelperTypes.RestrictChildrenAttribute)); + var restrictChildrenAttribute = type.GetAttributes().FirstOrDefault(static (a, self) => SymbolEqualityComparer.Default.Equals(a.AttributeClass, self._restrictChildrenAttributeSymbol), this); if (restrictChildrenAttribute == null) { return; @@ -156,7 +175,7 @@ private void AddDocumentation(INamedTypeSymbol type, TagHelperDescriptorBuilder private void AddTagOutputHint(INamedTypeSymbol type, TagHelperDescriptorBuilder builder) { string outputElementHint = null; - var outputElementHintAttribute = type.GetAttributes().FirstOrDefault(a => a.AttributeClass.HasFullName(TagHelperTypes.OutputElementHintAttribute)); + var outputElementHintAttribute = type.GetAttributes().FirstOrDefault(static (a, self) => SymbolEqualityComparer.Default.Equals(a.AttributeClass, self._outputElementHintAttributeSymbol), this); if (outputElementHintAttribute != null) { outputElementHint = (string)(outputElementHintAttribute.ConstructorArguments[0]).Value; @@ -171,7 +190,7 @@ private void ConfigureBoundAttribute( { var attributeNameAttribute = property .GetAttributes() - .FirstOrDefault(a => a.AttributeClass.HasFullName(TagHelperTypes.HtmlAttributeNameAttribute)); + .FirstOrDefault(static (a, self) => SymbolEqualityComparer.Default.Equals(a.AttributeClass, self._htmlAttributeNameAttributeSymbol), this); bool hasExplicitName; string attributeName; @@ -291,13 +310,13 @@ private void ConfigureDictionaryBoundAttribute( private IReadOnlyList GetDictionaryArgumentTypes(IPropertySymbol property) { INamedTypeSymbol dictionaryType; - if ((property.Type as INamedTypeSymbol)?.ConstructedFrom.HasFullName(TagHelperTypes.IDictionary) == true) + if (SymbolEqualityComparer.Default.Equals((property.Type as INamedTypeSymbol)?.ConstructedFrom, _iDictionarySymbol)) { dictionaryType = (INamedTypeSymbol)property.Type; } - else if (property.Type.AllInterfaces.Any(s => s.ConstructedFrom.HasFullName(TagHelperTypes.IDictionary))) + else if (property.Type.AllInterfaces.Any(static (s, self) => SymbolEqualityComparer.Default.Equals(s.ConstructedFrom, self._iDictionarySymbol), this)) { - dictionaryType = property.Type.AllInterfaces.First(s => s.ConstructedFrom.HasFullName(TagHelperTypes.IDictionary)); + dictionaryType = property.Type.AllInterfaces.First(static (s, self) => SymbolEqualityComparer.Default.Equals(s.ConstructedFrom, self._iDictionarySymbol), this); } else { @@ -361,7 +380,7 @@ private static TagStructure HtmlTargetElementAttribute_TagStructure(AttributeDat private bool IsPotentialDictionaryProperty(IPropertySymbol property) { return - ((property.Type as INamedTypeSymbol)?.ConstructedFrom.HasFullName(TagHelperTypes.IDictionary) == true || property.Type.AllInterfaces.Any(s => s.ConstructedFrom.HasFullName(TagHelperTypes.IDictionary))) && + (SymbolEqualityComparer.Default.Equals((property.Type as INamedTypeSymbol)?.ConstructedFrom, _iDictionarySymbol) || property.Type.AllInterfaces.Any(static (s, self) => SymbolEqualityComparer.Default.Equals(s.ConstructedFrom, self._iDictionarySymbol), this)) && GetDictionaryArgumentTypes(property)?[0].SpecialType == SpecialType.System_String; } @@ -378,8 +397,8 @@ private IEnumerable GetAccessibleProperties(INamedTypeSymbol ty property.Parameters.Length == 0 && property.GetMethod != null && property.GetMethod.DeclaredAccessibility == Accessibility.Public && - property.GetAttributes().FirstOrDefault(a => a.AttributeClass.HasFullName(TagHelperTypes.HtmlAttributeNotBoundAttribute)) == null && - (property.GetAttributes().Any(a => a.AttributeClass.HasFullName(TagHelperTypes.HtmlAttributeNameAttribute)) || + property.GetAttributes().FirstOrDefault(static (a, self) => SymbolEqualityComparer.Default.Equals(a.AttributeClass, self._htmlAttributeNotBoundAttributeSymbol), this) == null && + (property.GetAttributes().Any(static (a, self) => SymbolEqualityComparer.Default.Equals(a.AttributeClass, self._htmlAttributeNameAttributeSymbol), this) || property.SetMethod != null && property.SetMethod.DeclaredAccessibility == Accessibility.Public || IsPotentialDictionaryProperty(property)) && !accessibleProperties.ContainsKey(property.Name)) @@ -399,7 +418,7 @@ private bool ShouldSkipDescriptorCreation(ISymbol symbol) { if (ExcludeHidden) { - var editorBrowsableAttribute = symbol.GetAttributes().FirstOrDefault(a => a.AttributeClass.HasFullName(typeof(EditorBrowsableAttribute).FullName)); + var editorBrowsableAttribute = symbol.GetAttributes().FirstOrDefault(static (a, self) => SymbolEqualityComparer.Default.Equals(a.AttributeClass, self._editorBrowsableAttributeSymbol), this); if (editorBrowsableAttribute == null) { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorProvider.cs index e2f7a88c6e0..9254527c1c1 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorProvider.cs @@ -20,15 +20,15 @@ public void Execute(TagHelperDescriptorProviderContext context) throw new ArgumentNullException(nameof(context)); } - var compilation = context.GetCompilation(); - if (compilation == null) + var typeProvider = context.GetTypeProvider(); + if (typeProvider == null) { // No compilation, nothing to do. return; } - var iTagHelper = compilation.GetTypeByMetadataName(TagHelperTypes.ITagHelper); - if (iTagHelper == null || iTagHelper.TypeKind == TypeKind.Error) + if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersITagHelper, out var iTagHelper) + || iTagHelper.TypeKind == TypeKind.Error) { // Could not find attributes we care about in the compilation. Nothing to do. return; @@ -44,6 +44,7 @@ public void Execute(TagHelperDescriptorProviderContext context) } else { + var compilation = typeProvider.Compilation; visitor.Visit(compilation.Assembly.GlobalNamespace); foreach (var reference in compilation.References) { @@ -58,7 +59,7 @@ public void Execute(TagHelperDescriptorProviderContext context) } - var factory = new DefaultTagHelperDescriptorFactory(compilation, context.IncludeDocumentation, context.ExcludeHidden); + var factory = new DefaultTagHelperDescriptorFactory(typeProvider, context.IncludeDocumentation, context.ExcludeHidden); for (var i = 0; i < types.Count; i++) { var descriptor = factory.CreateDescriptor(types[i]); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/SymbolExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/SymbolExtensions.cs index c6056b8d906..57edcf37eae 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/SymbolExtensions.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/SymbolExtensions.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; @@ -12,8 +12,50 @@ internal static class SymbolExtensions .WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted) .RemoveMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); - internal static bool HasFullName(this ISymbol symbol, string fullName) + internal static SymbolVisibility GetResultantVisibility(this ISymbol symbol) { - return symbol.ToDisplayString(FullNameTypeDisplayFormat).Equals(fullName, StringComparison.Ordinal); + // Start by assuming it's visible. + SymbolVisibility visibility = SymbolVisibility.Public; + + switch (symbol.Kind) + { + case SymbolKind.Alias: + // Aliases are uber private. They're only visible in the same file that they + // were declared in. + return SymbolVisibility.Private; + + case SymbolKind.Parameter: + // Parameters are only as visible as their containing symbol + return GetResultantVisibility(symbol.ContainingSymbol); + + case SymbolKind.TypeParameter: + // Type Parameters are private. + return SymbolVisibility.Private; + } + + while (symbol != null && symbol.Kind != SymbolKind.Namespace) + { + switch (symbol.DeclaredAccessibility) + { + // If we see anything private, then the symbol is private. + case Accessibility.NotApplicable: + case Accessibility.Private: + return SymbolVisibility.Private; + + // If we see anything internal, then knock it down from public to + // internal. + case Accessibility.Internal: + case Accessibility.ProtectedAndInternal: + visibility = SymbolVisibility.Internal; + break; + + // For anything else (Public, Protected, ProtectedOrInternal), the + // symbol stays at the level we've gotten so far. + } + + symbol = symbol.ContainingSymbol; + } + + return visibility; } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/SymbolVisibility.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/SymbolVisibility.cs new file mode 100644 index 00000000000..020f2ff4351 --- /dev/null +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/SymbolVisibility.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.CodeAnalysis.Razor; + +internal enum SymbolVisibility +{ + Public = 0, + Internal = 1, + Private = 2, +} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/SymbolVisibilityExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/SymbolVisibilityExtensions.cs new file mode 100644 index 00000000000..b1e45c5e6e7 --- /dev/null +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/SymbolVisibilityExtensions.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.CodeAnalysis.Razor; + +/// +/// Extensions for . +/// +internal static class SymbolVisibilityExtensions +{ + /// + /// Determines whether is at least as visible as . + /// + /// The visibility to compare against. + /// The visibility to compare with. + /// True if one can say that is at least as visible as . + /// + /// For example, is at least as visible as , but is not as visible as . + /// + public static bool IsAtLeastAsVisibleAs(this SymbolVisibility typeVisibility, SymbolVisibility comparisonVisibility) + { + return typeVisibility switch + { + SymbolVisibility.Public => true, + SymbolVisibility.Internal => comparisonVisibility != SymbolVisibility.Public, + SymbolVisibility.Private => comparisonVisibility == SymbolVisibility.Private, + _ => throw new ArgumentOutOfRangeException(nameof(typeVisibility), typeVisibility, null), + }; + } +} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/TagHelperDescriptorProviderContextExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/TagHelperDescriptorProviderContextExtensions.cs index 23273e15454..a27f9709974 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/TagHelperDescriptorProviderContextExtensions.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/TagHelperDescriptorProviderContextExtensions.cs @@ -20,6 +20,7 @@ public static Compilation GetCompilation(this TagHelperDescriptorProviderContext return (Compilation)context.Items[typeof(Compilation)]; } + [Obsolete($"Use {nameof(SetTypeProvider)} instead.")] public static void SetCompilation(this TagHelperDescriptorProviderContext context, Compilation compilation) { if (context == null) @@ -27,6 +28,27 @@ public static void SetCompilation(this TagHelperDescriptorProviderContext contex throw new ArgumentNullException(nameof(context)); } - context.Items[typeof(Compilation)] = compilation; + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); + } + + public static WellKnownTypeProvider GetTypeProvider(this TagHelperDescriptorProviderContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + return (WellKnownTypeProvider)context.Items[typeof(WellKnownTypeProvider)]; + } + + public static void SetTypeProvider(this TagHelperDescriptorProviderContext context, WellKnownTypeProvider typeProvider) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.Items[typeof(Compilation)] = typeProvider.Compilation; + context.Items[typeof(WellKnownTypeProvider)] = typeProvider; } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/TagHelperTypes.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/TagHelperTypes.cs index 427e51d18d0..27004679d7b 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/TagHelperTypes.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/TagHelperTypes.cs @@ -3,25 +3,27 @@ #nullable disable +using Microsoft.AspNetCore.Razor.Language; + namespace Microsoft.CodeAnalysis.Razor; internal static class TagHelperTypes { - public const string ITagHelper = "Microsoft.AspNetCore.Razor.TagHelpers.ITagHelper"; + public const string ITagHelper = WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersITagHelper; - public const string IComponent = "Microsoft.AspNetCore.Components.IComponent"; + public const string IComponent = WellKnownTypeNames.MicrosoftAspNetCoreComponentsIComponent; - public const string IDictionary = "System.Collections.Generic.IDictionary"; + public const string IDictionary = WellKnownTypeNames.SystemCollectionsGenericIDictionary2; - public const string HtmlAttributeNameAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeNameAttribute"; + public const string HtmlAttributeNameAttribute = WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersHtmlAttributeNameAttribute; - public const string HtmlAttributeNotBoundAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeNotBoundAttribute"; + public const string HtmlAttributeNotBoundAttribute = WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersHtmlAttributeNotBoundAttribute; - public const string HtmlTargetElementAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute"; + public const string HtmlTargetElementAttribute = WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersHtmlTargetElementAttribute; - public const string OutputElementHintAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.OutputElementHintAttribute"; + public const string OutputElementHintAttribute = WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersOutputElementHintAttribute; - public const string RestrictChildrenAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.RestrictChildrenAttribute"; + public const string RestrictChildrenAttribute = WellKnownTypeNames.MicrosoftAspNetCoreRazorTagHelpersRestrictChildrenAttribute; public static class HtmlAttributeName { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/src/WellKnownTypeProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/WellKnownTypeProvider.cs new file mode 100644 index 00000000000..c9d7f8af09f --- /dev/null +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/src/WellKnownTypeProvider.cs @@ -0,0 +1,292 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Threading; +using Microsoft.AspNetCore.Razor.Language; + +namespace Microsoft.CodeAnalysis.Razor; + +/// +/// Provides and caches well known types in a compilation. +/// +public class WellKnownTypeProvider +{ + public WellKnownTypeProvider(Compilation compilation) + { + Compilation = compilation; + _fullNameToTypeMap = new ConcurrentDictionary(StringComparer.Ordinal); + _referencedAssemblies = new Lazy>( + () => + { + return ImmutableHashSet.Create( + SymbolEqualityComparer.Default, + Compilation.Assembly.Modules + .SelectMany(m => m.ReferencedAssemblySymbols) + .ToArray()); + }, + LazyThreadSafetyMode.ExecutionAndPublication); + } + + public Compilation Compilation { get; } + + /// + /// All the referenced assembly symbols. + /// + /// + /// Seems to be less memory intensive than: + /// foreach (Compilation.Assembly.Modules) + /// foreach (Module.ReferencedAssemblySymbols) + /// + private readonly Lazy> _referencedAssemblies; + + /// + /// Mapping of full name to . + /// + private readonly ConcurrentDictionary _fullNameToTypeMap; + + /// + /// Static cache of full type names (with namespaces) to namespace name parts, + /// so we can query . + /// + /// + /// Example: "System.Collections.Generic.List`1" => [ "System", "Collections", "Generic" ] + /// + /// https://github.com/dotnet/roslyn/blob/9e786147b8cb884af454db081bb747a5bd36a086/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs#L455 + /// suggests the TypeNames collection can be checked to avoid expensive operations. But realizing TypeNames seems to be + /// as memory intensive as unnecessary calls GetTypeByMetadataName() in some cases. So we'll go with namespace names. + /// + private static readonly ConcurrentDictionary> _fullTypeNameToNamespaceNames = + new(StringComparer.Ordinal); + + /// + /// Attempts to get the type by the full type name. + /// + /// Namespace + type name, e.g. "System.Exception". + /// Named type symbol, if any. + /// True if found in the compilation, false otherwise. + ////[PerformanceSensitive("https://github.com/dotnet/roslyn-analyzers/issues/4893", AllowCaptures = false)] + public bool TryGetOrCreateTypeByMetadataName( + string fullTypeName, + [NotNullWhen(returnValue: true)] out INamedTypeSymbol? namedTypeSymbol) + { + if (_fullNameToTypeMap.TryGetValue(fullTypeName, out namedTypeSymbol)) + { + return namedTypeSymbol is not null; + } + + return TryGetOrCreateTypeByMetadataNameSlow(fullTypeName, out namedTypeSymbol); + } + + private bool TryGetOrCreateTypeByMetadataNameSlow( + string fullTypeName, + [NotNullWhen(returnValue: true)] out INamedTypeSymbol? namedTypeSymbol) + { + namedTypeSymbol = _fullNameToTypeMap.GetOrAdd( + fullTypeName, + fullyQualifiedMetadataName => + { + // Caching null results is intended. + + // sharwell says: Suppose you reference assembly A with public API X.Y, and you reference assembly B with + // internal API X.Y. Even though you can use X.Y from assembly A, compilation.GetTypeByMetadataName will + // fail outright because it finds two types with the same name. + + INamedTypeSymbol? type = null; + + ImmutableArray namespaceNames; + if (string.IsInterned(fullTypeName) != null) + { + namespaceNames = _fullTypeNameToNamespaceNames.GetOrAdd( + fullTypeName, + GetNamespaceNamesFromFullTypeName); + } + else + { + namespaceNames = GetNamespaceNamesFromFullTypeName(fullTypeName); + } + + if (IsSubsetOfCollection(namespaceNames, Compilation.Assembly.NamespaceNames)) + { + type = Compilation.Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName); + } + + if (type is null) + { + Debug.Assert(namespaceNames != null); + + foreach (IAssemblySymbol? referencedAssembly in _referencedAssemblies.Value) + { + if (!IsSubsetOfCollection(namespaceNames, referencedAssembly.NamespaceNames)) + { + continue; + } + + var currentType = referencedAssembly.GetTypeByMetadataName(fullyQualifiedMetadataName); + if (currentType is null) + { + continue; + } + + switch (currentType.GetResultantVisibility()) + { + case SymbolVisibility.Public: + case SymbolVisibility.Internal when referencedAssembly.GivesAccessTo(Compilation.Assembly): + break; + + default: + continue; + } + + if (type is object) + { + // Multiple visible types with the same metadata name are present. + return null; + } + + type = currentType; + } + } + + return type; + }); + + return namedTypeSymbol != null; + } + + /// + /// Gets a type by its full type name. + /// + /// Namespace + type name, e.g. "System.Exception". + /// The if found, null otherwise. + public INamedTypeSymbol? GetOrCreateTypeByMetadataName(string fullTypeName) + { + TryGetOrCreateTypeByMetadataName(fullTypeName, out INamedTypeSymbol? namedTypeSymbol); + return namedTypeSymbol; + } + + /// + /// Determines if is a with its type + /// argument satisfying . + /// + /// Type potentially representing a . + /// Predicate to check the 's type argument. + /// True if is a with its + /// type argument satisfying , false otherwise. + internal bool IsTaskOfType([NotNullWhen(returnValue: true)] ITypeSymbol? typeSymbol, Func typeArgumentPredicate) + { + return typeSymbol != null + && typeSymbol.OriginalDefinition != null + && SymbolEqualityComparer.Default.Equals(typeSymbol.OriginalDefinition, + GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask1)) + && typeSymbol is INamedTypeSymbol namedTypeSymbol + && namedTypeSymbol.TypeArguments.Length == 1 + && typeArgumentPredicate(namedTypeSymbol.TypeArguments[0]); + } + + private static ImmutableArray GetNamespaceNamesFromFullTypeName(string fullTypeName) + { + var namespaceNamesBuilder = ImmutableArray.CreateBuilder(); + int prevStartIndex = 0; + for (int i = 0; i < fullTypeName.Length; i++) + { + if (fullTypeName[i] == '.') + { + namespaceNamesBuilder.Add(fullTypeName[prevStartIndex..i]); + prevStartIndex = i + 1; + } + else if (!IsIdentifierPartCharacter(fullTypeName[i])) + { + break; + } + } + + return namespaceNamesBuilder.ToImmutable(); + } + + /// + /// Returns true if the Unicode character can be a part of an identifier. + /// + /// The Unicode character. + private static bool IsIdentifierPartCharacter(char ch) + { + // identifier-part-character: + // letter-character + // decimal-digit-character + // connecting-character + // combining-character + // formatting-character + + if (ch < 'a') // '\u0061' + { + if (ch < 'A') // '\u0041' + { + return ch is >= '0' // '\u0030' + and <= '9'; // '\u0039' + } + + return ch is <= 'Z' // '\u005A' + or '_'; // '\u005F' + } + + if (ch <= 'z') // '\u007A' + { + return true; + } + + if (ch <= '\u007F') // max ASCII + { + return false; + } + + UnicodeCategory cat = CharUnicodeInfo.GetUnicodeCategory(ch); + + ////return IsLetterChar(cat) + //// || IsDecimalDigitChar(cat) + //// || IsConnectingChar(cat) + //// || IsCombiningChar(cat) + //// || IsFormattingChar(cat); + + return cat switch + { + // Letter + UnicodeCategory.UppercaseLetter + or UnicodeCategory.LowercaseLetter + or UnicodeCategory.TitlecaseLetter + or UnicodeCategory.ModifierLetter + or UnicodeCategory.OtherLetter + or UnicodeCategory.LetterNumber + or UnicodeCategory.DecimalDigitNumber + or UnicodeCategory.ConnectorPunctuation + or UnicodeCategory.NonSpacingMark + or UnicodeCategory.SpacingCombiningMark + or UnicodeCategory.Format => true, + _ => false, + }; + } + + private static bool IsSubsetOfCollection(ImmutableArray set1, ICollection set2) + { + if (set1.Length > set2.Count) + { + return false; + } + + for (int i = 0; i < set1.Length; i++) + { + if (!set2.Contains(set1[i])) + { + return false; + } + } + + return true; + } +} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/BaseTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/BaseTagHelperDescriptorProviderTest.cs index 3478ce8e9ce..783aaaae4c9 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/BaseTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/BaseTagHelperDescriptorProviderTest.cs @@ -18,11 +18,14 @@ public abstract class TagHelperDescriptorProviderTestBase static TagHelperDescriptorProviderTestBase() { BaseCompilation = TestCompilation.Create(typeof(ComponentTagHelperDescriptorProviderTest).Assembly); + BaseTypeProvider = new WellKnownTypeProvider(BaseCompilation); CSharpParseOptions = new CSharpParseOptions(LanguageVersion.CSharp7_3); } protected static Compilation BaseCompilation { get; } + protected static WellKnownTypeProvider BaseTypeProvider { get; } + protected static CSharpParseOptions CSharpParseOptions { get; } protected static CSharpSyntaxTree Parse(string text) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/BindTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/BindTagHelperDescriptorProviderTest.cs index 0dd4c38ef17..5e90e3a0fb5 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/BindTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/BindTagHelperDescriptorProviderTest.cs @@ -50,7 +50,7 @@ public Task SetParametersAsync(ParameterView parameters) Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); // We run after component discovery and depend on the results. var componentProvider = new ComponentTagHelperDescriptorProvider(); @@ -203,7 +203,7 @@ public Task SetParametersAsync(ParameterView parameters) Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); // We run after component discovery and depend on the results. var componentProvider = new ComponentTagHelperDescriptorProvider(); @@ -354,7 +354,7 @@ public Task SetParametersAsync(ParameterView parameters) Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); // We run after component discovery and depend on the results. var componentProvider = new ComponentTagHelperDescriptorProvider(); @@ -390,7 +390,7 @@ public class BindAttributes Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new BindTagHelperDescriptorProvider(); @@ -651,7 +651,7 @@ public class BindAttributes Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new BindTagHelperDescriptorProvider(); @@ -736,7 +736,7 @@ public class BindAttributes Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new BindTagHelperDescriptorProvider(); @@ -812,7 +812,7 @@ public class BindAttributes Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new BindTagHelperDescriptorProvider(); @@ -909,7 +909,7 @@ public class BindAttributes Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new BindTagHelperDescriptorProvider(); @@ -1008,7 +1008,7 @@ public class BindAttributes Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new BindTagHelperDescriptorProvider(); @@ -1033,11 +1033,11 @@ public class BindAttributes public void Execute_BindFallback_CreatesDescriptor() { // Arrange - var compilation = BaseCompilation; + var (compilation, typeProvider) = (BaseCompilation, BaseTypeProvider); Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(typeProvider); var provider = new BindTagHelperDescriptorProvider(); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/ComponentTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/ComponentTagHelperDescriptorProviderTest.cs index b233dd10a41..96947c2b5bb 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/ComponentTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/ComponentTagHelperDescriptorProviderTest.cs @@ -42,7 +42,7 @@ public Task SetParametersAsync(ParameterView parameters) Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -167,7 +167,7 @@ public Task SetParametersAsync(ParameterView parameters) Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -231,7 +231,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -273,7 +273,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -313,7 +313,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -353,7 +353,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -406,7 +406,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -454,7 +454,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -502,7 +502,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -566,7 +566,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -641,7 +641,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -691,7 +691,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -753,7 +753,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -804,7 +804,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -859,7 +859,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -922,7 +922,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -979,7 +979,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -1054,7 +1054,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -1126,7 +1126,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -1208,7 +1208,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -1290,7 +1290,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -1376,7 +1376,7 @@ public class Context Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -1462,7 +1462,7 @@ public class MyComponent : ComponentBase Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -1554,7 +1554,7 @@ public string this[int i] Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -1610,7 +1610,7 @@ public class MyDerivedComponent2 : MyDerivedComponent1 Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); @@ -1670,7 +1670,7 @@ public Task SetParametersAsync(ParameterView parameters) Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); context.Items.SetTargetSymbol((IAssemblySymbol)compilation.GetAssemblyOrModuleSymbol(compilation.References.First(r => r.Display.Contains("Microsoft.CodeAnalysis.Razor.Test.dll")))); var provider = new ComponentTagHelperDescriptorProvider(); @@ -1716,7 +1716,7 @@ public Task SetParametersAsync(ParameterView parameters) Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new ComponentTagHelperDescriptorProvider(); // Act diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorFactoryTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorFactoryTest.cs index daa95da2827..990eea0884e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorFactoryTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorFactoryTest.cs @@ -26,6 +26,7 @@ public class DefaultTagHelperDescriptorFactoryTest protected static readonly string AssemblyName = TagHelperDescriptorFactoryTestAssembly.Name; private static Compilation Compilation { get; } = TestCompilation.Create(_assembly); + private static WellKnownTypeProvider TypeProvider { get; } = new WellKnownTypeProvider(Compilation); public static TheoryData RequiredAttributeParserErrorData { @@ -403,7 +404,7 @@ public void CreateDescriptor_IsEnumIsSetCorrectly( TagHelperDescriptor expectedDescriptor) { // Arrange - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(tagHelperType.FullName); // Act @@ -460,7 +461,7 @@ public void CreateDescriptor_CreatesDesignTimeDescriptorsWithRequiredParent( TagHelperDescriptor expectedDescriptor) { // Arrange - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(tagHelperType.FullName); // Act @@ -522,7 +523,7 @@ public void CreateDescriptor_CreatesDescriptorsWithAllowedChildren( TagHelperDescriptor expectedDescriptor) { // Arrange - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(tagHelperType.FullName); // Act @@ -588,7 +589,7 @@ public void CreateDescriptor_CreatesDesignTimeDescriptorsWithTagStructure( TagHelperDescriptor expectedDescriptor) { // Arrange - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(tagHelperType.FullName); // Act @@ -783,7 +784,7 @@ public void CreateDescriptor_UnderstandsEditorBrowsableAttribute( TagHelperDescriptor expectedDescriptor) { // Arrange - var factory = new DefaultTagHelperDescriptorFactory(Compilation, designTime, designTime); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, designTime, designTime); var typeSymbol = Compilation.GetTypeByMetadataName(tagHelperType.FullName); // Act @@ -1004,7 +1005,7 @@ public void CreateDescriptor_ReturnsExpectedDescriptors( TagHelperDescriptor expectedDescriptor) { // Arrange - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(tagHelperType.FullName); // Act @@ -1041,7 +1042,7 @@ public void CreateDescriptor_HtmlCasesTagNameAndAttributeName( string expectedAttributeName) { // Arrange - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(tagHelperType.FullName); // Act @@ -1080,7 +1081,7 @@ public void CreateDescriptor_OverridesAttributeNameFromAttribute() .PropertyName(validProperty2.Name) .TypeName(validProperty2.PropertyType.FullName), }); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(OverriddenAttributeTagHelper).FullName); // Act @@ -1116,7 +1117,7 @@ public void CreateDescriptor_DoesNotInheritOverridenAttributeName() .PropertyName(validProperty2.Name) .TypeName(validProperty2.PropertyType.FullName), }); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(InheritedOverriddenAttributeTagHelper).FullName); // Act @@ -1152,7 +1153,7 @@ public void CreateDescriptor_AllowsOverriddenAttributeNameOnUnimplementedVirtual .PropertyName(validProperty2.Name) .TypeName(validProperty2.PropertyType.FullName), }); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(InheritedNotOverriddenAttributeTagHelper).FullName); // Act @@ -1179,7 +1180,7 @@ public void CreateDescriptor_BuildsDescriptorsWithInheritedProperties() .PropertyName(nameof(InheritedSingleAttributeTagHelper.IntAttribute)) .TypeName(typeof(int).FullName) }); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(InheritedSingleAttributeTagHelper).FullName); // Act @@ -1207,7 +1208,7 @@ public void CreateDescriptor_BuildsDescriptorsWithConventionNames() .PropertyName(intProperty.Name) .TypeName(intProperty.PropertyType.FullName) }); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(SingleAttributeTagHelper).FullName); // Act @@ -1236,7 +1237,7 @@ public void CreateDescriptor_OnlyAcceptsPropertiesWithGetAndSet() .PropertyName(validProperty.Name) .TypeName(validProperty.PropertyType.FullName) }); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(MissingAccessorTagHelper).FullName); // Act @@ -1265,7 +1266,7 @@ public void CreateDescriptor_OnlyAcceptsPropertiesWithPublicGetAndSet() .PropertyName(validProperty.Name) .TypeName(validProperty.PropertyType.FullName) }); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(NonPublicAccessorTagHelper).FullName); // Act @@ -1292,7 +1293,7 @@ public void CreateDescriptor_DoesNotIncludePropertiesWithNotBound() .PropertyName(nameof(NotBoundAttributeTagHelper.BoundProperty)) .TypeName(typeof(object).FullName) }); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(NotBoundAttributeTagHelper).FullName); // Act @@ -1325,7 +1326,7 @@ public void CreateDescriptor_ResolvesMultipleTagHelperDescriptorsFromSingleType( builder => builder.RequireTagName("p"), builder => builder.RequireTagName("div"), }); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(MultiTagTagHelper).FullName); // Act @@ -1353,7 +1354,7 @@ public void CreateDescriptor_DoesNotResolveInheritedTagNames() .PropertyName(validProp.Name) .TypeName(validProp.PropertyType.FullName), }); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(InheritedMultiTagTagHelper).FullName); // Act @@ -1379,7 +1380,7 @@ public void CreateDescriptor_IgnoresDuplicateTagNamesFromAttribute() builder => builder.RequireTagName("div"), }); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(DuplicateTagNameTagHelper).FullName); // Act @@ -1400,7 +1401,7 @@ public void CreateDescriptor_OverridesTagNameFromAttribute() AssemblyName, typeNamespace: typeof(OverrideNameTagHelper).FullName.Substring(0, typeof(OverrideNameTagHelper).FullName.Length - nameof(OverrideNameTagHelper).Length -1), typeNameIdentifier: nameof(OverrideNameTagHelper)); - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(OverrideNameTagHelper).FullName); // Act @@ -1440,9 +1441,10 @@ public class DynamicTestTagHelper : {typeof(AspNetCore.Razor.TagHelpers.TagHelpe }}"; var syntaxTree = CSharpSyntaxTree.ParseText(text); var compilation = TestCompilation.Create(_assembly, syntaxTree); + var typeProvider = new WellKnownTypeProvider(compilation); var tagHelperType = compilation.GetTypeByMetadataName("DynamicTestTagHelper"); var attribute = tagHelperType.GetAttributes().Single(); - var factory = new DefaultTagHelperDescriptorFactory(compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(typeProvider, includeDocumentation: false, excludeHidden: false); // Act var descriptor = factory.CreateDescriptor(tagHelperType); @@ -1574,7 +1576,7 @@ public void CreateDescriptor_DoesNotAllowDataDashAttributes( IEnumerable expectedAttributeDescriptors) { // Arrange - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(type.FullName); // Act @@ -1623,8 +1625,9 @@ public class DynamicTestTagHelper : {typeof(AspNetCore.Razor.TagHelpers.TagHelpe }}"; var syntaxTree = CSharpSyntaxTree.ParseText(text); var compilation = TestCompilation.Create(_assembly, syntaxTree); + var typeProvider = new WellKnownTypeProvider(compilation); var tagHelperType = compilation.GetTypeByMetadataName("DynamicTestTagHelper"); - var factory = new DefaultTagHelperDescriptorFactory(compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(typeProvider, includeDocumentation: false, excludeHidden: false); // Act var descriptor = factory.CreateDescriptor(tagHelperType); @@ -1663,8 +1666,9 @@ public class DynamicTestTagHelper : {typeof(AspNetCore.Razor.TagHelpers.TagHelpe }}"; var syntaxTree = CSharpSyntaxTree.ParseText(text); var compilation = TestCompilation.Create(_assembly, syntaxTree); + var typeProvider = new WellKnownTypeProvider(compilation); var tagHelperType = compilation.GetTypeByMetadataName("DynamicTestTagHelper"); - var factory = new DefaultTagHelperDescriptorFactory(compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(typeProvider, includeDocumentation: false, excludeHidden: false); // Act var descriptor = factory.CreateDescriptor(tagHelperType); @@ -1706,8 +1710,9 @@ public class DynamicTestTagHelper : {typeof(AspNetCore.Razor.TagHelpers.TagHelpe }}"; var syntaxTree = CSharpSyntaxTree.ParseText(text); var compilation = TestCompilation.Create(_assembly, syntaxTree); + var typeProvider = new WellKnownTypeProvider(compilation); var tagHelperType = compilation.GetTypeByMetadataName("DynamicTestTagHelper"); - var factory = new DefaultTagHelperDescriptorFactory(compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(typeProvider, includeDocumentation: false, excludeHidden: false); // Act var descriptor = factory.CreateDescriptor(tagHelperType); @@ -1752,8 +1757,9 @@ public class DynamicTestTagHelper : {typeof(AspNetCore.Razor.TagHelpers.TagHelpe }}"; var syntaxTree = CSharpSyntaxTree.ParseText(text); var compilation = TestCompilation.Create(_assembly, syntaxTree); + var typeProvider = new WellKnownTypeProvider(compilation); var tagHelperType = compilation.GetTypeByMetadataName("DynamicTestTagHelper"); - var factory = new DefaultTagHelperDescriptorFactory(compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(typeProvider, includeDocumentation: false, excludeHidden: false); // Act var descriptor = factory.CreateDescriptor(tagHelperType); @@ -1794,8 +1800,9 @@ public class DynamicTestTagHelper : {typeof(AspNetCore.Razor.TagHelpers.TagHelpe }}"; var syntaxTree = CSharpSyntaxTree.ParseText(text); var compilation = TestCompilation.Create(_assembly, syntaxTree); + var typeProvider = new WellKnownTypeProvider(compilation); var tagHelperType = compilation.GetTypeByMetadataName("DynamicTestTagHelper"); - var factory = new DefaultTagHelperDescriptorFactory(compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(typeProvider, includeDocumentation: false, excludeHidden: false); // Act var descriptor = factory.CreateDescriptor(tagHelperType); @@ -1835,8 +1842,9 @@ public class DynamicTestTagHelper : {typeof(AspNetCore.Razor.TagHelpers.TagHelpe }}"; var syntaxTree = CSharpSyntaxTree.ParseText(text); var compilation = TestCompilation.Create(_assembly, syntaxTree); + var typeProvider = new WellKnownTypeProvider(compilation); var tagHelperType = compilation.GetTypeByMetadataName("DynamicTestTagHelper"); - var factory = new DefaultTagHelperDescriptorFactory(compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(typeProvider, includeDocumentation: false, excludeHidden: false); // Act var descriptor = factory.CreateDescriptor(tagHelperType); @@ -1851,7 +1859,7 @@ public void CreateDescriptor_BuildsDescriptorsFromSimpleTypes() { // Arrange var objectAssemblyName = typeof(Enumerable).GetTypeInfo().Assembly.GetName().Name; - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(Enumerable).FullName); var expectedDescriptor = CreateTagHelperDescriptor( @@ -2112,7 +2120,7 @@ public void CreateDescriptor_WithPrefixes_ReturnsExpectedAttributeDescriptors( IEnumerable expectedDiagnostics) { // Arrange - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(tagHelperType.FullName); // Act @@ -2184,7 +2192,7 @@ public void CreateDescriptor_CreatesDescriptorsWithOutputElementHint( TagHelperDescriptor expectedDescriptor) { // Arrange - var factory = new DefaultTagHelperDescriptorFactory(Compilation, includeDocumentation: false, excludeHidden: false); + var factory = new DefaultTagHelperDescriptorFactory(TypeProvider, includeDocumentation: false, excludeHidden: false); var typeSymbol = Compilation.GetTypeByMetadataName(tagHelperType.FullName); // Act @@ -2212,7 +2220,8 @@ public class DocumentedTagHelper : " + typeof(AspNetCore.Razor.TagHelpers.TagHel { }"); var compilation = TestCompilation.Create(_assembly, syntaxTree); - var factory = new DefaultTagHelperDescriptorFactory(compilation, includeDocumentation: true, excludeHidden: false); + var typeProvider = new WellKnownTypeProvider(compilation); + var factory = new DefaultTagHelperDescriptorFactory(typeProvider, includeDocumentation: true, excludeHidden: false); var typeSymbol = compilation.GetTypeByMetadataName("DocumentedTagHelper"); var expectedDocumentation = @" @@ -2261,7 +2270,8 @@ public class DocumentedTagHelper : " + typeof(AspNetCore.Razor.TagHelpers.TagHel public List RemarksAndSummaryProperty { get; set; } }"); var compilation = TestCompilation.Create(_assembly, syntaxTree); - var factory = new DefaultTagHelperDescriptorFactory(compilation, includeDocumentation: true, excludeHidden: false); + var typeProvider = new WellKnownTypeProvider(compilation); + var factory = new DefaultTagHelperDescriptorFactory(typeProvider, includeDocumentation: true, excludeHidden: false); var typeSymbol = compilation.GetTypeByMetadataName("DocumentedTagHelper"); var expectedDocumentations = new[] { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorProviderTest.cs index eec507e0ae8..09823d84285 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/DefaultTagHelperDescriptorProviderTest.cs @@ -26,7 +26,7 @@ public void Execute_DoesNotAddEditorBrowsableNeverDescriptorsAtDesignTime() var descriptorProvider = new DefaultTagHelperDescriptorProvider(); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); context.ExcludeHidden = true; // Act @@ -74,7 +74,7 @@ public override void Process(TagHelperContext context, TagHelperOutput output) { var descriptorProvider = new DefaultTagHelperDescriptorProvider(); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); // Act descriptorProvider.Execute(context); @@ -105,7 +105,7 @@ public override void Process(TagHelperContext context, TagHelperOutput output) { var descriptorProvider = new DefaultTagHelperDescriptorProvider(); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); context.Items.SetTargetSymbol((IAssemblySymbol)compilation.GetAssemblyOrModuleSymbol(compilation.References.First(r => r.Display.Contains("Microsoft.CodeAnalysis.Razor.Test.dll")))); // Act diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/EventHandlerTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/EventHandlerTagHelperDescriptorProviderTest.cs index afae6922869..69bfd6c85e3 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/EventHandlerTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/EventHandlerTagHelperDescriptorProviderTest.cs @@ -34,7 +34,7 @@ public class EventHandlers Assert.Empty(compilation.GetDiagnostics()); var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); var provider = new EventHandlerTagHelperDescriptorProvider(); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/KeyTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/KeyTagHelperDescriptorProviderTest.cs index ab1547c551a..d6f0b6cc8ed 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/KeyTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/KeyTagHelperDescriptorProviderTest.cs @@ -17,7 +17,7 @@ public void Execute_CreatesDescriptor() { // Arrange var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(BaseCompilation); + context.SetTypeProvider(BaseTypeProvider); var provider = new KeyTagHelperDescriptorProvider(); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/RefTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/RefTagHelperDescriptorProviderTest.cs index 8b424ce3d31..c1db2aa773c 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/RefTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/RefTagHelperDescriptorProviderTest.cs @@ -17,7 +17,7 @@ public void Execute_CreatesDescriptor() { // Arrange var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(BaseCompilation); + context.SetTypeProvider(BaseTypeProvider); var provider = new RefTagHelperDescriptorProvider(); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/SplatTagHelperDescriptorProviderTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/SplatTagHelperDescriptorProviderTest.cs index 66e5380a54c..9ec033e3146 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/SplatTagHelperDescriptorProviderTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/SplatTagHelperDescriptorProviderTest.cs @@ -17,7 +17,7 @@ public void Execute_CreatesDescriptor() { // Arrange var context = TagHelperDescriptorProviderContext.Create(); - context.SetCompilation(BaseCompilation); + context.SetTypeProvider(BaseTypeProvider); var provider = new SplatTagHelperDescriptorProvider(); diff --git a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.TagHelpers.cs b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.TagHelpers.cs deleted file mode 100644 index b0d30d393c9..00000000000 --- a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.TagHelpers.cs +++ /dev/null @@ -1,37 +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. - -using System.Collections.Generic; -using Microsoft.AspNetCore.Razor.Language; -using Microsoft.CodeAnalysis; - -namespace Microsoft.NET.Sdk.Razor.SourceGenerators -{ - public partial class RazorSourceGenerator - { - private IReadOnlyList GetTagHelpers(IEnumerable references, StaticCompilationTagHelperFeature tagHelperFeature, Compilation compilation) - { - List descriptors = new(); - tagHelperFeature.Compilation = compilation; - foreach (var reference in references) - { - if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) - { - tagHelperFeature.TargetSymbol = assembly; - descriptors.AddRange(tagHelperFeature.GetDescriptors()); - } - } - return descriptors; - } - - private static IReadOnlyList GetTagHelpersFromCompilation(Compilation compilation, StaticCompilationTagHelperFeature tagHelperFeature, SyntaxTree syntaxTrees) - { - var compilationWithDeclarations = compilation.AddSyntaxTrees(syntaxTrees); - - tagHelperFeature.Compilation = compilationWithDeclarations; - tagHelperFeature.TargetSymbol = compilationWithDeclarations.Assembly; - - return tagHelperFeature.GetDescriptors(); - } - } -} diff --git a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.cs b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.cs index 4fd80d7e3b6..e1b10a5e854 100644 --- a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.cs +++ b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/RazorSourceGenerator.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Razor; namespace Microsoft.NET.Sdk.Razor.SourceGenerators { @@ -22,6 +23,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var analyzerConfigOptions = context.AnalyzerConfigOptionsProvider; var parseOptions = context.ParseOptionsProvider; var compilation = context.CompilationProvider; + var typeProvider = compilation.Select(static (compilation, cancellationToken) => new WellKnownTypeProvider(compilation)); // determine if we should suppress this run and filter out all the additional files if so var isGeneratorSuppressed = context.AnalyzerConfigOptionsProvider.Select(GetSuppressionStatus); @@ -105,7 +107,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var compilationWithDeclarations = compilation.AddSyntaxTrees(generatedDeclarationSyntaxTrees); - tagHelperFeature.Compilation = compilationWithDeclarations; + tagHelperFeature.TypeProvider = new WellKnownTypeProvider(compilationWithDeclarations); tagHelperFeature.TargetSymbol = compilationWithDeclarations.Assembly; var result = (IList)tagHelperFeature.GetDescriptors(); @@ -130,13 +132,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return true; }, getHashCode: static a => a.Count); - var tagHelpersFromReferences = compilation + var tagHelpersFromReferences = typeProvider .Combine(razorSourceGeneratorOptions) .Combine(hasRazorFiles) .WithLambdaComparer(static (a, b) => { - var ((compilationA, razorSourceGeneratorOptionsA), hasRazorFilesA) = a; - var ((compilationB, razorSourceGeneratorOptionsB), hasRazorFilesB) = b; + var ((typeProviderA, razorSourceGeneratorOptionsA), hasRazorFilesA) = a; + var ((typeProviderB, razorSourceGeneratorOptionsB), hasRazorFilesB) = b; + var compilationA = typeProviderA.Compilation; + var compilationB = typeProviderB.Compilation; if (!compilationA.References.SequenceEqual(compilationB.References)) { @@ -153,14 +157,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context) static item => { // we'll use the number of references as a hashcode. - var ((compilationA, razorSourceGeneratorOptionsA), hasRazorFilesA) = item; - return compilationA.References.GetHashCode(); + var ((typeProviderA, razorSourceGeneratorOptionsA), hasRazorFilesA) = item; + return typeProviderA.Compilation.References.GetHashCode(); }) .Select(static (pair, _) => { RazorSourceGeneratorEventSource.Log.DiscoverTagHelpersFromReferencesStart(); - var ((compilation, razorSourceGeneratorOptions), hasRazorFiles) = pair; + var ((typeProvider, razorSourceGeneratorOptions), hasRazorFiles) = pair; + var compilation = typeProvider.Compilation; if (!hasRazorFiles) { // If there's no razor code in this app, don't do anything. @@ -172,7 +177,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var discoveryProjectEngine = GetDiscoveryProjectEngine(compilation.References.ToImmutableArray(), tagHelperFeature); List descriptors = new(); - tagHelperFeature.Compilation = compilation; + tagHelperFeature.TypeProvider = typeProvider; foreach (var reference in compilation.References) { if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) diff --git a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticCompilationTagHelperFeature.cs b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticCompilationTagHelperFeature.cs index 15d54b36587..4e5a12b4b82 100644 --- a/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticCompilationTagHelperFeature.cs +++ b/src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/StaticCompilationTagHelperFeature.cs @@ -17,14 +17,14 @@ internal sealed class StaticCompilationTagHelperFeature : RazorEngineFeatureBase public List GetDescriptors() { - if (Compilation is null) + if (TypeProvider is null) { return EmptyList; } var results = new List(); var context = TagHelperDescriptorProviderContext.Create(results); - context.SetCompilation(Compilation); + context.SetTypeProvider(TypeProvider); if (TargetSymbol is not null) { context.Items.SetTargetSymbol(TargetSymbol); @@ -40,7 +40,7 @@ public List GetDescriptors() IReadOnlyList ITagHelperFeature.GetDescriptors() => GetDescriptors(); - public Compilation? Compilation { get; set; } + public WellKnownTypeProvider? TypeProvider { get; set; } public ISymbol? TargetSymbol { get; set; } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperResolver.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperResolver.cs index 5e85460f646..09c9104183a 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperResolver.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TagHelperResolver.cs @@ -52,7 +52,7 @@ protected async Task GetTagHelpersAsync(Project works var compilation = await workspaceProject.GetCompilationAsync(cancellationToken).ConfigureAwait(false); if (CompilationTagHelperFeature.IsValidCompilation(compilation)) { - context.SetCompilation(compilation); + context.SetTypeProvider(new WellKnownTypeProvider(compilation)); } var timingDictionary = new Dictionary(); diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs index 563010db4fa..002ec504644 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/ImmutableArrayExtensions.cs @@ -1,6 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. +using System.Collections.Generic; +using System.Linq; + namespace System.Collections.Immutable; /// @@ -8,6 +11,60 @@ namespace System.Collections.Immutable; /// internal static class ImmutableArrayExtensions { + public static bool Any(this ImmutableArray array, Func predicate, TArg arg) + { + foreach (var item in array) + { + if (predicate(item, arg)) + { + return true; + } + } + + return false; + } + + public static bool Contains(this ImmutableArray array, T item, TComparer comparer) + where TComparer : IEqualityComparer + { + foreach (var actual in array) + { + if (comparer.Equals(actual, item)) + { + return true; + } + } + + return false; + } + + public static T First(this ImmutableArray array, Func predicate, TArg arg) + { + foreach (var item in array) + { + if (predicate(item, arg)) + { + return item; + } + } + + // Throw the same exception that System.Linq would + return ImmutableArray.Empty.First(static t => false); + } + + public static T? FirstOrDefault(this ImmutableArray array, Func predicate, TArg arg) + { + foreach (var item in array) + { + if (predicate(item, arg)) + { + return item; + } + } + + return default; + } + /// /// Returns an empty array if the input array is null (default) ///