From 853b458893b01458ef3df1f2045c7c4dec35f5fc Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Tue, 27 Jun 2017 16:10:19 -0700 Subject: [PATCH] This is a different take on Taylor's builders. This makes it possible to use another 'kind' of tag helpers, which isn't possible today. This also further decouples the tag helper api surface from the default implementation. VCTH now have their own 'kind'. Also improved generation of display names and error messages where it was coupled to the type name. --- .../TagHelperDescriptorExtensions.cs | 37 ++ .../ViewComponentTagHelperConventions.cs | 10 + ...ComponentTagHelperDescriptorConventions.cs | 27 - ...ViewComponentTagHelperDescriptorFactory.cs | 7 +- .../ViewComponentTagHelperMetadata.cs | 14 + .../ViewComponentTagHelperPass.cs | 14 +- .../ViewComponentTypes.cs | 2 - .../BoundAttributeDescriptor.cs | 2 + .../BoundAttributeDescriptorBuilder.cs | 294 +--------- .../BoundAttributeDescriptorExtensions.cs | 6 +- .../DefaultBoundAttributeDescriptor.cs | 40 ++ .../DefaultBoundAttributeDescriptorBuilder.cs | 262 +++++++++ .../DefaultRazorTagHelperBinderPhase.cs | 12 +- .../DefaultRequiredAttributeDescriptor.cs | 24 + ...faultRequiredAttributeDescriptorBuilder.cs | 105 ++++ .../DefaultTagHelperDescriptor.cs | 36 ++ .../DefaultTagHelperDescriptorBuilder.cs | 258 +++++++++ .../DefaultTagMatchingRuleDescriptor.cs | 22 + ...efaultTagMatchingRuleDescriptorBuilder.cs} | 94 ++-- .../Legacy/TagHelperParseTreeRewriter.cs | 12 +- .../Properties/Resources.Designer.cs | 132 ++--- .../RazorDiagnosticFactory.cs | 98 ++-- .../RequiredAttributeDescriptor.cs | 1 + .../RequiredAttributeDescriptorBuilder.cs | 136 +---- .../Resources.resx | 40 +- .../TagHelperBinder.cs | 4 +- .../TagHelperBinding.cs | 6 +- .../TagHelperConventions.cs | 10 + .../TagHelperDescriptor.cs | 4 +- .../TagHelperDescriptorBuilder.cs | 259 +-------- .../TagHelperDescriptorComparer.cs | 6 +- .../TagHelperDescriptorExtensions.cs | 6 +- .../TagHelperMatchingConventions.cs | 8 +- .../TagHelperMetadata.cs | 15 + ...ngRule.cs => TagMatchingRuleDescriptor.cs} | 10 +- .../TagMatchingRuleDescriptorBuilder.cs | 20 + ...s => TagMatchingRuleDescriptorComparer.cs} | 18 +- .../DefaultTagHelperDescriptorFactory.cs | 1 + .../Properties/Resources.Designer.cs | 76 +-- .../RazorDiagnosticFactory.cs | 50 +- .../RequiredAttributeParser.cs | 4 +- .../Resources.resx | 22 +- .../TagHelperDescriptorJsonConverter.cs | 20 +- .../InstrumentationPassIntegrationTest.cs | 1 + .../TagHelperDescriptorExtensionsTest.cs | 82 +++ ...onentTagHelperDescriptorConventionsTest.cs | 61 --- ...ComponentTagHelperDescriptorFactoryTest.cs | 15 +- ...omponentTagHelperDescriptorProviderTest.cs | 4 +- .../ViewComponentTagHelperPassTest.cs | 61 +-- .../BoundAttributeDescriptorExtensionsTest.cs | 56 +- .../DesignTimeTagHelperWriterTest.cs | 1 + .../RuntimeTagHelperWriterTest.cs | 1 + ...ultBoundAttributeDescriptorBuilderTest.cs} | 16 +- ...mediateNodeLoweringPhaseIntegrationTest.cs | 1 + .../DefaultRazorTagHelperBinderPhaseTest.cs | 29 +- ...RequiredAttributeDescriptorBuilderTest.cs} | 14 +- ...reallocatedAttributeTargetExtensionTest.cs | 26 +- .../TagHelpersIntegrationTest.cs | 1 + .../TestTagHelperDescriptors.cs | 42 +- .../Legacy/TagHelperParseTreeRewriterTest.cs | 2 +- .../TagHelperBinderTest.cs | 2 +- .../TagHelperDescriptorExtensionsTest.cs | 18 +- ...cs => TagHelperMatchingConventionsTest.cs} | 75 ++- ...BoundAttributes_DesignTime.diagnostics.txt | 10 +- ...bolBoundAttributes_Runtime.diagnostics.txt | 10 +- .../DefaultTagHelperDescriptorFactoryTest.cs | 502 ++++++++++-------- .../DefaultTagHelperFactsServiceTest.cs | 2 +- .../TagHelperDescriptorSerializationTest.cs | 9 +- 68 files changed, 1780 insertions(+), 1485 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Mvc.Razor.Extensions/TagHelperDescriptorExtensions.cs create mode 100644 src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperConventions.cs delete mode 100644 src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorConventions.cs create mode 100644 src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperMetadata.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Language/DefaultBoundAttributeDescriptor.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Language/DefaultBoundAttributeDescriptorBuilder.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Language/DefaultRequiredAttributeDescriptor.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Language/DefaultRequiredAttributeDescriptorBuilder.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Language/DefaultTagHelperDescriptor.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Language/DefaultTagHelperDescriptorBuilder.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Language/DefaultTagMatchingRuleDescriptor.cs rename src/Microsoft.AspNetCore.Razor.Language/{TagMatchingRuleBuilder.cs => DefaultTagMatchingRuleDescriptorBuilder.cs} (56%) create mode 100644 src/Microsoft.AspNetCore.Razor.Language/TagHelperConventions.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Language/TagHelperMetadata.cs rename src/Microsoft.AspNetCore.Razor.Language/{TagMatchingRule.cs => TagMatchingRuleDescriptor.cs} (81%) create mode 100644 src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptorBuilder.cs rename src/Microsoft.AspNetCore.Razor.Language/{TagMatchingRuleComparer.cs => TagMatchingRuleDescriptorComparer.cs} (79%) create mode 100644 test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TagHelperDescriptorExtensionsTest.cs delete mode 100644 test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorConventionsTest.cs rename test/Microsoft.AspNetCore.Razor.Language.Test/{BoundAttributeDescriptorBuilderTest.cs => DefaultBoundAttributeDescriptorBuilderTest.cs} (55%) rename test/Microsoft.AspNetCore.Razor.Language.Test/{TagHelperRequiredAttributeDescriptorBuilderTest.cs => DefaultRequiredAttributeDescriptorBuilderTest.cs} (74%) rename test/Microsoft.AspNetCore.Razor.Language.Test/{TagHelperRequiredAttributeDescriptorTest.cs => TagHelperMatchingConventionsTest.cs} (69%) diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/TagHelperDescriptorExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/TagHelperDescriptorExtensions.cs new file mode 100644 index 0000000000..dd32c0b013 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/TagHelperDescriptorExtensions.cs @@ -0,0 +1,37 @@ +// 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; +using Microsoft.AspNetCore.Razor.Language; + +namespace Microsoft.AspNetCore.Mvc.Razor.Extensions +{ + public static class TagHelperDescriptorExtensions + { + /// + /// Indicates whether a represents a view component. + /// + /// The to check. + /// Whether a represents a view component. + public static bool IsViewComponentKind(this TagHelperDescriptor tagHelper) + { + if (tagHelper == null) + { + throw new ArgumentNullException(nameof(tagHelper)); + } + + return string.Equals(ViewComponentTagHelperConventions.Kind, tagHelper.Kind, StringComparison.Ordinal); + } + + public static string GetViewComponentName(this TagHelperDescriptor tagHelper) + { + if (tagHelper == null) + { + throw new ArgumentNullException(nameof(tagHelper)); + } + + tagHelper.Metadata.TryGetValue(ViewComponentTagHelperMetadata.Name, out var result); + return result; + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperConventions.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperConventions.cs new file mode 100644 index 0000000000..fa7563d37b --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperConventions.cs @@ -0,0 +1,10 @@ +// 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. + +namespace Microsoft.AspNetCore.Mvc.Razor.Extensions +{ + public static class ViewComponentTagHelperConventions + { + public static readonly string Kind = "MVC.ViewComponent"; + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorConventions.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorConventions.cs deleted file mode 100644 index 63bb660c65..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorConventions.cs +++ /dev/null @@ -1,27 +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 Microsoft.AspNetCore.Razor.Language; - -namespace Microsoft.AspNetCore.Mvc.Razor.Extensions -{ - /// - /// A library of methods used to generate s for view components. - /// - public static class ViewComponentTagHelperDescriptorConventions - { - /// - /// The key in a containing - /// the short name of a view component. - /// - public static readonly string ViewComponentNameKey = "ViewComponentName"; - - /// - /// Indicates whether a represents a view component. - /// - /// The to check. - /// Whether a represents a view component. - public static bool IsViewComponentDescriptor(TagHelperDescriptor descriptor) => - descriptor != null && descriptor.Metadata.ContainsKey(ViewComponentNameKey); - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorFactory.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorFactory.cs index 7bc925d292..b3e64c465a 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorFactory.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperDescriptorFactory.cs @@ -56,7 +56,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions var tagName = $"vc:{HtmlConventions.ToHtmlCase(shortName)}"; var typeName = $"__Generated__{shortName}ViewComponentTagHelper"; var displayName = shortName + "ViewComponentTagHelper"; - var descriptorBuilder = TagHelperDescriptorBuilder.Create(typeName, assemblyName) + var descriptorBuilder = TagHelperDescriptorBuilder.Create(ViewComponentTagHelperConventions.Kind, typeName, assemblyName) + .TypeName(typeName) .DisplayName(displayName); if (TryFindInvokeMethod(type, out var method, out var diagnostic)) @@ -75,7 +76,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions descriptorBuilder.AddDiagnostic(diagnostic); } - descriptorBuilder.AddMetadata(ViewComponentTypes.ViewComponentNameKey, shortName); + descriptorBuilder.AddMetadata(ViewComponentTagHelperMetadata.Name, shortName); var descriptor = descriptorBuilder.Build(); return descriptor; @@ -152,7 +153,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions return true; } - private void AddRequiredAttributes(ImmutableArray methodParameters, TagMatchingRuleBuilder builder) + private void AddRequiredAttributes(ImmutableArray methodParameters, TagMatchingRuleDescriptorBuilder builder) { foreach (var parameter in methodParameters) { diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperMetadata.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperMetadata.cs new file mode 100644 index 0000000000..c4dd39918c --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperMetadata.cs @@ -0,0 +1,14 @@ +// 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. + +namespace Microsoft.AspNetCore.Mvc.Razor.Extensions +{ + public class ViewComponentTagHelperMetadata + { + /// + /// The key in a containing + /// the short name of a view component. + /// + public static readonly string Name = "MVC.ViewComponent.Name"; + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperPass.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperPass.cs index 96922b9a78..956413b66e 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperPass.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperPass.cs @@ -82,14 +82,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions ClassDeclarationIntermediateNode @class, TagHelperDescriptor tagHelper) { - var vcName = tagHelper.Metadata[ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey]; + var vcName = tagHelper.GetViewComponentName(); return $"{@namespace.Content}.{@class.Name}.__Generated__{vcName}ViewComponentTagHelper"; } private static string GetVCTHClassName( TagHelperDescriptor tagHelper) { - var vcName = tagHelper.Metadata[ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey]; + var vcName = tagHelper.GetViewComponentName(); return $"__Generated__{vcName}ViewComponentTagHelper"; } @@ -201,13 +201,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions private string[] GetMethodParameters(TagHelperDescriptor descriptor) { - var propertyNames = descriptor.BoundAttributes.Select( - attribute => attribute.GetPropertyName()); + var propertyNames = descriptor.BoundAttributes.Select(attribute => attribute.GetPropertyName()); var joinedPropertyNames = string.Join(", ", propertyNames); var parametersString = $"new {{ { joinedPropertyNames } }}"; - var viewComponentName = descriptor.Metadata[ - ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey]; + var viewComponentName = descriptor.GetViewComponentName(); var methodParameters = new[] { $"\"{viewComponentName}\"", parametersString }; return methodParameters; } @@ -239,10 +237,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions public override void VisitCreateTagHelper(CreateTagHelperIntermediateNode node) { var tagHelper = node.Descriptor; - if (ViewComponentTagHelperDescriptorConventions.IsViewComponentDescriptor(tagHelper)) + if (tagHelper.IsViewComponentKind()) { // Capture all the VCTagHelpers (unique by type name) so we can generate a class for each one. - var vcName = tagHelper.Metadata[ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey]; + var vcName = tagHelper.GetViewComponentName(); TagHelpers[vcName] = tagHelper; CreateTagHelpers.Add(new IntermediateNodeReference(Parent, node)); diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTypes.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTypes.cs index 2fab7878fa..5558ba5e06 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTypes.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTypes.cs @@ -23,8 +23,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions public const string IDictionary = "System.Collections.Generic.IDictionary`2"; - public const string ViewComponentNameKey = "ViewComponentName"; - public const string AsyncMethodName = "InvokeAsync"; public const string SyncMethodName = "Invoke"; diff --git a/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptor.cs index 03a8342a6d..2014a1336f 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptor.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptor.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace Microsoft.AspNetCore.Razor.Language @@ -10,6 +11,7 @@ namespace Microsoft.AspNetCore.Razor.Language /// /// A metadata class describing a tag helper attribute. /// + [DebuggerDisplay("{DisplayName,nq}")] public abstract class BoundAttributeDescriptor : IEquatable { protected BoundAttributeDescriptor(string kind) diff --git a/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorBuilder.cs index 29d1daf87d..188d0a8ac5 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorBuilder.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorBuilder.cs @@ -1,301 +1,27 @@ // 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; -using System.Collections.Generic; -using System.Linq; - namespace Microsoft.AspNetCore.Razor.Language { - public sealed class BoundAttributeDescriptorBuilder + public abstract class BoundAttributeDescriptorBuilder { - internal const string DescriptorKind = "ITagHelper"; - internal const string PropertyNameKey = "ITagHelper.PropertyName"; + public abstract BoundAttributeDescriptorBuilder Name(string name); - private static readonly IReadOnlyDictionary PrimitiveDisplayTypeNameLookups = new Dictionary(StringComparer.Ordinal) - { - [typeof(byte).FullName] = "byte", - [typeof(sbyte).FullName] = "sbyte", - [typeof(int).FullName] = "int", - [typeof(uint).FullName] = "uint", - [typeof(short).FullName] = "short", - [typeof(ushort).FullName] = "ushort", - [typeof(long).FullName] = "long", - [typeof(ulong).FullName] = "ulong", - [typeof(float).FullName] = "float", - [typeof(double).FullName] = "double", - [typeof(char).FullName] = "char", - [typeof(bool).FullName] = "bool", - [typeof(object).FullName] = "object", - [typeof(string).FullName] = "string", - [typeof(decimal).FullName] = "decimal", - }; + public abstract BoundAttributeDescriptorBuilder PropertyName(string propertyName); - private string _displayName; - private bool _isEnum; - private bool _hasIndexer; - private string _indexerValueTypeName; - private string _name; - private string _propertyName; - private string _typeName; - private string _documentation; - private string _indexerNamePrefix; - private readonly string _containingTypeName; - private readonly Dictionary _metadata; - private HashSet _diagnostics; + public abstract BoundAttributeDescriptorBuilder TypeName(string typeName); - private BoundAttributeDescriptorBuilder(string containingTypeName) - { - _containingTypeName = containingTypeName; - _metadata = new Dictionary(); - } + public abstract BoundAttributeDescriptorBuilder AsEnum(); - public static BoundAttributeDescriptorBuilder Create(string containingTypeName) - { - return new BoundAttributeDescriptorBuilder(containingTypeName); - } + public abstract BoundAttributeDescriptorBuilder AsDictionary(string attributeNamePrefix, string valueTypeName); - public BoundAttributeDescriptorBuilder Name(string name) - { - _name = name; + public abstract BoundAttributeDescriptorBuilder Documentation(string documentation); - return this; - } + public abstract BoundAttributeDescriptorBuilder AddMetadata(string key, string value); - public BoundAttributeDescriptorBuilder PropertyName(string propertyName) - { - _propertyName = propertyName; + public abstract BoundAttributeDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic); - return this; - } + public abstract BoundAttributeDescriptorBuilder DisplayName(string displayName); - public BoundAttributeDescriptorBuilder TypeName(string typeName) - { - _typeName = typeName; - - return this; - } - - public BoundAttributeDescriptorBuilder AsEnum() - { - _isEnum = true; - - return this; - } - - public BoundAttributeDescriptorBuilder AsDictionary(string attributeNamePrefix, string valueTypeName) - { - _indexerNamePrefix = attributeNamePrefix; - _indexerValueTypeName = valueTypeName; - _hasIndexer = true; - - return this; - } - - public BoundAttributeDescriptorBuilder Documentation(string documentation) - { - _documentation = documentation; - - return this; - } - - public BoundAttributeDescriptorBuilder AddMetadata(string key, string value) - { - _metadata[key] = value; - - return this; - } - - public BoundAttributeDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic) - { - EnsureDiagnostics(); - _diagnostics.Add(diagnostic); - - return this; - } - - public BoundAttributeDescriptorBuilder DisplayName(string displayName) - { - if (displayName == null) - { - throw new ArgumentNullException(nameof(displayName)); - } - - _displayName = displayName; - - return this; - } - - public BoundAttributeDescriptor Build() - { - var validationDiagnostics = Validate(); - var diagnostics = new HashSet(validationDiagnostics); - if (_diagnostics != null) - { - diagnostics.UnionWith(_diagnostics); - } - - var displayName = _displayName; - if (displayName == null) - { - if (!PrimitiveDisplayTypeNameLookups.TryGetValue(_typeName, out var simpleName)) - { - simpleName = _typeName; - } - - displayName = $"{simpleName} {_containingTypeName}.{_propertyName}"; - } - - var descriptor = new ITagHelperBoundAttributeDescriptor( - _isEnum, - _name, - _propertyName, - _typeName, - _indexerNamePrefix, - _indexerValueTypeName, - _hasIndexer, - _documentation, - displayName, - _metadata, - diagnostics); - - return descriptor; - } - - public void Reset() - { - _name = null; - _propertyName = null; - _typeName = null; - _documentation = null; - _isEnum = false; - _indexerNamePrefix = null; - _indexerValueTypeName = null; - _metadata.Clear(); - _diagnostics?.Clear(); - } - - private IEnumerable Validate() - { - // data-* attributes are explicitly not implemented by user agents and are not intended for use on - // the server; therefore it's invalid for TagHelpers to bind to them. - const string DataDashPrefix = "data-"; - - if (string.IsNullOrWhiteSpace(_name)) - { - if (_indexerNamePrefix == null) - { - var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNullOrWhitespace( - _containingTypeName, - _propertyName); - - yield return diagnostic; - } - } - else - { - if (_name.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase)) - { - var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNameStartsWith( - _containingTypeName, - _propertyName, - _name); - - yield return diagnostic; - } - - foreach (var character in _name) - { - if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character)) - { - var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeName( - _containingTypeName, - _propertyName, - _name, - character); - - yield return diagnostic; - } - } - } - - if (_indexerNamePrefix != null) - { - if (_indexerNamePrefix.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase)) - { - var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributePrefixStartsWith( - _containingTypeName, - _propertyName, - _indexerNamePrefix); - - yield return diagnostic; - } - else if (_indexerNamePrefix.Length > 0 && string.IsNullOrWhiteSpace(_indexerNamePrefix)) - { - var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNullOrWhitespace( - _containingTypeName, - _propertyName); - - yield return diagnostic; - } - else - { - foreach (var character in _indexerNamePrefix) - { - if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character)) - { - var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributePrefix( - _containingTypeName, - _propertyName, - _indexerNamePrefix, - character); - - yield return diagnostic; - } - } - } - } - } - - private void EnsureDiagnostics() - { - if (_diagnostics == null) - { - _diagnostics = new HashSet(); - } - } - - private class ITagHelperBoundAttributeDescriptor : BoundAttributeDescriptor - { - public ITagHelperBoundAttributeDescriptor( - bool isEnum, - string name, - string propertyName, - string typeName, - string dictionaryAttributeNamePrefix, - string dictionaryValueTypeName, - bool hasIndexer, - string documentation, - string displayName, - Dictionary metadata, - IEnumerable diagnostics) : base(DescriptorKind) - { - IsEnum = isEnum; - IsIndexerStringProperty = dictionaryValueTypeName == typeof(string).FullName || dictionaryValueTypeName == "string"; - IsStringProperty = typeName == typeof(string).FullName || typeName == "string"; - Name = name; - TypeName = typeName; - IndexerNamePrefix = dictionaryAttributeNamePrefix; - IndexerTypeName = dictionaryValueTypeName; - HasIndexer = hasIndexer; - Documentation = documentation; - DisplayName = displayName; - Diagnostics = new List(diagnostics); - Metadata = new Dictionary(metadata) - { - [PropertyNameKey] = propertyName - }; - } - } } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorExtensions.cs b/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorExtensions.cs index 137b605abd..e1bab599f2 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorExtensions.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorExtensions.cs @@ -9,12 +9,14 @@ namespace Microsoft.AspNetCore.Razor.Language { public static string GetPropertyName(this BoundAttributeDescriptor descriptor) { - descriptor.Metadata.TryGetValue(BoundAttributeDescriptorBuilder.PropertyNameKey, out var propertyName); + descriptor.Metadata.TryGetValue(TagHelperMetadata.Common.PropertyName, out var propertyName); return propertyName; } public static bool IsDefaultKind(this BoundAttributeDescriptor descriptor) - => string.Equals(descriptor.Kind, BoundAttributeDescriptorBuilder.DescriptorKind, StringComparison.Ordinal); + { + return string.Equals(descriptor.Kind, TagHelperConventions.DefaultKind, StringComparison.Ordinal); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Language/DefaultBoundAttributeDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Language/DefaultBoundAttributeDescriptor.cs new file mode 100644 index 0000000000..6389a49310 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Language/DefaultBoundAttributeDescriptor.cs @@ -0,0 +1,40 @@ +// 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; + +namespace Microsoft.AspNetCore.Razor.Language +{ + internal class DefaultBoundAttributeDescriptor : BoundAttributeDescriptor + { + public DefaultBoundAttributeDescriptor( + string kind, + string name, + string typeName, + bool isEnum, + bool hasIndexer, + string indexerNamePrefix, + string indexerTypeName, + string documentation, + string displayName, + Dictionary metadata, + RazorDiagnostic[] diagnostics) + : base(kind) + { + Name = name; + TypeName = typeName; + IsEnum = isEnum; + HasIndexer = hasIndexer; + IndexerNamePrefix = indexerNamePrefix; + IndexerTypeName = indexerTypeName; + Documentation = documentation; + DisplayName = displayName; + + Metadata = metadata; + Diagnostics = diagnostics; + + IsIndexerStringProperty = indexerTypeName == typeof(string).FullName || indexerTypeName == "string"; + IsStringProperty = typeName == typeof(string).FullName || typeName == "string"; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Language/DefaultBoundAttributeDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Razor.Language/DefaultBoundAttributeDescriptorBuilder.cs new file mode 100644 index 0000000000..da021c52d6 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Language/DefaultBoundAttributeDescriptorBuilder.cs @@ -0,0 +1,262 @@ +// 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; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.AspNetCore.Razor.Language +{ + internal class DefaultBoundAttributeDescriptorBuilder : BoundAttributeDescriptorBuilder + { + private static readonly IReadOnlyDictionary PrimitiveDisplayTypeNameLookups = new Dictionary(StringComparer.Ordinal) + { + [typeof(byte).FullName] = "byte", + [typeof(sbyte).FullName] = "sbyte", + [typeof(int).FullName] = "int", + [typeof(uint).FullName] = "uint", + [typeof(short).FullName] = "short", + [typeof(ushort).FullName] = "ushort", + [typeof(long).FullName] = "long", + [typeof(ulong).FullName] = "ulong", + [typeof(float).FullName] = "float", + [typeof(double).FullName] = "double", + [typeof(char).FullName] = "char", + [typeof(bool).FullName] = "bool", + [typeof(object).FullName] = "object", + [typeof(string).FullName] = "string", + [typeof(decimal).FullName] = "decimal", + }; + + private readonly DefaultTagHelperDescriptorBuilder _parent; + private readonly string _kind; + + private string _displayName; + private bool _isEnum; + private bool _hasIndexer; + private string _indexerValueTypeName; + private string _name; + private string _typeName; + private string _documentation; + private string _indexerNamePrefix; + private readonly Dictionary _metadata; + private HashSet _diagnostics; + + public DefaultBoundAttributeDescriptorBuilder(DefaultTagHelperDescriptorBuilder parent, string kind) + { + _parent = parent; + _kind = kind; + + _metadata = new Dictionary(); + } + + public override BoundAttributeDescriptorBuilder Name(string name) + { + _name = name; + + return this; + } + + public override BoundAttributeDescriptorBuilder PropertyName(string propertyName) + { + _metadata[TagHelperMetadata.Common.PropertyName] = propertyName; + + return this; + } + + public override BoundAttributeDescriptorBuilder TypeName(string typeName) + { + _typeName = typeName; + + return this; + } + + public override BoundAttributeDescriptorBuilder AsEnum() + { + _isEnum = true; + + return this; + } + + public override BoundAttributeDescriptorBuilder AsDictionary(string attributeNamePrefix, string valueTypeName) + { + _indexerNamePrefix = attributeNamePrefix; + _indexerValueTypeName = valueTypeName; + _hasIndexer = true; + + return this; + } + + public override BoundAttributeDescriptorBuilder Documentation(string documentation) + { + _documentation = documentation; + + return this; + } + + public override BoundAttributeDescriptorBuilder AddMetadata(string key, string value) + { + _metadata[key] = value; + + return this; + } + + public override BoundAttributeDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic) + { + EnsureDiagnostics(); + _diagnostics.Add(diagnostic); + + return this; + } + + public override BoundAttributeDescriptorBuilder DisplayName(string displayName) + { + if (displayName == null) + { + throw new ArgumentNullException(nameof(displayName)); + } + + _displayName = displayName; + + return this; + } + + public BoundAttributeDescriptor Build() + { + var validationDiagnostics = Validate(); + var diagnostics = new HashSet(validationDiagnostics); + if (_diagnostics != null) + { + diagnostics.UnionWith(_diagnostics); + } + + var descriptor = new DefaultBoundAttributeDescriptor( + _kind, + _name, + _typeName, + _isEnum, + _hasIndexer, + _indexerNamePrefix, + _indexerValueTypeName, + _documentation, + GetDisplayName(), + new Dictionary(_metadata), + diagnostics.ToArray()); + + return descriptor; + } + + private string GetDisplayName() + { + if (_displayName != null) + { + return _displayName; + } + + if (_typeName != null && + _metadata.ContainsKey(TagHelperMetadata.Common.PropertyName) && + _parent.Metadata.ContainsKey(TagHelperMetadata.Common.TypeName)) + { + // This looks like a normal c# property, so lets compute a display name based on that. + if (!PrimitiveDisplayTypeNameLookups.TryGetValue(_typeName, out var simpleTypeName)) + { + simpleTypeName = _typeName; + } + + return $"{simpleTypeName} {_parent.Metadata[TagHelperMetadata.Common.TypeName]}.{_metadata[TagHelperMetadata.Common.PropertyName]}"; + } + + return _name; + } + + private IEnumerable Validate() + { + // data-* attributes are explicitly not implemented by user agents and are not intended for use on + // the server; therefore it's invalid for TagHelpers to bind to them. + const string DataDashPrefix = "data-"; + + if (string.IsNullOrWhiteSpace(_name)) + { + if (_indexerNamePrefix == null) + { + var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNullOrWhitespace( + _parent.GetDisplayName(), + GetDisplayName()); + + yield return diagnostic; + } + } + else + { + if (_name.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase)) + { + var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNameStartsWith( + _parent.GetDisplayName(), + GetDisplayName(), + _name); + + yield return diagnostic; + } + + foreach (var character in _name) + { + if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character)) + { + var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeName( + _parent.GetDisplayName(), + GetDisplayName(), + _name, + character); + + yield return diagnostic; + } + } + } + + if (_indexerNamePrefix != null) + { + if (_indexerNamePrefix.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase)) + { + var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributePrefixStartsWith( + _parent.GetDisplayName(), + GetDisplayName(), + _indexerNamePrefix); + + yield return diagnostic; + } + else if (_indexerNamePrefix.Length > 0 && string.IsNullOrWhiteSpace(_indexerNamePrefix)) + { + var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNullOrWhitespace( + _parent.GetDisplayName(), + GetDisplayName()); + + yield return diagnostic; + } + else + { + foreach (var character in _indexerNamePrefix) + { + if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character)) + { + var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributePrefix( + _parent.GetDisplayName(), + GetDisplayName(), + _indexerNamePrefix, + character); + + yield return diagnostic; + } + } + } + } + } + + private void EnsureDiagnostics() + { + if (_diagnostics == null) + { + _diagnostics = new HashSet(); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorTagHelperBinderPhase.cs b/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorTagHelperBinderPhase.cs index f3892516ee..41c4385d41 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorTagHelperBinderPhase.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/DefaultRazorTagHelperBinderPhase.cs @@ -262,14 +262,6 @@ namespace Microsoft.AspNetCore.Razor.Language return false; } - if (!descriptor.IsDefaultKind()) - { - // We only understand default TagHelperDescriptors. - return false; - } - - var descriptorTypeName = descriptor.GetTypeName(); - if (lookupInfo.TypePattern.EndsWith("*", StringComparison.Ordinal)) { if (lookupInfo.TypePattern.Length == 1) @@ -280,10 +272,10 @@ namespace Microsoft.AspNetCore.Razor.Language var lookupTypeName = lookupInfo.TypePattern.Substring(0, lookupInfo.TypePattern.Length - 1); - return descriptorTypeName.StartsWith(lookupTypeName, StringComparison.Ordinal); + return descriptor.Name.StartsWith(lookupTypeName, StringComparison.Ordinal); } - return string.Equals(descriptorTypeName, lookupInfo.TypePattern, StringComparison.Ordinal); + return string.Equals(descriptor.Name, lookupInfo.TypePattern, StringComparison.Ordinal); } private static int GetErrorLength(string directiveText) diff --git a/src/Microsoft.AspNetCore.Razor.Language/DefaultRequiredAttributeDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Language/DefaultRequiredAttributeDescriptor.cs new file mode 100644 index 0000000000..8802abcf7a --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Language/DefaultRequiredAttributeDescriptor.cs @@ -0,0 +1,24 @@ +// 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. + +namespace Microsoft.AspNetCore.Razor.Language +{ + internal class DefaultRequiredAttributeDescriptor : RequiredAttributeDescriptor + { + public DefaultRequiredAttributeDescriptor( + string name, + NameComparisonMode nameComparison, + string value, + ValueComparisonMode valueComparison, + string displayName, + RazorDiagnostic[] diagnostics) + { + Name = name; + NameComparison = nameComparison; + Value = value; + ValueComparison = valueComparison; + DisplayName = displayName; + Diagnostics = diagnostics; + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Language/DefaultRequiredAttributeDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Razor.Language/DefaultRequiredAttributeDescriptorBuilder.cs new file mode 100644 index 0000000000..2620051e60 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Language/DefaultRequiredAttributeDescriptorBuilder.cs @@ -0,0 +1,105 @@ +// 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; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.AspNetCore.Razor.Language +{ + internal class DefaultRequiredAttributeDescriptorBuilder : RequiredAttributeDescriptorBuilder + { + private string _name; + private RequiredAttributeDescriptor.NameComparisonMode _nameComparison; + private string _value; + private RequiredAttributeDescriptor.ValueComparisonMode _valueComparison; + private HashSet _diagnostics; + + public override RequiredAttributeDescriptorBuilder Name(string name) + { + _name = name; + + return this; + } + + public override RequiredAttributeDescriptorBuilder NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode nameComparison) + { + _nameComparison = nameComparison; + + return this; + } + + public override RequiredAttributeDescriptorBuilder Value(string value) + { + _value = value; + + return this; + } + + public override RequiredAttributeDescriptorBuilder ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode valueComparison) + { + _valueComparison = valueComparison; + + return this; + } + + public override RequiredAttributeDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic) + { + EnsureDiagnostics(); + _diagnostics.Add(diagnostic); + + return this; + } + + public RequiredAttributeDescriptor Build() + { + var validationDiagnostics = Validate(); + var diagnostics = new HashSet(validationDiagnostics); + if (_diagnostics != null) + { + diagnostics.UnionWith(_diagnostics); + } + + var displayName = _nameComparison == RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch ? string.Concat(_name, "...") : _name; + var rule = new DefaultRequiredAttributeDescriptor( + _name, + _nameComparison, + _value, + _valueComparison, + displayName, + diagnostics?.ToArray() ?? Array.Empty()); + + return rule; + } + + private IEnumerable Validate() + { + if (string.IsNullOrWhiteSpace(_name)) + { + var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeNameNullOrWhitespace(); + + yield return diagnostic; + } + else + { + foreach (var character in _name) + { + if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character)) + { + var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeName(_name, character); + + yield return diagnostic; + } + } + } + } + + private void EnsureDiagnostics() + { + if (_diagnostics == null) + { + _diagnostics = new HashSet(); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Language/DefaultTagHelperDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Language/DefaultTagHelperDescriptor.cs new file mode 100644 index 0000000000..c9e3cd55eb --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Language/DefaultTagHelperDescriptor.cs @@ -0,0 +1,36 @@ +// 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; + +namespace Microsoft.AspNetCore.Razor.Language +{ + internal class DefaultTagHelperDescriptor : TagHelperDescriptor + { + public DefaultTagHelperDescriptor( + string kind, + string name, + string assemblyName, + string displayName, + string documentation, + string tagOutputHint, + TagMatchingRuleDescriptor[] tagMatchingRules, + BoundAttributeDescriptor[] attributeDescriptors, + string[] allowedChildTags, + Dictionary metadata, + RazorDiagnostic[] diagnostics) + : base(kind) + { + Name = name; + AssemblyName = assemblyName; + DisplayName = displayName; + Documentation = documentation; + TagOutputHint = tagOutputHint; + TagMatchingRules = tagMatchingRules; + BoundAttributes = attributeDescriptors; + AllowedChildTags = allowedChildTags; + Diagnostics = diagnostics; + Metadata = metadata; + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Language/DefaultTagHelperDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Razor.Language/DefaultTagHelperDescriptorBuilder.cs new file mode 100644 index 0000000000..4ae93c528d --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Language/DefaultTagHelperDescriptorBuilder.cs @@ -0,0 +1,258 @@ +// 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; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.AspNetCore.Razor.Language +{ + internal class DefaultTagHelperDescriptorBuilder : TagHelperDescriptorBuilder + { + // Required values + private readonly string _kind; + private readonly string _name; + private readonly string _assemblyName; + private readonly Dictionary _metadata; + + private string _displayName; + private string _documentation; + private string _tagOutputHint; + private HashSet _allowedChildTags; + private List _attributeBuilders; + private List _tagMatchingRuleBuilders; + private HashSet _diagnostics; + + public DefaultTagHelperDescriptorBuilder(string kind, string name, string assemblyName) + { + _kind = kind; + _name = name; + _assemblyName = assemblyName; + + _metadata = new Dictionary(StringComparer.Ordinal); + } + + public IDictionary Metadata => _metadata; + + public override TagHelperDescriptorBuilder BindAttribute(Action configure) + { + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + EnsureAttributeBuilders(); + + var builder = new DefaultBoundAttributeDescriptorBuilder(this, _kind); + configure(builder); + _attributeBuilders.Add(builder); + return this; + } + + public override TagHelperDescriptorBuilder TagMatchingRule(Action configure) + { + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + EnsureTagMatchingRuleBuilders(); + + var builder = new DefaultTagMatchingRuleDescriptorBuilder(); + configure(builder); + _tagMatchingRuleBuilders.Add(builder); + + return this; + } + + public override TagHelperDescriptorBuilder AllowChildTag(string allowedChild) + { + EnsureAllowedChildTags(); + _allowedChildTags.Add(allowedChild); + + return this; + } + + public override TagHelperDescriptorBuilder TagOutputHint(string hint) + { + _tagOutputHint = hint; + + return this; + } + + public override TagHelperDescriptorBuilder Documentation(string documentation) + { + _documentation = documentation; + + return this; + } + + public override TagHelperDescriptorBuilder AddMetadata(string key, string value) + { + _metadata[key] = value; + + return this; + } + + public override TagHelperDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic) + { + EnsureDiagnostics(); + _diagnostics.Add(diagnostic); + + return this; + } + + public override TagHelperDescriptorBuilder DisplayName(string displayName) + { + if (displayName == null) + { + throw new ArgumentNullException(nameof(displayName)); + } + + _displayName = displayName; + + return this; + } + + public override TagHelperDescriptorBuilder TypeName(string typeName) + { + if (typeName == null) + { + throw new ArgumentNullException(nameof(typeName)); + } + + _metadata[TagHelperMetadata.Common.TypeName] = typeName; + return this; + } + + public override TagHelperDescriptor Build() + { + var validationDiagnostics = Validate(); + var diagnostics = new HashSet(validationDiagnostics); + if (_diagnostics != null) + { + diagnostics.UnionWith(_diagnostics); + } + + var tagMatchingRules = Array.Empty(); + if (_tagMatchingRuleBuilders != null) + { + var tagMatchingRuleSet = new HashSet(TagMatchingRuleDescriptorComparer.Default); + for (var i = 0; i < _tagMatchingRuleBuilders.Count; i++) + { + tagMatchingRuleSet.Add(_tagMatchingRuleBuilders[i].Build()); + } + + tagMatchingRules = tagMatchingRuleSet.ToArray(); + } + + var attributes = Array.Empty(); + if (_attributeBuilders != null) + { + var attributeSet = new HashSet(BoundAttributeDescriptorComparer.Default); + for (var i = 0; i < _attributeBuilders.Count; i++) + { + attributeSet.Add(_attributeBuilders[i].Build()); + } + + attributes = attributeSet.ToArray(); + } + + var descriptor = new DefaultTagHelperDescriptor( + _kind, + _name, + _assemblyName, + GetDisplayName(), + _documentation, + _tagOutputHint, + tagMatchingRules, + attributes, + _allowedChildTags?.ToArray() ?? Array.Empty(), + new Dictionary(_metadata), + diagnostics.ToArray()); + + return descriptor; + } + + public override void Reset() + { + _documentation = null; + _tagOutputHint = null; + _allowedChildTags?.Clear(); + _attributeBuilders?.Clear(); + _tagMatchingRuleBuilders?.Clear(); + _metadata.Clear(); + _diagnostics?.Clear(); + } + + public string GetDisplayName() + { + if (_displayName != null) + { + return _displayName; + } + + return _metadata.ContainsKey(TagHelperMetadata.Common.TypeName) ? _metadata[TagHelperMetadata.Common.TypeName] : _name; + } + + private IEnumerable Validate() + { + if (_allowedChildTags != null) + { + foreach (var name in _allowedChildTags) + { + if (string.IsNullOrWhiteSpace(name)) + { + var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRestrictedChildNullOrWhitespace(GetDisplayName()); + + yield return diagnostic; + } + else if (name != TagHelperMatchingConventions.ElementCatchAllName) + { + foreach (var character in name) + { + if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character)) + { + var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRestrictedChild(GetDisplayName(), name, character); + + yield return diagnostic; + } + } + } + } + } + } + + private void EnsureAttributeBuilders() + { + if (_attributeBuilders == null) + { + _attributeBuilders = new List(); + } + } + + private void EnsureTagMatchingRuleBuilders() + { + if (_tagMatchingRuleBuilders == null) + { + _tagMatchingRuleBuilders = new List(); + } + } + + private void EnsureAllowedChildTags() + { + if (_allowedChildTags == null) + { + _allowedChildTags = new HashSet(StringComparer.OrdinalIgnoreCase); + } + } + + private void EnsureDiagnostics() + { + if (_diagnostics == null) + { + _diagnostics = new HashSet(); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Language/DefaultTagMatchingRuleDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Language/DefaultTagMatchingRuleDescriptor.cs new file mode 100644 index 0000000000..78595a9165 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Language/DefaultTagMatchingRuleDescriptor.cs @@ -0,0 +1,22 @@ +// 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. + +namespace Microsoft.AspNetCore.Razor.Language +{ + internal class DefaultTagMatchingRuleDescriptor : TagMatchingRuleDescriptor + { + public DefaultTagMatchingRuleDescriptor( + string tagName, + string parentTag, + TagStructure tagStructure, + RequiredAttributeDescriptor[] attributes, + RazorDiagnostic[] diagnostics) + { + TagName = tagName; + ParentTag = parentTag; + TagStructure = tagStructure; + Attributes = attributes; + Diagnostics = diagnostics; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleBuilder.cs b/src/Microsoft.AspNetCore.Razor.Language/DefaultTagMatchingRuleDescriptorBuilder.cs similarity index 56% rename from src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleBuilder.cs rename to src/Microsoft.AspNetCore.Razor.Language/DefaultTagMatchingRuleDescriptorBuilder.cs index 6f576f44b6..3e1a659bcf 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleBuilder.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/DefaultTagMatchingRuleDescriptorBuilder.cs @@ -8,74 +8,56 @@ using Microsoft.AspNetCore.Razor.Language.Legacy; namespace Microsoft.AspNetCore.Razor.Language { - public sealed class TagMatchingRuleBuilder + internal class DefaultTagMatchingRuleDescriptorBuilder : TagMatchingRuleDescriptorBuilder { private string _tagName; private string _parentTag; private TagStructure _tagStructure; - private HashSet _requiredAttributeDescriptors; + private List _requiredAttributeBuilders; private HashSet _diagnostics; - private TagMatchingRuleBuilder() + internal DefaultTagMatchingRuleDescriptorBuilder() { } - public static TagMatchingRuleBuilder Create() - { - return new TagMatchingRuleBuilder(); - } - - public TagMatchingRuleBuilder RequireTagName(string tagName) + public override TagMatchingRuleDescriptorBuilder RequireTagName(string tagName) { _tagName = tagName; return this; } - public TagMatchingRuleBuilder RequireParentTag(string parentTag) + public override TagMatchingRuleDescriptorBuilder RequireParentTag(string parentTag) { _parentTag = parentTag; return this; } - public TagMatchingRuleBuilder RequireTagStructure(TagStructure tagStructure) + public override TagMatchingRuleDescriptorBuilder RequireTagStructure(TagStructure tagStructure) { _tagStructure = tagStructure; return this; } - public TagMatchingRuleBuilder RequireAttribute(RequiredAttributeDescriptor requiredAttributeDescriptor) - { - if (requiredAttributeDescriptor == null) - { - throw new ArgumentNullException(nameof(requiredAttributeDescriptor)); - } - - EnsureRequiredAttributeDescriptors(); - _requiredAttributeDescriptors.Add(requiredAttributeDescriptor); - - return this; - } - - public TagMatchingRuleBuilder RequireAttribute(Action configure) + public override TagMatchingRuleDescriptorBuilder RequireAttribute(Action configure) { if (configure == null) { throw new ArgumentNullException(nameof(configure)); } - var builder = RequiredAttributeDescriptorBuilder.Create(); + EnsureRequiredAttributeBuilders(); + var builder = new DefaultRequiredAttributeDescriptorBuilder(); configure(builder); + _requiredAttributeBuilders.Add(builder); - var requiredAttributeDescriptor = builder.Build(); - - return RequireAttribute(requiredAttributeDescriptor); + return this; } - public TagMatchingRuleBuilder AddDiagnostic(RazorDiagnostic diagnostic) + public override TagMatchingRuleDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic) { EnsureDiagnostics(); _diagnostics.Add(diagnostic); @@ -83,7 +65,7 @@ namespace Microsoft.AspNetCore.Razor.Language return this; } - public TagMatchingRule Build() + public TagMatchingRuleDescriptor Build() { var validationDiagnostics = Validate(); var diagnostics = new HashSet(validationDiagnostics); @@ -92,25 +74,28 @@ namespace Microsoft.AspNetCore.Razor.Language diagnostics.UnionWith(_diagnostics); } - var rule = new DefaultTagMatchingRule( + var requiredAttributes = Array.Empty(); + if (_requiredAttributeBuilders != null) + { + var requiredAttributeSet = new HashSet(RequiredAttributeDescriptorComparer.Default); + for (var i = 0; i < _requiredAttributeBuilders.Count; i++) + { + requiredAttributeSet.Add(_requiredAttributeBuilders[i].Build()); + } + + requiredAttributes = requiredAttributeSet.ToArray(); + } + + var rule = new DefaultTagMatchingRuleDescriptor( _tagName, _parentTag, _tagStructure, - _requiredAttributeDescriptors ?? Enumerable.Empty(), - diagnostics); + requiredAttributes, + diagnostics.ToArray()); return rule; } - public void Reset() - { - _tagName = null; - _parentTag = null; - _tagStructure = default(TagStructure); - _requiredAttributeDescriptors?.Clear(); - _diagnostics?.Clear(); - } - private IEnumerable Validate() { if (string.IsNullOrWhiteSpace(_tagName)) @@ -155,11 +140,11 @@ namespace Microsoft.AspNetCore.Razor.Language } } - private void EnsureRequiredAttributeDescriptors() + private void EnsureRequiredAttributeBuilders() { - if (_requiredAttributeDescriptors == null) + if (_requiredAttributeBuilders == null) { - _requiredAttributeDescriptors = new HashSet(RequiredAttributeDescriptorComparer.Default); + _requiredAttributeBuilders = new List(); } } @@ -170,22 +155,5 @@ namespace Microsoft.AspNetCore.Razor.Language _diagnostics = new HashSet(); } } - - private class DefaultTagMatchingRule : TagMatchingRule - { - public DefaultTagMatchingRule( - string tagName, - string parentTag, - TagStructure tagStructure, - IEnumerable requiredAttributeDescriptors, - IEnumerable diagnostics) - { - TagName = tagName; - ParentTag = parentTag; - TagStructure = tagStructure; - Attributes = new List(requiredAttributeDescriptors); - Diagnostics = new List(diagnostics); - } - } } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs index 959980e326..f138573498 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs @@ -273,14 +273,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy if (invalidRule != null) { - var typeName = descriptor.GetTypeName(); - // End tag TagHelper that states it shouldn't have an end tag. errorSink.OnError( SourceLocationTracker.Advance(tagBlock.Start, " string.Format(CultureInfo.CurrentCulture, GetString("FeatureDependencyMissing"), p0, p1, p2); /// - /// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName". + /// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "name, assemblyName". /// internal static string InvalidTagHelperLookupText { @@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Razor.Language } /// - /// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName". + /// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "name, assemblyName". /// internal static string FormatInvalidTagHelperLookupText(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperLookupText"), p0); @@ -193,186 +193,186 @@ namespace Microsoft.AspNetCore.Razor.Language => string.Format(CultureInfo.CurrentCulture, GetString("RazorTemplateEngine_ItemCouldNotBeFound"), p0); /// - /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character. /// - internal static string InvalidBoundAttributeName + internal static string TagHelper_InvalidBoundAttributeName { - get => GetString("InvalidBoundAttributeName"); + get => GetString("TagHelper_InvalidBoundAttributeName"); } /// - /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character. /// - internal static string FormatInvalidBoundAttributeName(object p0, object p1, object p2, object p3) - => string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributeName"), p0, p1, p2, p3); + internal static string FormatTagHelper_InvalidBoundAttributeName(object p0, object p1, object p2, object p3) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidBoundAttributeName"), p0, p1, p2, p3); /// - /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'. /// - internal static string InvalidBoundAttributeNameStartsWith + internal static string TagHelper_InvalidBoundAttributeNameStartsWith { - get => GetString("InvalidBoundAttributeNameStartsWith"); + get => GetString("TagHelper_InvalidBoundAttributeNameStartsWith"); } /// - /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'. /// - internal static string FormatInvalidBoundAttributeNameStartsWith(object p0, object p1, object p2, object p3) - => string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributeNameStartsWith"), p0, p1, p2, p3); + internal static string FormatTagHelper_InvalidBoundAttributeNameStartsWith(object p0, object p1, object p2, object p3) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidBoundAttributeNameStartsWith"), p0, p1, p2, p3); /// - /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with a null or empty name. /// - internal static string InvalidBoundAttributeNullOrWhitespace + internal static string TagHelper_InvalidBoundAttributeNullOrWhitespace { - get => GetString("InvalidBoundAttributeNullOrWhitespace"); + get => GetString("TagHelper_InvalidBoundAttributeNullOrWhitespace"); } /// - /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with a null or empty name. /// - internal static string FormatInvalidBoundAttributeNullOrWhitespace(object p0, object p1) - => string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributeNullOrWhitespace"), p0, p1); + internal static string FormatTagHelper_InvalidBoundAttributeNullOrWhitespace(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidBoundAttributeNullOrWhitespace"), p0, p1); /// - /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character. /// - internal static string InvalidBoundAttributePrefix + internal static string TagHelper_InvalidBoundAttributePrefix { - get => GetString("InvalidBoundAttributePrefix"); + get => GetString("TagHelper_InvalidBoundAttributePrefix"); } /// - /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character. /// - internal static string FormatInvalidBoundAttributePrefix(object p0, object p1, object p2, object p3) - => string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributePrefix"), p0, p1, p2, p3); + internal static string FormatTagHelper_InvalidBoundAttributePrefix(object p0, object p1, object p2, object p3) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidBoundAttributePrefix"), p0, p1, p2, p3); /// - /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'. /// - internal static string InvalidBoundAttributePrefixStartsWith + internal static string TagHelper_InvalidBoundAttributePrefixStartsWith { - get => GetString("InvalidBoundAttributePrefixStartsWith"); + get => GetString("TagHelper_InvalidBoundAttributePrefixStartsWith"); } /// - /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'. /// - internal static string FormatInvalidBoundAttributePrefixStartsWith(object p0, object p1, object p2, object p3) - => string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributePrefixStartsWith"), p0, p1, p2, p3); + internal static string FormatTagHelper_InvalidBoundAttributePrefixStartsWith(object p0, object p1, object p2, object p3) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidBoundAttributePrefixStartsWith"), p0, p1, p2, p3); /// - /// Invalid restricted child '{0}' for tag helper '{1}'. Tag helpers cannot restrict child elements that contain a '{2}' character. + /// Invalid restricted child '{1}' for tag helper '{0}'. Tag helpers cannot restrict child elements that contain a '{2}' character. /// - internal static string InvalidRestrictedChild + internal static string TagHelper_InvalidRestrictedChild { - get => GetString("InvalidRestrictedChild"); + get => GetString("TagHelper_InvalidRestrictedChild"); } /// - /// Invalid restricted child '{0}' for tag helper '{1}'. Tag helpers cannot restrict child elements that contain a '{2}' character. + /// Invalid restricted child '{1}' for tag helper '{0}'. Tag helpers cannot restrict child elements that contain a '{2}' character. /// - internal static string FormatInvalidRestrictedChild(object p0, object p1, object p2) - => string.Format(CultureInfo.CurrentCulture, GetString("InvalidRestrictedChild"), p0, p1, p2); + internal static string FormatTagHelper_InvalidRestrictedChild(object p0, object p1, object p2) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidRestrictedChild"), p0, p1, p2); /// /// Invalid restricted child for tag helper '{0}'. Name cannot be null or whitespace. /// - internal static string InvalidRestrictedChildNullOrWhitespace + internal static string TagHelper_InvalidRestrictedChildNullOrWhitespace { - get => GetString("InvalidRestrictedChildNullOrWhitespace"); + get => GetString("TagHelper_InvalidRestrictedChildNullOrWhitespace"); } /// /// Invalid restricted child for tag helper '{0}'. Name cannot be null or whitespace. /// - internal static string FormatInvalidRestrictedChildNullOrWhitespace(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("InvalidRestrictedChildNullOrWhitespace"), p0); + internal static string FormatTagHelper_InvalidRestrictedChildNullOrWhitespace(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidRestrictedChildNullOrWhitespace"), p0); /// /// Tag helpers cannot target attribute name '{0}' because it contains a '{1}' character. /// - internal static string InvalidTargetedAttributeName + internal static string TagHelper_InvalidTargetedAttributeName { - get => GetString("InvalidTargetedAttributeName"); + get => GetString("TagHelper_InvalidTargetedAttributeName"); } /// /// Tag helpers cannot target attribute name '{0}' because it contains a '{1}' character. /// - internal static string FormatInvalidTargetedAttributeName(object p0, object p1) - => string.Format(CultureInfo.CurrentCulture, GetString("InvalidTargetedAttributeName"), p0, p1); + internal static string FormatTagHelper_InvalidTargetedAttributeName(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidTargetedAttributeName"), p0, p1); /// /// Targeted attribute name cannot be null or whitespace. /// - internal static string InvalidTargetedAttributeNameNullOrWhitespace + internal static string TagHelper_InvalidTargetedAttributeNameNullOrWhitespace { - get => GetString("InvalidTargetedAttributeNameNullOrWhitespace"); + get => GetString("TagHelper_InvalidTargetedAttributeNameNullOrWhitespace"); } /// /// Targeted attribute name cannot be null or whitespace. /// - internal static string FormatInvalidTargetedAttributeNameNullOrWhitespace() - => GetString("InvalidTargetedAttributeNameNullOrWhitespace"); + internal static string FormatTagHelper_InvalidTargetedAttributeNameNullOrWhitespace() + => GetString("TagHelper_InvalidTargetedAttributeNameNullOrWhitespace"); /// /// Tag helpers cannot target parent tag name '{0}' because it contains a '{1}' character. /// - internal static string InvalidTargetedParentTagName + internal static string TagHelper_InvalidTargetedParentTagName { - get => GetString("InvalidTargetedParentTagName"); + get => GetString("TagHelper_InvalidTargetedParentTagName"); } /// /// Tag helpers cannot target parent tag name '{0}' because it contains a '{1}' character. /// - internal static string FormatInvalidTargetedParentTagName(object p0, object p1) - => string.Format(CultureInfo.CurrentCulture, GetString("InvalidTargetedParentTagName"), p0, p1); + internal static string FormatTagHelper_InvalidTargetedParentTagName(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidTargetedParentTagName"), p0, p1); /// /// Targeted parent tag name cannot be null or whitespace. /// - internal static string InvalidTargetedParentTagNameNullOrWhitespace + internal static string TagHelper_InvalidTargetedParentTagNameNullOrWhitespace { - get => GetString("InvalidTargetedParentTagNameNullOrWhitespace"); + get => GetString("TagHelper_InvalidTargetedParentTagNameNullOrWhitespace"); } /// /// Targeted parent tag name cannot be null or whitespace. /// - internal static string FormatInvalidTargetedParentTagNameNullOrWhitespace() - => GetString("InvalidTargetedParentTagNameNullOrWhitespace"); + internal static string FormatTagHelper_InvalidTargetedParentTagNameNullOrWhitespace() + => GetString("TagHelper_InvalidTargetedParentTagNameNullOrWhitespace"); /// /// Tag helpers cannot target tag name '{0}' because it contains a '{1}' character. /// - internal static string InvalidTargetedTagName + internal static string TagHelper_InvalidTargetedTagName { - get => GetString("InvalidTargetedTagName"); + get => GetString("TagHelper_InvalidTargetedTagName"); } /// /// Tag helpers cannot target tag name '{0}' because it contains a '{1}' character. /// - internal static string FormatInvalidTargetedTagName(object p0, object p1) - => string.Format(CultureInfo.CurrentCulture, GetString("InvalidTargetedTagName"), p0, p1); + internal static string FormatTagHelper_InvalidTargetedTagName(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidTargetedTagName"), p0, p1); /// /// Targeted tag name cannot be null or whitespace. /// - internal static string InvalidTargetedTagNameNullOrWhitespace + internal static string TagHelper_InvalidTargetedTagNameNullOrWhitespace { - get => GetString("InvalidTargetedTagNameNullOrWhitespace"); + get => GetString("TagHelper_InvalidTargetedTagNameNullOrWhitespace"); } /// /// Targeted tag name cannot be null or whitespace. /// - internal static string FormatInvalidTargetedTagNameNullOrWhitespace() - => GetString("InvalidTargetedTagNameNullOrWhitespace"); + internal static string FormatTagHelper_InvalidTargetedTagNameNullOrWhitespace() + => GetString("TagHelper_InvalidTargetedTagNameNullOrWhitespace"); /// /// The node '{0}' is not the owner of change '{1}'. diff --git a/src/Microsoft.AspNetCore.Razor.Language/RazorDiagnosticFactory.cs b/src/Microsoft.AspNetCore.Razor.Language/RazorDiagnosticFactory.cs index 80e5d6043c..8d8e7872f8 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/RazorDiagnosticFactory.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/RazorDiagnosticFactory.cs @@ -61,144 +61,144 @@ namespace Microsoft.AspNetCore.Razor.Language // TagHelper Errors ID Offset = 3000 - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRestrictedChildNullOrWhitespace = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidRestrictedChildNullOrWhitespace = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3000", - () => Resources.InvalidRestrictedChildNullOrWhitespace, + () => Resources.TagHelper_InvalidRestrictedChildNullOrWhitespace, RazorDiagnosticSeverity.Error); - public static RazorDiagnostic CreateTagHelper_InvalidRestrictedChildNullOrWhitespace(string tagHelperType) + public static RazorDiagnostic CreateTagHelper_InvalidRestrictedChildNullOrWhitespace(string tagHelperDisplayName) { var diagnostic = RazorDiagnostic.Create( TagHelper_InvalidRestrictedChildNullOrWhitespace, new SourceSpan(SourceLocation.Undefined, contentLength: 0), - tagHelperType); + tagHelperDisplayName); return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRestrictedChild = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidRestrictedChild = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3001", - () => Resources.InvalidRestrictedChild, + () => Resources.TagHelper_InvalidRestrictedChild, RazorDiagnosticSeverity.Error); - public static RazorDiagnostic CreateTagHelper_InvalidRestrictedChild(string restrictedChild, string tagHelperType, char invalidCharacter) + public static RazorDiagnostic CreateTagHelper_InvalidRestrictedChild(string tagHelperDisplayName, string restrictedChild, char invalidCharacter) { var diagnostic = RazorDiagnostic.Create( TagHelper_InvalidRestrictedChild, new SourceSpan(SourceLocation.Undefined, contentLength: 0), + tagHelperDisplayName, restrictedChild, - tagHelperType, invalidCharacter); return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeNullOrWhitespace = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeNullOrWhitespace = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3002", - () => Resources.InvalidBoundAttributeNullOrWhitespace, + () => Resources.TagHelper_InvalidBoundAttributeNullOrWhitespace, RazorDiagnosticSeverity.Error); - public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeNullOrWhitespace(string containingTypeName, string propertyName) + public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeNullOrWhitespace(string tagHelperDisplayName, string propertyDisplayName) { var diagnostic = RazorDiagnostic.Create( TagHelper_InvalidBoundAttributeNullOrWhitespace, new SourceSpan(SourceLocation.Undefined, contentLength: 0), - containingTypeName, - propertyName); + tagHelperDisplayName, + propertyDisplayName); return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeName = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeName = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3003", - () => Resources.InvalidBoundAttributeName, + () => Resources.TagHelper_InvalidBoundAttributeName, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeName( - string containingTypeName, - string propertyName, + string tagHelperDisplayName, + string propertyDisplayName, string invalidName, char invalidCharacter) { var diagnostic = RazorDiagnostic.Create( TagHelper_InvalidBoundAttributeName, new SourceSpan(SourceLocation.Undefined, contentLength: 0), - containingTypeName, - propertyName, + tagHelperDisplayName, + propertyDisplayName, invalidName, invalidCharacter); return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeNameStartsWith = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeNameStartsWith = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3004", - () => Resources.InvalidBoundAttributeNameStartsWith, + () => Resources.TagHelper_InvalidBoundAttributeNameStartsWith, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeNameStartsWith( - string containingTypeName, - string propertyName, + string tagHelperDisplayName, + string propertyDisplayName, string invalidName) { var diagnostic = RazorDiagnostic.Create( TagHelper_InvalidBoundAttributeNameStartsWith, new SourceSpan(SourceLocation.Undefined, contentLength: 0), - containingTypeName, - propertyName, + tagHelperDisplayName, + propertyDisplayName, invalidName, "data-"); return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributePrefix = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributePrefix = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3005", - () => Resources.InvalidBoundAttributePrefix, + () => Resources.TagHelper_InvalidBoundAttributePrefix, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributePrefix( - string containingTypeName, - string propertyName, + string tagHelperDisplayName, + string propertyDisplayName, string invalidName, char invalidCharacter) { var diagnostic = RazorDiagnostic.Create( TagHelper_InvalidBoundAttributePrefix, new SourceSpan(SourceLocation.Undefined, contentLength: 0), - containingTypeName, - propertyName, + tagHelperDisplayName, + propertyDisplayName, invalidName, invalidCharacter); return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributePrefixStartsWith = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributePrefixStartsWith = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3006", - () => Resources.InvalidBoundAttributePrefixStartsWith, + () => Resources.TagHelper_InvalidBoundAttributePrefixStartsWith, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributePrefixStartsWith( - string containingTypeName, - string propertyName, + string tagHelperDisplayName, + string propertyDisplayName, string invalidName) { var diagnostic = RazorDiagnostic.Create( TagHelper_InvalidBoundAttributePrefixStartsWith, new SourceSpan(SourceLocation.Undefined, contentLength: 0), - containingTypeName, - propertyName, + tagHelperDisplayName, + propertyDisplayName, invalidName, "data-"); return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedTagNameNullOrWhitespace = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedTagNameNullOrWhitespace = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3007", - () => Resources.InvalidTargetedTagNameNullOrWhitespace, + () => Resources.TagHelper_InvalidTargetedTagNameNullOrWhitespace, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidTargetedTagNameNullOrWhitespace() { @@ -209,10 +209,10 @@ namespace Microsoft.AspNetCore.Razor.Language return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedTagName = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedTagName = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3008", - () => Resources.InvalidTargetedTagName, + () => Resources.TagHelper_InvalidTargetedTagName, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidTargetedTagName(string invalidTagName, char invalidCharacter) { @@ -225,10 +225,10 @@ namespace Microsoft.AspNetCore.Razor.Language return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedParentTagNameNullOrWhitespace = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedParentTagNameNullOrWhitespace = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3009", - () => Resources.InvalidTargetedParentTagNameNullOrWhitespace, + () => Resources.TagHelper_InvalidTargetedParentTagNameNullOrWhitespace, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidTargetedParentTagNameNullOrWhitespace() { @@ -239,10 +239,10 @@ namespace Microsoft.AspNetCore.Razor.Language return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedParentTagName = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedParentTagName = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3010", - () => Resources.InvalidTargetedParentTagName, + () => Resources.TagHelper_InvalidTargetedParentTagName, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidTargetedParentTagName(string invalidTagName, char invalidCharacter) { @@ -255,10 +255,10 @@ namespace Microsoft.AspNetCore.Razor.Language return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedAttributeNameNullOrWhitespace = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedAttributeNameNullOrWhitespace = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3009", - () => Resources.InvalidTargetedAttributeNameNullOrWhitespace, + () => Resources.TagHelper_InvalidTargetedAttributeNameNullOrWhitespace, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidTargetedAttributeNameNullOrWhitespace() { @@ -269,10 +269,10 @@ namespace Microsoft.AspNetCore.Razor.Language return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedAttributeName = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedAttributeName = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3010", - () => Resources.InvalidTargetedAttributeName, + () => Resources.TagHelper_InvalidTargetedAttributeName, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidTargetedAttributeName(string invalidAttributeName, char invalidCharacter) { diff --git a/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptor.cs index 5fa519f6a7..6da95bfd4d 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptor.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptor.cs @@ -8,6 +8,7 @@ using System.Linq; namespace Microsoft.AspNetCore.Razor.Language { + [DebuggerDisplay("{DisplayName,nq}")] public abstract class RequiredAttributeDescriptor { public string Name { get; protected set; } diff --git a/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptorBuilder.cs index 098de7cea4..77f91eacff 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptorBuilder.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/RequiredAttributeDescriptorBuilder.cs @@ -1,142 +1,18 @@ // 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; -using System.Collections.Generic; -using System.Linq; - namespace Microsoft.AspNetCore.Razor.Language { - public sealed class RequiredAttributeDescriptorBuilder + public abstract class RequiredAttributeDescriptorBuilder { - private string _name; - private RequiredAttributeDescriptor.NameComparisonMode _nameComparison; - private string _value; - private RequiredAttributeDescriptor.ValueComparisonMode _valueComparison; - private HashSet _diagnostics; + public abstract RequiredAttributeDescriptorBuilder Name(string name); - private RequiredAttributeDescriptorBuilder() - { - } + public abstract RequiredAttributeDescriptorBuilder NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode nameComparison); - public static RequiredAttributeDescriptorBuilder Create() - { - return new RequiredAttributeDescriptorBuilder(); - } + public abstract RequiredAttributeDescriptorBuilder Value(string value); - public RequiredAttributeDescriptorBuilder Name(string name) - { - _name = name; + public abstract RequiredAttributeDescriptorBuilder ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode valueComparison); - return this; - } - - public RequiredAttributeDescriptorBuilder NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode nameComparison) - { - _nameComparison = nameComparison; - - return this; - } - - public RequiredAttributeDescriptorBuilder Value(string value) - { - _value = value; - - return this; - } - - public RequiredAttributeDescriptorBuilder ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode valueComparison) - { - _valueComparison = valueComparison; - - return this; - } - - public RequiredAttributeDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic) - { - EnsureDiagnostics(); - _diagnostics.Add(diagnostic); - - return this; - } - - public RequiredAttributeDescriptor Build() - { - var validationDiagnostics = Validate(); - var diagnostics = new HashSet(validationDiagnostics); - if (_diagnostics != null) - { - diagnostics.UnionWith(_diagnostics); - } - - var displayName = _nameComparison == RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch ? string.Concat(_name, "...") : _name; - var rule = new DefaultTagHelperRequiredAttributeDescriptor( - _name, - _nameComparison, - _value, - _valueComparison, - displayName, - diagnostics); - - return rule; - } - - public void Reset() - { - _name = null; - _value = null; - _nameComparison = default(RequiredAttributeDescriptor.NameComparisonMode); - _valueComparison = default(RequiredAttributeDescriptor.ValueComparisonMode); - _diagnostics?.Clear(); - } - - private IEnumerable Validate() - { - if (string.IsNullOrWhiteSpace(_name)) - { - var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeNameNullOrWhitespace(); - - yield return diagnostic; - } - else - { - foreach (var character in _name) - { - if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character)) - { - var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeName(_name, character); - - yield return diagnostic; - } - } - } - } - - private void EnsureDiagnostics() - { - if (_diagnostics == null) - { - _diagnostics = new HashSet(); - } - } - - private class DefaultTagHelperRequiredAttributeDescriptor : RequiredAttributeDescriptor - { - public DefaultTagHelperRequiredAttributeDescriptor( - string name, - NameComparisonMode nameComparison, - string value, - ValueComparisonMode valueComparison, - string displayName, - IEnumerable diagnostics) - { - Name = name; - NameComparison = nameComparison; - Value = value; - ValueComparison = valueComparison; - DisplayName = displayName; - Diagnostics = new List(diagnostics); - } - } + public abstract RequiredAttributeDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic); } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/Resources.resx b/src/Microsoft.AspNetCore.Razor.Language/Resources.resx index b0b16e933f..6e07cf8e3d 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Resources.resx +++ b/src/Microsoft.AspNetCore.Razor.Language/Resources.resx @@ -124,7 +124,7 @@ The '{0}' feature requires a '{1}' provided by the '{2}'. - Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName". + Invalid tag helper directive look up text '{0}'. The correct look up text format is: "name, assemblyName". Invalid tag helper directive '{0}' value. '{1}' is not allowed in prefix '{2}'. @@ -156,43 +156,43 @@ The item '{0}' could not be found. - - Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character. + + Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character. - - Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'. + + Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'. - - Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name. + + Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with a null or empty name. - - Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character. + + Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character. - - Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'. + + Invalid tag helper bound property '{1}' on tag helper '{0}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'. - - Invalid restricted child '{0}' for tag helper '{1}'. Tag helpers cannot restrict child elements that contain a '{2}' character. + + Invalid restricted child '{1}' for tag helper '{0}'. Tag helpers cannot restrict child elements that contain a '{2}' character. - + Invalid restricted child for tag helper '{0}'. Name cannot be null or whitespace. - + Tag helpers cannot target attribute name '{0}' because it contains a '{1}' character. - + Targeted attribute name cannot be null or whitespace. - + Tag helpers cannot target parent tag name '{0}' because it contains a '{1}' character. - + Targeted parent tag name cannot be null or whitespace. - + Tag helpers cannot target tag name '{0}' because it contains a '{1}' character. - + Targeted tag name cannot be null or whitespace. diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagHelperBinder.cs b/src/Microsoft.AspNetCore.Razor.Language/TagHelperBinder.cs index d75023fd10..529a80325f 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/TagHelperBinder.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/TagHelperBinder.cs @@ -74,7 +74,7 @@ namespace Microsoft.AspNetCore.Razor.Language } var tagNameWithoutPrefix = _tagHelperPrefix != null ? tagName.Substring(_tagHelperPrefix.Length) : tagName; - Dictionary> applicableDescriptorMappings = null; + Dictionary> applicableDescriptorMappings = null; foreach (var descriptor in descriptors) { var applicableRules = descriptor.TagMatchingRules.Where( @@ -84,7 +84,7 @@ namespace Microsoft.AspNetCore.Razor.Language { if (applicableDescriptorMappings == null) { - applicableDescriptorMappings = new Dictionary>(); + applicableDescriptorMappings = new Dictionary>(); } applicableDescriptorMappings[descriptor] = applicableRules; diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagHelperBinding.cs b/src/Microsoft.AspNetCore.Razor.Language/TagHelperBinding.cs index 6e5119a1a3..cb1e76a4c0 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/TagHelperBinding.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/TagHelperBinding.cs @@ -7,13 +7,13 @@ namespace Microsoft.AspNetCore.Razor.Language { public sealed class TagHelperBinding { - private IReadOnlyDictionary> _mappings; + private IReadOnlyDictionary> _mappings; internal TagHelperBinding( string tagName, IEnumerable> attributes, string parentTagName, - IReadOnlyDictionary> mappings, + IReadOnlyDictionary> mappings, string tagHelperPrefix) { TagName = tagName; @@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Razor.Language public string TagHelperPrefix { get; } - public IEnumerable GetBoundRules(TagHelperDescriptor descriptor) + public IEnumerable GetBoundRules(TagHelperDescriptor descriptor) { return _mappings[descriptor]; } diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagHelperConventions.cs b/src/Microsoft.AspNetCore.Razor.Language/TagHelperConventions.cs new file mode 100644 index 0000000000..f2c4e10c72 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Language/TagHelperConventions.cs @@ -0,0 +1,10 @@ +// 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. + +namespace Microsoft.AspNetCore.Razor.Language +{ + internal static class TagHelperConventions + { + internal const string DefaultKind = "ITagHelper"; + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptor.cs index 45f7eba55f..6ad3528353 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptor.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptor.cs @@ -3,10 +3,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace Microsoft.AspNetCore.Razor.Language { + [DebuggerDisplay("{DisplayName,nq}")] public abstract class TagHelperDescriptor : IEquatable { private IEnumerable _allDiagnostics; @@ -20,7 +22,7 @@ namespace Microsoft.AspNetCore.Razor.Language public string Name { get; protected set; } - public IEnumerable TagMatchingRules { get; protected set; } + public IEnumerable TagMatchingRules { get; protected set; } public string AssemblyName { get; protected set; } diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorBuilder.cs index 82f0f90e70..3f301037c8 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorBuilder.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorBuilder.cs @@ -1,273 +1,66 @@ // 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; -using System.Collections.Generic; -using System.Linq; namespace Microsoft.AspNetCore.Razor.Language { - public sealed class TagHelperDescriptorBuilder + public abstract class TagHelperDescriptorBuilder { - internal const string DescriptorKind = "ITagHelper"; - internal const string TypeNameKey = "ITagHelper.TypeName"; - - private string _displayName; - private string _documentation; - private string _tagOutputHint; - private HashSet _allowedChildTags; - private HashSet _attributeDescriptors; - private HashSet _tagMatchingRules; - private readonly string _assemblyName; - private readonly string _typeName; - private readonly Dictionary _metadata; - private HashSet _diagnostics; - - private TagHelperDescriptorBuilder(string typeName, string assemblyName) + public static TagHelperDescriptorBuilder Create(string name, string assemblyName) { - _typeName = typeName; - _displayName = _typeName; - _assemblyName = assemblyName; - _metadata = new Dictionary(StringComparer.Ordinal); - } - - public static TagHelperDescriptorBuilder Create(string typeName, string assemblyName) - { - return new TagHelperDescriptorBuilder(typeName, assemblyName); - } - - public TagHelperDescriptorBuilder BindAttribute(BoundAttributeDescriptor descriptor) - { - if (descriptor == null) + if (name == null) { - throw new ArgumentNullException(nameof(descriptor)); + throw new ArgumentNullException(nameof(name)); } - EnsureAttributeDescriptors(); - _attributeDescriptors.Add(descriptor); - - return this; - } - - public TagHelperDescriptorBuilder BindAttribute(Action configure) - { - if (configure == null) + if (assemblyName == null) { - throw new ArgumentNullException(nameof(configure)); + throw new ArgumentNullException(nameof(assemblyName)); } - var builder = BoundAttributeDescriptorBuilder.Create(_typeName); - - configure(builder); - - var attributeDescriptor = builder.Build(); - - return BindAttribute(attributeDescriptor); + return new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, name, assemblyName); } - public TagHelperDescriptorBuilder TagMatchingRule(TagMatchingRule rule) + public static TagHelperDescriptorBuilder Create(string kind, string name, string assemblyName) { - if (rule == null) + if (kind == null) { - throw new ArgumentNullException(nameof(rule)); + throw new ArgumentNullException(nameof(kind)); } - EnsureTagMatchingRules(); - _tagMatchingRules.Add(rule); - - return this; - } - - public TagHelperDescriptorBuilder TagMatchingRule(Action configure) - { - if (configure == null) + if (name == null) { - throw new ArgumentNullException(nameof(configure)); + throw new ArgumentNullException(nameof(name)); } - var builder = TagMatchingRuleBuilder.Create(); - - configure(builder); - - var rule = builder.Build(); - - return TagMatchingRule(rule); - } - - public TagHelperDescriptorBuilder AllowChildTag(string allowedChild) - { - EnsureAllowedChildTags(); - _allowedChildTags.Add(allowedChild); - - return this; - } - - public TagHelperDescriptorBuilder TagOutputHint(string hint) - { - _tagOutputHint = hint; - - return this; - } - - public TagHelperDescriptorBuilder Documentation(string documentation) - { - _documentation = documentation; - - return this; - } - - public TagHelperDescriptorBuilder AddMetadata(string key, string value) - { - _metadata[key] = value; - - return this; - } - - public TagHelperDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic) - { - EnsureDiagnostics(); - _diagnostics.Add(diagnostic); - - return this; - } - - public TagHelperDescriptorBuilder DisplayName(string displayName) - { - if (displayName == null) + if (assemblyName == null) { - throw new ArgumentNullException(nameof(displayName)); + throw new ArgumentNullException(nameof(assemblyName)); } - _displayName = displayName; - - return this; + return new DefaultTagHelperDescriptorBuilder(kind, name, assemblyName); } - public TagHelperDescriptor Build() - { - var validationDiagnostics = Validate(); - var diagnostics = new HashSet(validationDiagnostics); - if (_diagnostics != null) - { - diagnostics.UnionWith(_diagnostics); - } + public abstract TagHelperDescriptorBuilder BindAttribute(Action configure); - var descriptor = new ITagHelperDescriptor( - _typeName, - _assemblyName, - _typeName /* Name */, - _displayName, - _documentation, - _tagOutputHint, - _tagMatchingRules ?? Enumerable.Empty(), - _attributeDescriptors ?? Enumerable.Empty(), - _allowedChildTags ?? Enumerable.Empty(), - _metadata, - diagnostics); + public abstract TagHelperDescriptorBuilder TagMatchingRule(Action configure); - return descriptor; - } + public abstract TagHelperDescriptorBuilder AllowChildTag(string allowedChild); - public void Reset() - { - _documentation = null; - _tagOutputHint = null; - _allowedChildTags?.Clear(); - _attributeDescriptors?.Clear(); - _tagMatchingRules?.Clear(); - _metadata.Clear(); - _diagnostics?.Clear(); - } + public abstract TagHelperDescriptorBuilder TagOutputHint(string hint); - private IEnumerable Validate() - { - if (_allowedChildTags != null) - { - foreach (var name in _allowedChildTags) - { - if (string.IsNullOrWhiteSpace(name)) - { - var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRestrictedChildNullOrWhitespace(_typeName); + public abstract TagHelperDescriptorBuilder Documentation(string documentation); - yield return diagnostic; - } - else if (name != TagHelperMatchingConventions.ElementCatchAllName) - { - foreach (var character in name) - { - if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character)) - { - var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRestrictedChild(name, _typeName, character); + public abstract TagHelperDescriptorBuilder AddMetadata(string key, string value); - yield return diagnostic; - } - } - } - } - } - } + public abstract TagHelperDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic); - private void EnsureAttributeDescriptors() - { - if (_attributeDescriptors == null) - { - _attributeDescriptors = new HashSet(BoundAttributeDescriptorComparer.Default); - } - } + public abstract TagHelperDescriptorBuilder DisplayName(string displayName); - private void EnsureTagMatchingRules() - { - if (_tagMatchingRules == null) - { - _tagMatchingRules = new HashSet(TagMatchingRuleComparer.Default); - } - } + public abstract TagHelperDescriptorBuilder TypeName(string typeName); - private void EnsureAllowedChildTags() - { - if (_allowedChildTags == null) - { - _allowedChildTags = new HashSet(StringComparer.OrdinalIgnoreCase); - } - } + public abstract TagHelperDescriptor Build(); - private void EnsureDiagnostics() - { - if (_diagnostics == null) - { - _diagnostics = new HashSet(); - } - } - - private class ITagHelperDescriptor : TagHelperDescriptor - { - public ITagHelperDescriptor( - string typeName, - string assemblyName, - string name, - string displayName, - string documentation, - string tagOutputHint, - IEnumerable tagMatchingRules, - IEnumerable attributeDescriptors, - IEnumerable allowedChildTags, - Dictionary metadata, - IEnumerable diagnostics) : base(DescriptorKind) - { - Name = typeName; - AssemblyName = assemblyName; - DisplayName = displayName; - Documentation = documentation; - TagOutputHint = tagOutputHint; - TagMatchingRules = new List(tagMatchingRules); - BoundAttributes = new List(attributeDescriptors); - AllowedChildTags = new List(allowedChildTags); - Diagnostics = new List(diagnostics); - Metadata = new Dictionary(metadata) - { - [TypeNameKey] = typeName - }; - } - } + public abstract void Reset(); } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorComparer.cs b/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorComparer.cs index 09e822b726..b45aaa2c98 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorComparer.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorComparer.cs @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Language private readonly StringComparer _stringComparer; private readonly StringComparison _stringComparison; private readonly BoundAttributeDescriptorComparer _boundAttributeComparer; - private readonly TagMatchingRuleComparer _tagMatchingRuleComparer; + private readonly TagMatchingRuleDescriptorComparer _tagMatchingRuleComparer; private TagHelperDescriptorComparer(bool caseSensitive = false) { @@ -33,14 +33,14 @@ namespace Microsoft.AspNetCore.Razor.Language _stringComparer = StringComparer.Ordinal; _stringComparison = StringComparison.Ordinal; _boundAttributeComparer = BoundAttributeDescriptorComparer.CaseSensitive; - _tagMatchingRuleComparer = TagMatchingRuleComparer.CaseSensitive; + _tagMatchingRuleComparer = TagMatchingRuleDescriptorComparer.CaseSensitive; } else { _stringComparer = StringComparer.OrdinalIgnoreCase; _stringComparison = StringComparison.OrdinalIgnoreCase; _boundAttributeComparer = BoundAttributeDescriptorComparer.Default; - _tagMatchingRuleComparer = TagMatchingRuleComparer.Default; + _tagMatchingRuleComparer = TagMatchingRuleDescriptorComparer.Default; } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorExtensions.cs b/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorExtensions.cs index ead9d6ca62..e08126cbc2 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorExtensions.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/TagHelperDescriptorExtensions.cs @@ -9,12 +9,14 @@ namespace Microsoft.AspNetCore.Razor.Language { public static string GetTypeName(this TagHelperDescriptor descriptor) { - descriptor.Metadata.TryGetValue(TagHelperDescriptorBuilder.TypeNameKey, out var typeName); + descriptor.Metadata.TryGetValue(TagHelperMetadata.Common.TypeName, out var typeName); return typeName; } public static bool IsDefaultKind(this TagHelperDescriptor descriptor) - => string.Equals(descriptor.Kind, TagHelperDescriptorBuilder.DescriptorKind, StringComparison.Ordinal); + { + return string.Equals(descriptor.Kind, TagHelperConventions.DefaultKind, StringComparison.Ordinal); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagHelperMatchingConventions.cs b/src/Microsoft.AspNetCore.Razor.Language/TagHelperMatchingConventions.cs index c930f0914b..6e6b2442de 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/TagHelperMatchingConventions.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/TagHelperMatchingConventions.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Razor.Language string tagNameWithoutPrefix, string parentTagName, IEnumerable> tagAttributes, - TagMatchingRule rule) + TagMatchingRuleDescriptor rule) { if (tagNameWithoutPrefix == null) { @@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Razor.Language return true; } - public static bool SatisfiesTagName(string tagNameWithoutPrefix, TagMatchingRule rule) + public static bool SatisfiesTagName(string tagNameWithoutPrefix, TagMatchingRuleDescriptor rule) { if (tagNameWithoutPrefix == null) { @@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Razor.Language return true; } - public static bool SatisfiesParentTag(string parentTagName, TagMatchingRule rule) + public static bool SatisfiesParentTag(string parentTagName, TagMatchingRuleDescriptor rule) { if (rule == null) { @@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Razor.Language return true; } - public static bool SatisfiesAttributes(IEnumerable> tagAttributes, TagMatchingRule rule) + public static bool SatisfiesAttributes(IEnumerable> tagAttributes, TagMatchingRuleDescriptor rule) { if (tagAttributes == null) { diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagHelperMetadata.cs b/src/Microsoft.AspNetCore.Razor.Language/TagHelperMetadata.cs new file mode 100644 index 0000000000..d51b795caa --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Language/TagHelperMetadata.cs @@ -0,0 +1,15 @@ +// 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. + +namespace Microsoft.AspNetCore.Razor.Language +{ + internal static class TagHelperMetadata + { + public static class Common + { + internal const string PropertyName = "Common.PropertyName"; + + internal const string TypeName = "Common.TypeName"; + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRule.cs b/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptor.cs similarity index 81% rename from src/Microsoft.AspNetCore.Razor.Language/TagMatchingRule.cs rename to src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptor.cs index 998690fefd..847eb294f1 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRule.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptor.cs @@ -7,7 +7,7 @@ using System.Linq; namespace Microsoft.AspNetCore.Razor.Language { - public abstract class TagMatchingRule : IEquatable + public abstract class TagMatchingRuleDescriptor : IEquatable { private IEnumerable _allDiagnostics; @@ -44,19 +44,19 @@ namespace Microsoft.AspNetCore.Razor.Language return _allDiagnostics; } - public bool Equals(TagMatchingRule other) + public bool Equals(TagMatchingRuleDescriptor other) { - return TagMatchingRuleComparer.Default.Equals(this, other); + return TagMatchingRuleDescriptorComparer.Default.Equals(this, other); } public override bool Equals(object obj) { - return Equals(obj as TagMatchingRule); + return Equals(obj as TagMatchingRuleDescriptor); } public override int GetHashCode() { - return TagMatchingRuleComparer.Default.GetHashCode(this); + return TagMatchingRuleDescriptorComparer.Default.GetHashCode(this); } } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptorBuilder.cs new file mode 100644 index 0000000000..d3cab36921 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptorBuilder.cs @@ -0,0 +1,20 @@ +// 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; + +namespace Microsoft.AspNetCore.Razor.Language +{ + public abstract class TagMatchingRuleDescriptorBuilder + { + public abstract TagMatchingRuleDescriptorBuilder RequireTagName(string tagName); + + public abstract TagMatchingRuleDescriptorBuilder RequireParentTag(string parentTag); + + public abstract TagMatchingRuleDescriptorBuilder RequireTagStructure(TagStructure tagStructure); + + public abstract TagMatchingRuleDescriptorBuilder RequireAttribute(Action configure); + + public abstract TagMatchingRuleDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic); + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleComparer.cs b/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptorComparer.cs similarity index 79% rename from src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleComparer.cs rename to src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptorComparer.cs index e1a3d1c49a..c77bdfbddb 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleComparer.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/TagMatchingRuleDescriptorComparer.cs @@ -9,24 +9,24 @@ using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Razor.Language { - internal class TagMatchingRuleComparer : IEqualityComparer + internal class TagMatchingRuleDescriptorComparer : IEqualityComparer { /// - /// A default instance of the . + /// A default instance of the . /// - public static readonly TagMatchingRuleComparer Default = new TagMatchingRuleComparer(); + public static readonly TagMatchingRuleDescriptorComparer Default = new TagMatchingRuleDescriptorComparer(); /// - /// A default instance of the that does case-sensitive comparison. + /// A default instance of the that does case-sensitive comparison. /// - internal static readonly TagMatchingRuleComparer CaseSensitive = - new TagMatchingRuleComparer(caseSensitive: true); + internal static readonly TagMatchingRuleDescriptorComparer CaseSensitive = + new TagMatchingRuleDescriptorComparer(caseSensitive: true); private readonly StringComparer _stringComparer; private readonly StringComparison _stringComparison; private readonly RequiredAttributeDescriptorComparer _requiredAttributeComparer; - private TagMatchingRuleComparer(bool caseSensitive = false) + private TagMatchingRuleDescriptorComparer(bool caseSensitive = false) { if (caseSensitive) { @@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Razor.Language } } - public virtual bool Equals(TagMatchingRule ruleX, TagMatchingRule ruleY) + public virtual bool Equals(TagMatchingRuleDescriptor ruleX, TagMatchingRuleDescriptor ruleY) { if (object.ReferenceEquals(ruleX, ruleY)) { @@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.Razor.Language Enumerable.SequenceEqual(ruleX.Diagnostics, ruleY.Diagnostics); } - public virtual int GetHashCode(TagMatchingRule rule) + public virtual int GetHashCode(TagMatchingRuleDescriptor rule) { if (rule == null) { diff --git a/src/Microsoft.CodeAnalysis.Razor/DefaultTagHelperDescriptorFactory.cs b/src/Microsoft.CodeAnalysis.Razor/DefaultTagHelperDescriptorFactory.cs index 148e35a557..f2cf712ceb 100644 --- a/src/Microsoft.CodeAnalysis.Razor/DefaultTagHelperDescriptorFactory.cs +++ b/src/Microsoft.CodeAnalysis.Razor/DefaultTagHelperDescriptorFactory.cs @@ -58,6 +58,7 @@ namespace Microsoft.CodeAnalysis.Razor var typeName = GetFullName(type); var assemblyName = type.ContainingAssembly.Identity.Name; var descriptorBuilder = TagHelperDescriptorBuilder.Create(typeName, assemblyName); + descriptorBuilder.TypeName(typeName); AddBoundAttributes(type, descriptorBuilder); AddTagMatchingRules(type, descriptorBuilder); diff --git a/src/Microsoft.CodeAnalysis.Razor/Properties/Resources.Designer.cs b/src/Microsoft.CodeAnalysis.Razor/Properties/Resources.Designer.cs index 4c8094d092..df00f34c36 100644 --- a/src/Microsoft.CodeAnalysis.Razor/Properties/Resources.Designer.cs +++ b/src/Microsoft.CodeAnalysis.Razor/Properties/Resources.Designer.cs @@ -13,114 +13,114 @@ namespace Microsoft.CodeAnalysis.Razor /// /// Could not find matching ']' for required attribute '{0}'. /// - internal static string TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace + internal static string TagHelper_CouldNotFindMatchingEndBrace { - get => GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"); + get => GetString("TagHelper_CouldNotFindMatchingEndBrace"); } /// /// Could not find matching ']' for required attribute '{0}'. /// - internal static string FormatTagHelperDescriptorFactory_CouldNotFindMatchingEndBrace(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"), p0); + internal static string FormatTagHelper_CouldNotFindMatchingEndBrace(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_CouldNotFindMatchingEndBrace"), p0); /// - /// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. '{2}.{3}' must be null unless property type implements '{4}'. /// - internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNotNull + internal static string TagHelper_InvalidAttributePrefixNotNull { - get => GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"); + get => GetString("TagHelper_InvalidAttributePrefixNotNull"); } /// - /// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. '{2}.{3}' must be null unless property type implements '{4}'. /// - internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNotNull(object p0, object p1, object p2, object p3, object p4) - => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"), p0, p1, p2, p3, p4); + internal static string FormatTagHelper_InvalidAttributePrefixNotNull(object p0, object p1, object p2, object p3, object p4) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidAttributePrefixNotNull"), p0, p1, p2, p3, p4); /// - /// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. '{2}.{3}' must be null or empty if property has no public setter. /// - internal static string TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty + internal static string TagHelper_InvalidAttributeNameNotNullOrEmpty { - get => GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty"); + get => GetString("TagHelper_InvalidAttributeNameNotNullOrEmpty"); } /// - /// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. '{2}.{3}' must be null or empty if property has no public setter. /// - internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty(object p0, object p1, object p2, object p3) - => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty"), p0, p1, p2, p3); + internal static string FormatTagHelper_InvalidAttributeNameNotNullOrEmpty(object p0, object p1, object p2, object p3) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidAttributeNameNotNullOrEmpty"), p0, p1, p2, p3); /// - /// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'. /// - internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNull + internal static string TagHelper_InvalidAttributePrefixNull { - get => GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"); + get => GetString("TagHelper_InvalidAttributePrefixNull"); } /// - /// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'. + /// Invalid tag helper bound property '{1}' on tag helper '{0}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'. /// - internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNull(object p0, object p1, object p2, object p3, object p4) - => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"), p0, p1, p2, p3, p4); + internal static string FormatTagHelper_InvalidAttributePrefixNull(object p0, object p1, object p2, object p3, object p4) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidAttributePrefixNull"), p0, p1, p2, p3, p4); /// /// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas. /// - internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter + internal static string TagHelper_InvalidRequiredAttributeCharacter { - get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"); + get => GetString("TagHelper_InvalidRequiredAttributeCharacter"); } /// /// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas. /// - internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeCharacter(object p0, object p1) - => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"), p0, p1); + internal static string FormatTagHelper_InvalidRequiredAttributeCharacter(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidRequiredAttributeCharacter"), p0, p1); /// /// Required attribute '{0}' has mismatched quotes '{1}' around value. /// - internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes + internal static string TagHelper_InvalidRequiredAttributeMismatchedQuotes { - get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"); + get => GetString("TagHelper_InvalidRequiredAttributeMismatchedQuotes"); } /// /// Required attribute '{0}' has mismatched quotes '{1}' around value. /// - internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes(object p0, object p1) - => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"), p0, p1); + internal static string FormatTagHelper_InvalidRequiredAttributeMismatchedQuotes(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidRequiredAttributeMismatchedQuotes"), p0, p1); /// /// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'. /// - internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeOperator + internal static string TagHelper_InvalidRequiredAttributeOperator { - get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"); + get => GetString("TagHelper_InvalidRequiredAttributeOperator"); } /// /// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'. /// - internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeOperator(object p0, object p1) - => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"), p0, p1); + internal static string FormatTagHelper_InvalidRequiredAttributeOperator(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_InvalidRequiredAttributeOperator"), p0, p1); /// /// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals. /// - internal static string TagHelperDescriptorFactory_PartialRequiredAttributeOperator + internal static string TagHelper_PartialRequiredAttributeOperator { - get => GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"); + get => GetString("TagHelper_PartialRequiredAttributeOperator"); } /// /// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals. /// - internal static string FormatTagHelperDescriptorFactory_PartialRequiredAttributeOperator(object p0, object p1) - => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"), p0, p1); + internal static string FormatTagHelper_PartialRequiredAttributeOperator(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("TagHelper_PartialRequiredAttributeOperator"), p0, p1); private static string GetString(string name, params string[] formatterNames) { diff --git a/src/Microsoft.CodeAnalysis.Razor/RazorDiagnosticFactory.cs b/src/Microsoft.CodeAnalysis.Razor/RazorDiagnosticFactory.cs index e57e28439d..12fa708189 100644 --- a/src/Microsoft.CodeAnalysis.Razor/RazorDiagnosticFactory.cs +++ b/src/Microsoft.CodeAnalysis.Razor/RazorDiagnosticFactory.cs @@ -44,36 +44,36 @@ namespace Microsoft.CodeAnalysis.Razor * TagHelper Errors ID Offset = 3500 */ - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributeNameNullOrEmpty = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributeNameNullOrEmpty = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3500", - () => Resources.TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty, + () => Resources.TagHelper_InvalidAttributeNameNotNullOrEmpty, RazorDiagnosticSeverity.Error); - public static RazorDiagnostic CreateTagHelper_InvalidAttributeNameNullOrEmpty(string containingTypeName, string boundPropertyName) + public static RazorDiagnostic CreateTagHelper_InvalidAttributeNameNullOrEmpty(string tagHelperDisplayName, string propertyDisplayName) { var diagnostic = RazorDiagnostic.Create( TagHelper_InvalidAttributeNameNullOrEmpty, new SourceSpan(SourceLocation.Undefined, contentLength: 0), - containingTypeName, - boundPropertyName, + tagHelperDisplayName, + propertyDisplayName, TagHelperTypes.HtmlAttributeNameAttribute, TagHelperTypes.HtmlAttributeName.Name); return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributePrefixNotNull = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributePrefixNotNull = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3501", - () => Resources.TagHelperDescriptorFactory_InvalidAttributePrefixNotNull, + () => Resources.TagHelper_InvalidAttributePrefixNotNull, RazorDiagnosticSeverity.Error); - public static RazorDiagnostic CreateTagHelper_InvalidAttributePrefixNotNull(string containingTypeName, string boundPropertyName) + public static RazorDiagnostic CreateTagHelper_InvalidAttributePrefixNotNull(string tagHelperDisplayName, string propertyDisplayName) { var diagnostic = RazorDiagnostic.Create( TagHelper_InvalidAttributePrefixNotNull, new SourceSpan(SourceLocation.Undefined, contentLength: 0), - containingTypeName, - boundPropertyName, + tagHelperDisplayName, + propertyDisplayName, TagHelperTypes.HtmlAttributeNameAttribute, TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix, "IDictionary"); @@ -81,18 +81,18 @@ namespace Microsoft.CodeAnalysis.Razor return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributePrefixNull = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributePrefixNull = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3502", - () => Resources.TagHelperDescriptorFactory_InvalidAttributePrefixNull, + () => Resources.TagHelper_InvalidAttributePrefixNull, RazorDiagnosticSeverity.Error); - public static RazorDiagnostic CreateTagHelper_InvalidAttributePrefixNull(string containingTypeName, string boundPropertyName) + public static RazorDiagnostic CreateTagHelper_InvalidAttributePrefixNull(string tagHelperDisplayName, string propertyDisplayName) { var diagnostic = RazorDiagnostic.Create( TagHelper_InvalidAttributePrefixNull, new SourceSpan(SourceLocation.Undefined, contentLength: 0), - containingTypeName, - boundPropertyName, + tagHelperDisplayName, + propertyDisplayName, TagHelperTypes.HtmlAttributeNameAttribute, TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix, "IDictionary"); @@ -100,10 +100,10 @@ namespace Microsoft.CodeAnalysis.Razor return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeCharacter = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeCharacter = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3503", - () => Resources.TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter, + () => Resources.TagHelper_InvalidRequiredAttributeCharacter, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidRequiredAttributeCharacter(char invalidCharacter, string requiredAttributes) { @@ -116,10 +116,10 @@ namespace Microsoft.CodeAnalysis.Razor return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_PartialRequiredAttributeOperator = + internal static readonly RazorDiagnosticDescriptor TagHelper_PartialRequiredAttributeOperator = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3504", - () => Resources.TagHelperDescriptorFactory_PartialRequiredAttributeOperator, + () => Resources.TagHelper_PartialRequiredAttributeOperator, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_PartialRequiredAttributeOperator(char partialOperator, string requiredAttributes) { @@ -132,10 +132,10 @@ namespace Microsoft.CodeAnalysis.Razor return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeOperator = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeOperator = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3505", - () => Resources.TagHelperDescriptorFactory_InvalidRequiredAttributeOperator, + () => Resources.TagHelper_InvalidRequiredAttributeOperator, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidRequiredAttributeOperator(char invalidOperator, string requiredAttributes) { @@ -148,10 +148,10 @@ namespace Microsoft.CodeAnalysis.Razor return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeMismatchedQuotes = + internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeMismatchedQuotes = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3506", - () => Resources.TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes, + () => Resources.TagHelper_InvalidRequiredAttributeMismatchedQuotes, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_InvalidRequiredAttributeMismatchedQuotes(char quote, string requiredAttributes) { @@ -164,10 +164,10 @@ namespace Microsoft.CodeAnalysis.Razor return diagnostic; } - private static readonly RazorDiagnosticDescriptor TagHelper_CouldNotFindMatchingEndBrace = + internal static readonly RazorDiagnosticDescriptor TagHelper_CouldNotFindMatchingEndBrace = new RazorDiagnosticDescriptor( $"{DiagnosticPrefix}3507", - () => Resources.TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace, + () => Resources.TagHelper_CouldNotFindMatchingEndBrace, RazorDiagnosticSeverity.Error); public static RazorDiagnostic CreateTagHelper_CouldNotFindMatchingEndBrace(string requiredAttributes) { diff --git a/src/Microsoft.CodeAnalysis.Razor/RequiredAttributeParser.cs b/src/Microsoft.CodeAnalysis.Razor/RequiredAttributeParser.cs index 9c3653aecb..1946a723b5 100644 --- a/src/Microsoft.CodeAnalysis.Razor/RequiredAttributeParser.cs +++ b/src/Microsoft.CodeAnalysis.Razor/RequiredAttributeParser.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Razor // Internal for testing internal static class RequiredAttributeParser { - public static void AddRequiredAttributes(string requiredAttributes, TagMatchingRuleBuilder ruleBuilder) + public static void AddRequiredAttributes(string requiredAttributes, TagMatchingRuleDescriptorBuilder ruleBuilder) { var requiredAttributeParser = new DefaultRequiredAttributeParser(requiredAttributes); requiredAttributeParser.AddRequiredAttributes(ruleBuilder); @@ -46,7 +46,7 @@ namespace Microsoft.CodeAnalysis.Razor private bool AtEnd => _index >= _requiredAttributes.Length; - public void AddRequiredAttributes(TagMatchingRuleBuilder ruleBuilder) + public void AddRequiredAttributes(TagMatchingRuleDescriptorBuilder ruleBuilder) { if (string.IsNullOrEmpty(_requiredAttributes)) { diff --git a/src/Microsoft.CodeAnalysis.Razor/Resources.resx b/src/Microsoft.CodeAnalysis.Razor/Resources.resx index edfa6f2970..fd92b354d9 100644 --- a/src/Microsoft.CodeAnalysis.Razor/Resources.resx +++ b/src/Microsoft.CodeAnalysis.Razor/Resources.resx @@ -117,28 +117,28 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + Could not find matching ']' for required attribute '{0}'. - - Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'. + + Invalid tag helper bound property '{1}' on tag helper '{0}'. '{2}.{3}' must be null unless property type implements '{4}'. - - Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter. + + Invalid tag helper bound property '{1}' on tag helper '{0}'. '{2}.{3}' must be null or empty if property has no public setter. - - Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'. + + Invalid tag helper bound property '{1}' on tag helper '{0}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'. - + Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas. - + Required attribute '{0}' has mismatched quotes '{1}' around value. - + Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'. - + Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals. \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/TagHelperDescriptorJsonConverter.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/TagHelperDescriptorJsonConverter.cs index 639270b2f6..572459e707 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/TagHelperDescriptorJsonConverter.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/TagHelperDescriptorJsonConverter.cs @@ -87,13 +87,13 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor throw new NotImplementedException(); } - private void ReadTagMatchingRule(TagMatchingRuleBuilder builder, JObject rule, JsonSerializer serializer) + private void ReadTagMatchingRule(TagMatchingRuleDescriptorBuilder builder, JObject rule, JsonSerializer serializer) { - var tagName = rule[nameof(TagMatchingRule.TagName)].Value(); - var attributes = rule[nameof(TagMatchingRule.Attributes)].Value(); - var parentTag = rule[nameof(TagMatchingRule.ParentTag)].Value(); - var tagStructure = rule[nameof(TagMatchingRule.TagStructure)].Value(); - var diagnostics = rule[nameof(TagMatchingRule.Diagnostics)].Value(); + var tagName = rule[nameof(TagMatchingRuleDescriptor.TagName)].Value(); + var attributes = rule[nameof(TagMatchingRuleDescriptor.Attributes)].Value(); + var parentTag = rule[nameof(TagMatchingRuleDescriptor.ParentTag)].Value(); + var tagStructure = rule[nameof(TagMatchingRuleDescriptor.TagStructure)].Value(); + var diagnostics = rule[nameof(TagMatchingRuleDescriptor.Diagnostics)].Value(); builder .RequireTagName(tagName) @@ -139,11 +139,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor private void ReadBoundAttribute(BoundAttributeDescriptorBuilder builder, JObject attribute, JsonSerializer serializer) { var descriptorKind = attribute[nameof(BoundAttributeDescriptor.Kind)].Value(); - if (descriptorKind != BoundAttributeDescriptorBuilder.DescriptorKind) - { - throw new NotSupportedException(); - } - var name = attribute[nameof(BoundAttributeDescriptor.Name)].Value(); var typeName = attribute[nameof(BoundAttributeDescriptor.TypeName)].Value(); var isEnum = attribute[nameof(BoundAttributeDescriptor.IsEnum)].Value(); @@ -181,9 +176,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor { builder.AddMetadata(item.Key, item.Value); } - - var propertyName = metadataValue[BoundAttributeDescriptorBuilder.PropertyNameKey]; - builder.PropertyName(propertyName); } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/IntegrationTests/InstrumentationPassIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/IntegrationTests/InstrumentationPassIntegrationTest.cs index 5059869898..1ce20724f2 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/IntegrationTests/InstrumentationPassIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/IntegrationTests/InstrumentationPassIntegrationTest.cs @@ -72,6 +72,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.IntegrationTests IEnumerable> attributes = null) { var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName); + builder.TypeName(typeName); if (attributes != null) { diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TagHelperDescriptorExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TagHelperDescriptorExtensionsTest.cs new file mode 100644 index 0000000000..7fea99dae3 --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/TagHelperDescriptorExtensionsTest.cs @@ -0,0 +1,82 @@ +// 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 Microsoft.AspNetCore.Razor.Language; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.Razor.Extensions +{ + public class TagHelperDescriptorExtensionsTest + { + [Fact] + public void IsViewComponentKind_ReturnsFalse_ForNonVCTHDescriptor() + { + //Arrange + var tagHelper = CreateTagHelperDescriptor(); + + // Act + var result = tagHelper.IsViewComponentKind(); + + // Assert + Assert.False(result); + } + + [Fact] + public void IsViewComponentKind_ReturnsTrue_ForVCTHDescriptor() + { + // Arrange + var tagHelper = CreateViewComponentTagHelperDescriptor(); + + // Act + var result = tagHelper.IsViewComponentKind(); + + // Assert + Assert.True(result); + } + + [Fact] + public void GetViewComponentName_ReturnsNull_ForNonVCTHDescriptor() + { + //Arrange + var tagHelper = CreateTagHelperDescriptor(); + + // Act + var result = tagHelper.GetViewComponentName(); + + // Assert + Assert.Null(result); + } + + [Fact] + public void GetViewComponentName_ReturnsName_ForVCTHDescriptor() + { + // Arrange + var tagHelper = CreateViewComponentTagHelperDescriptor("ViewComponentName"); + + // Act + var result = tagHelper.GetViewComponentName(); + + // Assert + Assert.Equal("ViewComponentName", result); + } + + private static TagHelperDescriptor CreateTagHelperDescriptor() + { + var descriptor = TagHelperDescriptorBuilder.Create("TypeName", "AssemblyName") + .TagMatchingRule(rule => rule.RequireTagName("tag-name")) + .Build(); + + return descriptor; + } + + private static TagHelperDescriptor CreateViewComponentTagHelperDescriptor(string name = "ViewComponentName") + { + var descriptor = TagHelperDescriptorBuilder.Create(ViewComponentTagHelperConventions.Kind, "TypeName", "AssemblyName") + .TagMatchingRule(rule => rule.RequireTagName("tag-name")) + .AddMetadata(ViewComponentTagHelperMetadata.Name, name) + .Build(); + + return descriptor; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorConventionsTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorConventionsTest.cs deleted file mode 100644 index f0bd076179..0000000000 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorConventionsTest.cs +++ /dev/null @@ -1,61 +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 Microsoft.AspNetCore.Razor.Language; -using Xunit; - -namespace Microsoft.AspNetCore.Mvc.Razor.Extensions -{ - public class ViewComponentTagHelperDescriptorConventionsTest - { - [Fact] - public void IsViewComponentDescriptor_ReturnsFalseForInvalidDescriptor() - { - //Arrange - var tagHelperDescriptor = CreateTagHelperDescriptor(); - - // Act - var isViewComponentDescriptor = ViewComponentTagHelperDescriptorConventions - .IsViewComponentDescriptor(tagHelperDescriptor); - - // Assert - Assert.False(isViewComponentDescriptor); - } - - [Fact] - public void IsViewComponentDescriptor_ReturnsTrueForValidDescriptor() - { - // Arrange - var descriptor = CreateViewComponentTagHelperDescriptor(); - - // Act - var isViewComponentDescriptor = ViewComponentTagHelperDescriptorConventions - .IsViewComponentDescriptor(descriptor); - - // Assert - Assert.True(isViewComponentDescriptor); - } - - private static TagHelperDescriptor CreateTagHelperDescriptor() - { - var descriptor = TagHelperDescriptorBuilder.Create("TypeName", "AssemblyName") - .TagMatchingRule(rule => - rule.RequireTagName("tag-name")) - .Build(); - - - return descriptor; - } - - private static TagHelperDescriptor CreateViewComponentTagHelperDescriptor() - { - var descriptor = TagHelperDescriptorBuilder.Create("TypeName", "AssemblyName") - .TagMatchingRule(rule => - rule.RequireTagName("tag-name")) - .AddMetadata(ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey, "ViewComponentName") - .Build(); - - return descriptor; - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorFactoryTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorFactoryTest.cs index fcfd3b7bf9..b4996badc1 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorFactoryTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorFactoryTest.cs @@ -21,9 +21,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions var testCompilation = TestCompilation.Create(_assembly); var viewComponent = testCompilation.GetTypeByMetadataName(typeof(StringParameterViewComponent).FullName); var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation); + var expectedDescriptor = TagHelperDescriptorBuilder.Create( + ViewComponentTagHelperConventions.Kind, "__Generated__StringParameterViewComponentTagHelper", typeof(StringParameterViewComponent).GetTypeInfo().Assembly.GetName().Name) + .TypeName("__Generated__StringParameterViewComponentTagHelper") .DisplayName("StringParameterViewComponentTagHelper") .TagMatchingRule(rule => rule @@ -42,7 +45,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions .PropertyName("bar") .TypeName(typeof(string).FullName) .DisplayName("string StringParameterViewComponentTagHelper.bar")) - .AddMetadata(ViewComponentTypes.ViewComponentNameKey, "StringParameter") + .AddMetadata(ViewComponentTagHelperMetadata.Name, "StringParameter") .Build(); // Act @@ -59,9 +62,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions var testCompilation = TestCompilation.Create(_assembly); var viewComponent = testCompilation.GetTypeByMetadataName(typeof(VariousParameterViewComponent).FullName); var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation); + var expectedDescriptor = TagHelperDescriptorBuilder.Create( + ViewComponentTagHelperConventions.Kind, "__Generated__VariousParameterViewComponentTagHelper", typeof(VariousParameterViewComponent).GetTypeInfo().Assembly.GetName().Name) + .TypeName("__Generated__VariousParameterViewComponentTagHelper") .DisplayName("VariousParameterViewComponentTagHelper") .TagMatchingRule(rule => rule @@ -88,7 +94,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions .PropertyName("baz") .TypeName(typeof(int).FullName) .DisplayName("int VariousParameterViewComponentTagHelper.baz")) - .AddMetadata(ViewComponentTypes.ViewComponentNameKey, "VariousParameter") + .AddMetadata(ViewComponentTagHelperMetadata.Name, "VariousParameter") .Build(); // Act @@ -105,9 +111,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions var testCompilation = TestCompilation.Create(_assembly); var viewComponent = testCompilation.GetTypeByMetadataName(typeof(GenericParameterViewComponent).FullName); var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation); + var expectedDescriptor = TagHelperDescriptorBuilder.Create( + ViewComponentTagHelperConventions.Kind, "__Generated__GenericParameterViewComponentTagHelper", typeof(GenericParameterViewComponent).GetTypeInfo().Assembly.GetName().Name) + .TypeName("__Generated__GenericParameterViewComponentTagHelper") .DisplayName("GenericParameterViewComponentTagHelper") .TagMatchingRule(rule => rule @@ -126,7 +135,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions .TypeName("System.Collections.Generic.Dictionary") .AsDictionary("bar-", typeof(int).FullName) .DisplayName("System.Collections.Generic.Dictionary GenericParameterViewComponentTagHelper.Bar")) - .AddMetadata(ViewComponentTypes.ViewComponentNameKey, "GenericParameter") + .AddMetadata(ViewComponentTagHelperMetadata.Name, "GenericParameter") .Build(); // Act diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorProviderTest.cs index fe5ec8b068..cc2e0da6cf 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperDescriptorProviderTest.cs @@ -39,8 +39,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions }; var expectedDescriptor = TagHelperDescriptorBuilder.Create( + ViewComponentTagHelperConventions.Kind, "__Generated__StringParameterViewComponentTagHelper", TestCompilation.AssemblyName) + .TypeName("__Generated__StringParameterViewComponentTagHelper") .DisplayName("StringParameterViewComponentTagHelper") .TagMatchingRule(rule => rule @@ -59,7 +61,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions .PropertyName("bar") .TypeName(typeof(string).FullName) .DisplayName("string StringParameterViewComponentTagHelper.bar")) - .AddMetadata(ViewComponentTypes.ViewComponentNameKey, "StringParameter") + .AddMetadata(ViewComponentTagHelperMetadata.Name, "StringParameter") .Build(); // Act diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperPassTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperPassTest.cs index 1d083bf63c..52f22cb8ff 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperPassTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/ViewComponentTagHelperPassTest.cs @@ -22,12 +22,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions var tagHelpers = new[] { TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly") - .BindAttribute(attribute => - attribute - .Name("Foo") - .TypeName("System.Int32")) - .TagMatchingRule(rule => - rule.RequireTagName("p")) + .BindAttribute(attribute => attribute + .Name("Foo") + .TypeName("System.Int32")) + .TagMatchingRule(rule => rule.RequireTagName("p")) .Build() }; @@ -61,15 +59,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions var tagHelpers = new[] { - TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly") - .BindAttribute(attribute => - attribute - .Name("Foo") - .TypeName("System.Int32") - .PropertyName("Foo")) - .TagMatchingRule(rule => - rule.RequireTagName("tagcloud")) - .AddMetadata(ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey, "TagCloud") + TagHelperDescriptorBuilder.Create(ViewComponentTagHelperConventions.Kind, "TestTagHelper", "TestAssembly") + .BindAttribute(attribute => attribute + .Name("Foo") + .TypeName("System.Int32") + .PropertyName("Foo")) + .TagMatchingRule(rule => rule.RequireTagName("tagcloud")) + .AddMetadata(ViewComponentTagHelperMetadata.Name, "TagCloud") .Build() }; @@ -133,16 +129,14 @@ public class __Generated__TagCloudViewComponentTagHelper : Microsoft.AspNetCore. var tagHelpers = new[] { - TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly") - .BindAttribute(attribute => - attribute - .Name("Foo") - .TypeName("System.Collections.Generic.Dictionary") - .PropertyName("Tags") - .AsDictionary("foo-", "System.Int32")) - .TagMatchingRule(rule => - rule.RequireTagName("tagcloud")) - .AddMetadata(ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey, "TagCloud") + TagHelperDescriptorBuilder.Create(ViewComponentTagHelperConventions.Kind, "TestTagHelper", "TestAssembly") + .BindAttribute(attribute => attribute + .Name("Foo") + .TypeName("System.Collections.Generic.Dictionary") + .PropertyName("Tags") + .AsDictionary("foo-", "System.Int32")) + .TagMatchingRule(rule => rule.RequireTagName("tagcloud")) + .AddMetadata(ViewComponentTagHelperMetadata.Name, "TagCloud") .Build() }; @@ -207,6 +201,7 @@ public class __Generated__TagCloudViewComponentTagHelper : Microsoft.AspNetCore. var tagHelpers = new[] { TagHelperDescriptorBuilder.Create("PTestTagHelper", "TestAssembly") + .TypeName("PTestTagHelper") .BindAttribute(attribute => attribute .Name("Foo") @@ -214,15 +209,13 @@ public class __Generated__TagCloudViewComponentTagHelper : Microsoft.AspNetCore. .TagMatchingRule(rule => rule.RequireTagName("p")) .Build(), - TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly") - .BindAttribute(attribute => - attribute - .Name("Foo") - .TypeName("System.Int32") - .PropertyName("Foo")) - .TagMatchingRule(rule => - rule.RequireTagName("tagcloud")) - .AddMetadata(ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey, "TagCloud") + TagHelperDescriptorBuilder.Create(ViewComponentTagHelperConventions.Kind, "TestTagHelper", "TestAssembly") + .BindAttribute(attribute => attribute + .Name("Foo") + .TypeName("System.Int32") + .PropertyName("Foo")) + .TagMatchingRule(rule => rule.RequireTagName("tagcloud")) + .AddMetadata(ViewComponentTagHelperMetadata.Name, "TagCloud") .Build() }; diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/BoundAttributeDescriptorExtensionsTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/BoundAttributeDescriptorExtensionsTest.cs index 0eba3ee16d..c3e1c18ecf 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/BoundAttributeDescriptorExtensionsTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/BoundAttributeDescriptorExtensionsTest.cs @@ -12,11 +12,17 @@ namespace Microsoft.AspNetCore.Razor.Language { // Arrange var expectedPropertyName = "IntProperty"; - var descriptor = BoundAttributeDescriptorBuilder.Create("TestTagHelper") + + var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test"); + tagHelperBuilder.TypeName("TestTagHelper"); + + var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind); + builder .Name("test") .PropertyName(expectedPropertyName) - .TypeName(typeof(int).FullName) - .Build(); + .TypeName(typeof(int).FullName); + + var descriptor = builder.Build(); // Act var propertyName = descriptor.GetPropertyName(); @@ -29,10 +35,15 @@ namespace Microsoft.AspNetCore.Razor.Language public void GetPropertyName_ReturnsNullIfNoPropertyName() { // Arrange - var descriptor = BoundAttributeDescriptorBuilder.Create("TestTagHelper") + var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test"); + tagHelperBuilder.TypeName("TestTagHelper"); + + var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind); + builder .Name("test") - .TypeName(typeof(int).FullName) - .Build(); + .TypeName(typeof(int).FullName); + + var descriptor = builder.Build(); // Act var propertyName = descriptor.GetPropertyName(); @@ -42,14 +53,19 @@ namespace Microsoft.AspNetCore.Razor.Language } [Fact] - public void IsDefaultKind_ReturnsTrueIfFromDefaultBuilder() + public void IsDefaultKind_ReturnsTrue_IfKindIsDefault() { // Arrange - var descriptor = BoundAttributeDescriptorBuilder.Create("TestTagHelper") + var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test"); + tagHelperBuilder.TypeName("TestTagHelper"); + + var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind); + builder .Name("test") .PropertyName("IntProperty") - .TypeName(typeof(int).FullName) - .Build(); + .TypeName(typeof(int).FullName); + + var descriptor = builder.Build(); // Act var isDefault = descriptor.IsDefaultKind(); @@ -59,10 +75,19 @@ namespace Microsoft.AspNetCore.Razor.Language } [Fact] - public void IsDefaultKind_ReturnsFalseIfFromCustomBuilder() + public void IsDefaultKind_ReturnsFalse_IfKindIsNotDefault() { // Arrange - var descriptor = new CustomBoundAttributeDescriptor(); + var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder("other-kind", "TestTagHelper", "Test"); + tagHelperBuilder.TypeName("TestTagHelper"); + + var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, "other-kind"); + builder + .Name("test") + .PropertyName("IntProperty") + .TypeName(typeof(int).FullName); + + var descriptor = builder.Build(); // Act var isDefault = descriptor.IsDefaultKind(); @@ -70,12 +95,5 @@ namespace Microsoft.AspNetCore.Razor.Language // Assert Assert.False(isDefault); } - - private class CustomBoundAttributeDescriptor : BoundAttributeDescriptor - { - public CustomBoundAttributeDescriptor() : base("custom") - { - } - } } } diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/DesignTimeTagHelperWriterTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/DesignTimeTagHelperWriterTest.cs index 035c77834f..ddea3bcecf 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/DesignTimeTagHelperWriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/DesignTimeTagHelperWriterTest.cs @@ -247,6 +247,7 @@ __InputTagHelper.FooProp[""bound""] = 42; IEnumerable> attributes = null) { var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName); + builder.TypeName(typeName); if (attributes != null) { diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/RuntimeTagHelperWriterTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/RuntimeTagHelperWriterTest.cs index d0da4dcb11..b49fd37571 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/RuntimeTagHelperWriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/RuntimeTagHelperWriterTest.cs @@ -483,6 +483,7 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""foo-bound"", __InputTagHelpe IEnumerable> attributes = null) { var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName); + builder.TypeName(typeName); if (attributes != null) { diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/BoundAttributeDescriptorBuilderTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultBoundAttributeDescriptorBuilderTest.cs similarity index 55% rename from test/Microsoft.AspNetCore.Razor.Language.Test/BoundAttributeDescriptorBuilderTest.cs rename to test/Microsoft.AspNetCore.Razor.Language.Test/DefaultBoundAttributeDescriptorBuilderTest.cs index ff3a3d11bd..52317aa157 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/BoundAttributeDescriptorBuilderTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultBoundAttributeDescriptorBuilderTest.cs @@ -5,17 +5,21 @@ using Xunit; namespace Microsoft.AspNetCore.Razor.Language { - public class BoundAttributeDescriptorBuilderTest + public class DefaultBoundAttributeDescriptorBuilderTest { [Fact] public void DisplayName_SetsDescriptorsDisplayName() { // Arrange var expectedDisplayName = "ExpectedDisplayName"; - var builder = BoundAttributeDescriptorBuilder.Create("TestTagHelper"); + + var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test"); + + var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind); + builder.DisplayName(expectedDisplayName); // Act - var descriptor = builder.DisplayName(expectedDisplayName).Build(); + var descriptor = builder.Build(); // Assert Assert.Equal(expectedDisplayName, descriptor.DisplayName); @@ -25,7 +29,11 @@ namespace Microsoft.AspNetCore.Razor.Language public void DisplayName_DefaultsToPropertyLookingDisplayName() { // Arrange - var builder = BoundAttributeDescriptorBuilder.Create("TestTagHelper") + var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TestTagHelper", "Test"); + tagHelperBuilder.TypeName("TestTagHelper"); + + var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind); + builder .TypeName(typeof(int).FullName) .PropertyName("SomeProperty"); diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorIntermediateNodeLoweringPhaseIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorIntermediateNodeLoweringPhaseIntegrationTest.cs index 6024fa1bdb..accd49fd3c 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorIntermediateNodeLoweringPhaseIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorIntermediateNodeLoweringPhaseIntegrationTest.cs @@ -476,6 +476,7 @@ namespace Microsoft.AspNetCore.Razor.Language IEnumerable> attributes = null) { var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName); + builder.TypeName(typeName); if (attributes != null) { diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorTagHelperBinderPhaseTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorTagHelperBinderPhaseTest.cs index c264bbf92b..78c89df6df 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorTagHelperBinderPhaseTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRazorTagHelperBinderPhaseTest.cs @@ -160,11 +160,11 @@ namespace Microsoft.AspNetCore.Razor.Language { CreateTagHelperDescriptor( tagName: "form", - typeName: null, + typeName: "TestFormTagHelper", assemblyName: "TestAssembly"), CreateTagHelperDescriptor( tagName: "input", - typeName: null, + typeName: "TestInputTagHelper", assemblyName: "TestAssembly"), }); }); @@ -201,7 +201,7 @@ namespace Microsoft.AspNetCore.Razor.Language tagName: "form", typeName: "TestFormTagHelper", assemblyName: "TestAssembly", - ruleBuilders: new Action[] + ruleBuilders: new Action[] { ruleBuilder => ruleBuilder .RequireAttribute(attribute => attribute @@ -254,7 +254,7 @@ namespace Microsoft.AspNetCore.Razor.Language tagName: "form", typeName: "TestFormTagHelper", assemblyName: "TestAssembly", - ruleBuilders: new Action[] + ruleBuilders: new Action[] { ruleBuilder => ruleBuilder .RequireAttribute(attribute => attribute @@ -415,11 +415,11 @@ namespace Microsoft.AspNetCore.Razor.Language { CreateTagHelperDescriptor( tagName: "form", - typeName: null, + typeName: "TestFormTagHelper", assemblyName: "TestAssembly"), CreateTagHelperDescriptor( tagName: "input", - typeName: null, + typeName: "TestInputTagHelper", assemblyName: "TestAssembly"), }); }); @@ -1323,7 +1323,7 @@ namespace Microsoft.AspNetCore.Razor.Language var expectedErrorMessage = string.Format( "Invalid tag helper directive look up text '{0}'. The correct look up text " + - "format is: \"typeName, assemblyName\".", + "format is: \"name, assemblyName\".", directiveText); var expectedError = RazorDiagnostic.Create( @@ -1346,28 +1346,16 @@ namespace Microsoft.AspNetCore.Razor.Language private static TagHelperDescriptor CreatePrefixedValidPlainDescriptor(string prefix) { return Valid_PlainTagHelperDescriptor; - //return new TagHelperDescriptor(Valid_PlainTagHelperDescriptor) - //{ - // Prefix = prefix, - //}; } private static TagHelperDescriptor CreatePrefixedValidInheritedDescriptor(string prefix) { return Valid_InheritedTagHelperDescriptor; - //return new TagHelperDescriptor(Valid_InheritedTagHelperDescriptor) - //{ - // Prefix = prefix, - //}; } private static TagHelperDescriptor CreatePrefixedStringDescriptor(string prefix) { return String_TagHelperDescriptor; - //return new TagHelperDescriptor(String_TagHelperDescriptor) - //{ - // Prefix = prefix, - //}; } private static TagHelperDirectiveDescriptor CreateTagHelperDirectiveDescriptor( @@ -1400,9 +1388,10 @@ namespace Microsoft.AspNetCore.Razor.Language string typeName, string assemblyName, IEnumerable> attributes = null, - IEnumerable> ruleBuilders = null) + IEnumerable> ruleBuilders = null) { var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName); + builder.TypeName(typeName); if (attributes != null) { diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperRequiredAttributeDescriptorBuilderTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRequiredAttributeDescriptorBuilderTest.cs similarity index 74% rename from test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperRequiredAttributeDescriptorBuilderTest.cs rename to test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRequiredAttributeDescriptorBuilderTest.cs index 5b81a9c9a0..f847f6ae3c 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperRequiredAttributeDescriptorBuilderTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/DefaultRequiredAttributeDescriptorBuilderTest.cs @@ -5,18 +5,20 @@ using Xunit; namespace Microsoft.AspNetCore.Razor.Language { - public class TagHelperRequiredAttributeDescriptorBuilderTest + public class DefaultRequiredAttributeDescriptorBuilderTest { [Fact] public void Build_DisplayNameIsName_NameComparisonFullMatch() { // Arrange - var descriptorBuilder = RequiredAttributeDescriptorBuilder.Create() + var builder = new DefaultRequiredAttributeDescriptorBuilder(); + + builder .Name("asp-action") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch); // Act - var descriptor = descriptorBuilder.Build(); + var descriptor = builder.Build(); // Assert Assert.Equal("asp-action", descriptor.DisplayName); @@ -26,12 +28,14 @@ namespace Microsoft.AspNetCore.Razor.Language public void Build_DisplayNameIsNameWithDots_NameComparisonPrefixMatch() { // Arrange - var descriptorBuilder = RequiredAttributeDescriptorBuilder.Create() + var builder = new DefaultRequiredAttributeDescriptorBuilder(); + + builder .Name("asp-route-") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch); // Act - var descriptor = descriptorBuilder.Build(); + var descriptor = builder.Build(); // Assert Assert.Equal("asp-route-...", descriptor.DisplayName); diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Extensions/PreallocatedAttributeTargetExtensionTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Extensions/PreallocatedAttributeTargetExtensionTest.cs index d6e1c28815..3146c34efc 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Extensions/PreallocatedAttributeTargetExtensionTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Extensions/PreallocatedAttributeTargetExtensionTest.cs @@ -135,12 +135,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions Writer = new CSharpCodeWriter() }; - var descriptor = BoundAttributeDescriptorBuilder - .Create("FooTagHelper") + var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "FooTagHelper", "Test"); + tagHelperBuilder.TypeName("FooTagHelper"); + + var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind); + + builder .Name("Foo") .TypeName("System.String") - .PropertyName("FooProp") - .Build(); + .PropertyName("FooProp"); + + var descriptor = builder.Build(); var node = new SetPreallocatedTagHelperPropertyIntermediateNode() { @@ -174,13 +179,18 @@ __tagHelperExecutionContext.AddTagHelperAttribute(_tagHelper1); TagHelperRenderingContext = new TagHelperRenderingContext() }; - var descriptor = BoundAttributeDescriptorBuilder - .Create("FooTagHelper") + var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "FooTagHelper", "Test"); + tagHelperBuilder.TypeName("FooTagHelper"); + + var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind); + + builder .Name("Foo") .TypeName("System.Collections.Generic.Dictionary") .AsDictionary("pre-", "System.String") - .PropertyName("FooProp") - .Build(); + .PropertyName("FooProp"); + + var descriptor = builder.Build(); var node = new SetPreallocatedTagHelperPropertyIntermediateNode() { diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TagHelpersIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TagHelpersIntegrationTest.cs index 9e7b370eee..3016cd87d5 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TagHelpersIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TagHelpersIntegrationTest.cs @@ -104,6 +104,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests IEnumerable> attributes = null) { var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName); + builder.TypeName(typeName); if (attributes != null) { diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TestTagHelperDescriptors.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TestTagHelperDescriptors.cs index 14ae56a1af..47000a154a 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TestTagHelperDescriptors.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/TestTagHelperDescriptors.cs @@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests tagName: "a", typeName: "TestNamespace.ATagHelper", assemblyName: "TestAssembly", - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests tagName: "a", typeName: "TestNamespace.ATagHelperMultipleSelectors", assemblyName: "TestAssembly", - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute @@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute @@ -111,7 +111,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute @@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests tagName: "*", typeName: "TestNamespace.CatchAllTagHelper", assemblyName: "TestAssembly", - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute @@ -135,7 +135,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests tagName: "*", typeName: "TestNamespace.CatchAllTagHelper2", assemblyName: "TestAssembly", - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute @@ -195,7 +195,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests builder => builder .Name("[item]") .PropertyName("ListItems") - .TypeName(typeof(List).FullName), + .TypeName("System.Collections.Generic.List"), builder => builder .Name("[(item)]") .PropertyName("ArrayItems") @@ -217,7 +217,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests .PropertyName("StringProperty2") .TypeName(typeof(string).FullName), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("bound")), }), @@ -242,7 +242,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests .PropertyName("BoundRequiredString") .TypeName(typeof(string).FullName), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("catchall-unbound-required")), }), @@ -261,7 +261,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests .PropertyName("BoundString") .TypeName(typeof(string).FullName), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute.Name("input-bound-required-string")) @@ -309,7 +309,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", typePropertyInfo), builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", checkedPropertyInfo), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("type")), builder => builder.RequireAttribute(attribute => attribute.Name("checked")) @@ -323,7 +323,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", typePropertyInfo), builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", checkedPropertyInfo), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("type")), builder => builder.RequireAttribute(attribute => attribute.Name("checked")) @@ -344,7 +344,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests tagName: "p", typeName: "TestNamespace.PTagHelper", assemblyName: "TestAssembly", - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("class")), }), @@ -356,7 +356,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("type")), }), @@ -369,7 +369,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo), builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", inputCheckedPropertyInfo), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute.Name("type")) @@ -379,7 +379,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests tagName: "*", typeName: "TestNamespace.CatchAllTagHelper", assemblyName: "TestAssembly", - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("catchAll")), }), @@ -480,7 +480,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "age", pAgePropertyInfo), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireTagStructure(TagStructure.NormalOrSelfClosing) }), @@ -492,7 +492,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireTagStructure(TagStructure.WithoutEndTag) }), @@ -514,9 +514,10 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests string typeName, string assemblyName, IEnumerable> attributes = null, - IEnumerable> ruleBuilders = null) + IEnumerable> ruleBuilders = null) { var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName); + builder.TypeName(typeName); if (attributes != null) { @@ -530,7 +531,8 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { foreach (var ruleBuilder in ruleBuilders) { - builder.TagMatchingRule(innerRuleBuilder => { + builder.TagMatchingRule(innerRuleBuilder => + { innerRuleBuilder.RequireTagName(tagName); ruleBuilder(innerRuleBuilder); }); diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs index 3e6886ffae..dea7f61983 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs @@ -1165,7 +1165,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy "InputTagHelper1", "InputTagHelper2", "input", - nameof(TagMatchingRule.TagStructure)), + nameof(TagMatchingRuleDescriptor.TagStructure)), absoluteIndex: 0, lineIndex: 0, columnIndex: 0, diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperBinderTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperBinderTest.cs index 0dc5d70f2e..d755ca1b7c 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperBinderTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperBinderTest.cs @@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Razor.Language Assert.Equal("body", bindingResult.ParentTagName); Assert.Equal(expectedAttributes, bindingResult.Attributes); Assert.Equal("th:", bindingResult.TagHelperPrefix); - Assert.Equal(divTagHelper.TagMatchingRules, bindingResult.GetBoundRules(divTagHelper), TagMatchingRuleComparer.CaseSensitive); + Assert.Equal(divTagHelper.TagMatchingRules, bindingResult.GetBoundRules(divTagHelper), TagMatchingRuleDescriptorComparer.CaseSensitive); } public static TheoryData RequiredParentData diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperDescriptorExtensionsTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperDescriptorExtensionsTest.cs index ac55dff8ac..0940661058 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperDescriptorExtensionsTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperDescriptorExtensionsTest.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Razor.Language { // Arrange var expectedTypeName = "TestTagHelper"; - var descriptor = TagHelperDescriptorBuilder.Create(expectedTypeName, "TestAssembly").Build(); + var descriptor = TagHelperDescriptorBuilder.Create(expectedTypeName, "TestAssembly").TypeName(expectedTypeName).Build(); // Act var typeName = descriptor.GetTypeName(); @@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Razor.Language public void GetTypeName_ReturnsNullIfNoTypeName() { // Arrange - var descriptor = new CustomTagHelperDescriptor(); + var descriptor = TagHelperDescriptorBuilder.Create("Test", "TestAssembly").Build(); // Act var typeName = descriptor.GetTypeName(); @@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Razor.Language } [Fact] - public void IsDefaultKind_ReturnsTrueIfFromDefaultBuilder() + public void IsDefaultKind_ReturnsTrue_IfKindIsDefault() { // Arrange var descriptor = TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly").Build(); @@ -49,10 +49,10 @@ namespace Microsoft.AspNetCore.Razor.Language } [Fact] - public void IsDefaultKind_ReturnsFalseIfFromCustomBuilder() + public void IsDefaultKind_ReturnsFalse_IfKindIsNotDefault() { // Arrange - var descriptor = new CustomTagHelperDescriptor(); + var descriptor = TagHelperDescriptorBuilder.Create("other-kind", "TestTagHelper", "TestAssembly").Build(); // Act var isDefault = descriptor.IsDefaultKind(); @@ -60,13 +60,5 @@ namespace Microsoft.AspNetCore.Razor.Language // Assert Assert.False(isDefault); } - - private class CustomTagHelperDescriptor : TagHelperDescriptor - { - public CustomTagHelperDescriptor() : base("custom") - { - Metadata = new Dictionary(); - } - } } } diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperRequiredAttributeDescriptorTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperMatchingConventionsTest.cs similarity index 69% rename from test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperRequiredAttributeDescriptorTest.cs rename to test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperMatchingConventionsTest.cs index eedb969b1e..7a1fc6c1e3 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperRequiredAttributeDescriptorTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperMatchingConventionsTest.cs @@ -1,138 +1,128 @@ // 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; using Xunit; namespace Microsoft.AspNetCore.Razor.Language { - public class TagHelperRequiredAttributeDescriptorTest + public class TagHelperMatchingConventionsTest { public static TheoryData RequiredAttributeDescriptorData { get { // requiredAttributeDescriptor, attributeName, attributeValue, expectedResult - return new TheoryData + return new TheoryData, string, string, bool> { { - RequiredAttributeDescriptorBuilder.Create().Name("key").Build(), + builder => builder.Name("key"), "KeY", "value", true }, { - RequiredAttributeDescriptorBuilder.Create().Name("key").Build(), + builder => builder.Name("key"), "keys", "value", false }, { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("route-") - .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch) - .Build(), + .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch), "ROUTE-area", "manage", true }, { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("route-") - .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch) - .Build(), + .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch), "routearea", "manage", false }, { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("route-") - .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch) - .Build(), + .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch), "route-", "manage", false }, { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("key") - .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch) - .Build(), + .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch), "KeY", "value", true }, { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("key") - .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch) - .Build(), + .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch), "keys", "value", false }, { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("key") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch) .Value("value") - .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch) - .Build(), + .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch), "key", "value", true }, { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("key") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch) .Value("value") - .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch) - .Build(), + .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch), "key", "Value", false }, { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("class") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch) .Value("btn") - .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch) - .Build(), + .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch), "class", "btn btn-success", true }, { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("class") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch) .Value("btn") - .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch) - .Build(), + .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch), "class", "BTN btn-success", false }, { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("href") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch) .Value("#navigate") - .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch) - .Build(), + .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch), "href", "/home/index#navigate", true }, { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("href") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch) .Value("#navigate") - .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch) - .Build(), + .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch), "href", "/home/index#NAVigate", false @@ -144,13 +134,20 @@ namespace Microsoft.AspNetCore.Razor.Language [Theory] [MemberData(nameof(RequiredAttributeDescriptorData))] public void Matches_ReturnsExpectedResult( - object requiredAttributeDescriptor, + Action configure, string attributeName, string attributeValue, bool expectedResult) { + // Arrange + + var builder = new DefaultRequiredAttributeDescriptorBuilder(); + configure(builder); + + var requiredAttibute = builder.Build(); + // Act - var result = TagHelperMatchingConventions.SatisfiesRequiredAttribute(attributeName, attributeValue, (RequiredAttributeDescriptor)requiredAttributeDescriptor); + var result = TagHelperMatchingConventions.SatisfiesRequiredAttribute(attributeName, attributeValue, requiredAttibute); // Assert Assert.Equal(expectedResult, result); diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/SymbolBoundAttributes_DesignTime.diagnostics.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/SymbolBoundAttributes_DesignTime.diagnostics.txt index 54ed3b15e0..1ee160ff70 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/SymbolBoundAttributes_DesignTime.diagnostics.txt +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/SymbolBoundAttributes_DesignTime.diagnostics.txt @@ -1,5 +1,5 @@ -(0,0): Error RZ3003: Invalid tag helper bound property 'TestNamespace.CatchAllTagHelper.ListItems'. Tag helpers cannot bind to HTML attributes with name '[item]' because the name contains a '[' character. -(0,0): Error RZ3003: Invalid tag helper bound property 'TestNamespace.CatchAllTagHelper.ListItems'. Tag helpers cannot bind to HTML attributes with name '[item]' because the name contains a ']' character. -(0,0): Error RZ3003: Invalid tag helper bound property 'TestNamespace.CatchAllTagHelper.ArrayItems'. Tag helpers cannot bind to HTML attributes with name '[(item)]' because the name contains a '[' character. -(0,0): Error RZ3003: Invalid tag helper bound property 'TestNamespace.CatchAllTagHelper.ArrayItems'. Tag helpers cannot bind to HTML attributes with name '[(item)]' because the name contains a ']' character. -(0,0): Error RZ3003: Invalid tag helper bound property 'TestNamespace.CatchAllTagHelper.StringProperty1'. Tag helpers cannot bind to HTML attributes with name '*something' because the name contains a '*' character. +(0,0): Error RZ3003: Invalid tag helper bound property 'System.Collections.Generic.List TestNamespace.CatchAllTagHelper.ListItems' on tag helper 'TestNamespace.CatchAllTagHelper'. Tag helpers cannot bind to HTML attributes with name '[item]' because the name contains a '[' character. +(0,0): Error RZ3003: Invalid tag helper bound property 'System.Collections.Generic.List TestNamespace.CatchAllTagHelper.ListItems' on tag helper 'TestNamespace.CatchAllTagHelper'. Tag helpers cannot bind to HTML attributes with name '[item]' because the name contains a ']' character. +(0,0): Error RZ3003: Invalid tag helper bound property 'System.String[] TestNamespace.CatchAllTagHelper.ArrayItems' on tag helper 'TestNamespace.CatchAllTagHelper'. Tag helpers cannot bind to HTML attributes with name '[(item)]' because the name contains a '[' character. +(0,0): Error RZ3003: Invalid tag helper bound property 'System.String[] TestNamespace.CatchAllTagHelper.ArrayItems' on tag helper 'TestNamespace.CatchAllTagHelper'. Tag helpers cannot bind to HTML attributes with name '[(item)]' because the name contains a ']' character. +(0,0): Error RZ3003: Invalid tag helper bound property 'string TestNamespace.CatchAllTagHelper.StringProperty1' on tag helper 'TestNamespace.CatchAllTagHelper'. Tag helpers cannot bind to HTML attributes with name '*something' because the name contains a '*' character. diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/SymbolBoundAttributes_Runtime.diagnostics.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/SymbolBoundAttributes_Runtime.diagnostics.txt index 54ed3b15e0..1ee160ff70 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/SymbolBoundAttributes_Runtime.diagnostics.txt +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/SymbolBoundAttributes_Runtime.diagnostics.txt @@ -1,5 +1,5 @@ -(0,0): Error RZ3003: Invalid tag helper bound property 'TestNamespace.CatchAllTagHelper.ListItems'. Tag helpers cannot bind to HTML attributes with name '[item]' because the name contains a '[' character. -(0,0): Error RZ3003: Invalid tag helper bound property 'TestNamespace.CatchAllTagHelper.ListItems'. Tag helpers cannot bind to HTML attributes with name '[item]' because the name contains a ']' character. -(0,0): Error RZ3003: Invalid tag helper bound property 'TestNamespace.CatchAllTagHelper.ArrayItems'. Tag helpers cannot bind to HTML attributes with name '[(item)]' because the name contains a '[' character. -(0,0): Error RZ3003: Invalid tag helper bound property 'TestNamespace.CatchAllTagHelper.ArrayItems'. Tag helpers cannot bind to HTML attributes with name '[(item)]' because the name contains a ']' character. -(0,0): Error RZ3003: Invalid tag helper bound property 'TestNamespace.CatchAllTagHelper.StringProperty1'. Tag helpers cannot bind to HTML attributes with name '*something' because the name contains a '*' character. +(0,0): Error RZ3003: Invalid tag helper bound property 'System.Collections.Generic.List TestNamespace.CatchAllTagHelper.ListItems' on tag helper 'TestNamespace.CatchAllTagHelper'. Tag helpers cannot bind to HTML attributes with name '[item]' because the name contains a '[' character. +(0,0): Error RZ3003: Invalid tag helper bound property 'System.Collections.Generic.List TestNamespace.CatchAllTagHelper.ListItems' on tag helper 'TestNamespace.CatchAllTagHelper'. Tag helpers cannot bind to HTML attributes with name '[item]' because the name contains a ']' character. +(0,0): Error RZ3003: Invalid tag helper bound property 'System.String[] TestNamespace.CatchAllTagHelper.ArrayItems' on tag helper 'TestNamespace.CatchAllTagHelper'. Tag helpers cannot bind to HTML attributes with name '[(item)]' because the name contains a '[' character. +(0,0): Error RZ3003: Invalid tag helper bound property 'System.String[] TestNamespace.CatchAllTagHelper.ArrayItems' on tag helper 'TestNamespace.CatchAllTagHelper'. Tag helpers cannot bind to HTML attributes with name '[(item)]' because the name contains a ']' character. +(0,0): Error RZ3003: Invalid tag helper bound property 'string TestNamespace.CatchAllTagHelper.StringProperty1' on tag helper 'TestNamespace.CatchAllTagHelper'. Tag helpers cannot bind to HTML attributes with name '*something' because the name contains a '*' character. diff --git a/test/Microsoft.CodeAnalysis.Razor.Test/DefaultTagHelperDescriptorFactoryTest.cs b/test/Microsoft.CodeAnalysis.Razor.Test/DefaultTagHelperDescriptorFactoryTest.cs index 3d1ea6ff3a..5c602ed165 100644 --- a/test/Microsoft.CodeAnalysis.Razor.Test/DefaultTagHelperDescriptorFactoryTest.cs +++ b/test/Microsoft.CodeAnalysis.Razor.Test/DefaultTagHelperDescriptorFactoryTest.cs @@ -28,182 +28,165 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { get { - return new TheoryData + return new TheoryData[]> { { "name,", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("name") - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("name,")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("name,")), } }, { " ", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name(string.Empty) - .AddDiagnostic(AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeNameNullOrWhitespace()) - .Build(), + .AddDiagnostic(AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeNameNullOrWhitespace()), } }, { "n@me", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("n@me") - .AddDiagnostic(AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeName("n@me", '@')) - .Build(), + .AddDiagnostic(AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeName("n@me", '@')), } }, { "name extra", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("name") - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeCharacter('e', "name extra")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeCharacter('e', "name extra")), } }, { "[[ ", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("[") - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[[ ")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[[ ")), } }, { "[ ", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("") - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[ ")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[ ")), } }, { "[name='unended]", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("name") .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch) - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeMismatchedQuotes('\'', "[name='unended]")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeMismatchedQuotes('\'', "[name='unended]")), } }, { "[name='unended", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("name") .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch) - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeMismatchedQuotes('\'', "[name='unended")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeMismatchedQuotes('\'', "[name='unended")), } }, { "[name", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("name") - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[name")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[name")), } }, { "[ ]", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name(string.Empty) - .AddDiagnostic(AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeNameNullOrWhitespace()) - .Build(), + .AddDiagnostic(AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeNameNullOrWhitespace()), } }, { "[n@me]", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("n@me") - .AddDiagnostic(AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeName("n@me", '@')) - .Build(), + .AddDiagnostic(AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeName("n@me", '@')), } }, { "[name@]", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("name@") - .AddDiagnostic(AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeName("name@", '@')) - .Build(), + .AddDiagnostic(AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeName("name@", '@')), } }, { "[name^]", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("name") - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_PartialRequiredAttributeOperator('^', "[name^]")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_PartialRequiredAttributeOperator('^', "[name^]")), } }, { "[name='value'", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("name") .Value("value") .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch) - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[name='value'")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[name='value'")), } }, { "[name ", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("name") - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[name ")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[name ")), } }, { "[name extra]", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("name") - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeOperator('e', "[name extra]")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeOperator('e', "[name extra]")), } }, { "[name=value ", - new[] + new Action[] { - RequiredAttributeDescriptorBuilder.Create() + builder => builder .Name("name") .Value("value") .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch) - .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[name=value ")) - .Build(), + .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace("[name=value ")), } }, }; @@ -214,39 +197,47 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces [MemberData(nameof(RequiredAttributeParserErrorData))] public void RequiredAttributeParser_ParsesRequiredAttributesAndLogsDiagnosticsCorrectly( string requiredAttributes, - IEnumerable expectedDescriptors) + IEnumerable> configureBuilders) { // Arrange - var ruleBuilder = TagMatchingRuleBuilder.Create(); + var ruleBuilder = new DefaultTagMatchingRuleDescriptorBuilder(); + + var expectedRules = new List(); + foreach (var configureBuilder in configureBuilders) + { + var builder = new DefaultRequiredAttributeDescriptorBuilder(); + configureBuilder(builder); + + expectedRules.Add(builder.Build()); + } // Act RequiredAttributeParser.AddRequiredAttributes(requiredAttributes, ruleBuilder); // Assert var descriptors = ruleBuilder.Build().Attributes; - Assert.Equal(expectedDescriptors, descriptors, RequiredAttributeDescriptorComparer.CaseSensitive); + Assert.Equal(expectedRules, descriptors, RequiredAttributeDescriptorComparer.CaseSensitive); } public static TheoryData RequiredAttributeParserData { get { - Func plain = - (name, nameComparison) => RequiredAttributeDescriptorBuilder.Create() + Func> plain = + (name, nameComparison) => (builder) => builder .Name(name) - .NameComparisonMode(nameComparison) - .Build(); - Func css = - (name, value, valueComparison) => RequiredAttributeDescriptorBuilder.Create() + .NameComparisonMode(nameComparison); + + Func> css = + (name, value, valueComparison) => (builder) => builder .Name(name) .Value(value) - .ValueComparisonMode(valueComparison) - .Build(); + .ValueComparisonMode(valueComparison); - return new TheoryData> + return new TheoryData>> { - { null, Enumerable.Empty() }, - { string.Empty, Enumerable.Empty() }, + { null, Enumerable.Empty>() }, + { string.Empty, Enumerable.Empty>() }, { "name", new[] { plain("name", RequiredAttributeDescriptor.NameComparisonMode.FullMatch) } }, { "name-*", new[] { plain("name-", RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch) } }, { " name-* ", new[] { plain("name-", RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch) } }, @@ -300,17 +291,26 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces [MemberData(nameof(RequiredAttributeParserData))] public void RequiredAttributeParser_ParsesRequiredAttributesCorrectly( string requiredAttributes, - IEnumerable expectedDescriptors) + IEnumerable> configureBuilders) { // Arrange - var ruleBuilder = TagMatchingRuleBuilder.Create(); + var ruleBuilder = new DefaultTagMatchingRuleDescriptorBuilder(); + + var expectedRules = new List(); + foreach (var configureBuilder in configureBuilders) + { + var builder = new DefaultRequiredAttributeDescriptorBuilder(); + configureBuilder(builder); + + expectedRules.Add(builder.Build()); + } // Act RequiredAttributeParser.AddRequiredAttributes(requiredAttributes, ruleBuilder); // Assert var descriptors = ruleBuilder.Build().Attributes; - Assert.Equal(expectedDescriptors, descriptors, RequiredAttributeDescriptorComparer.CaseSensitive); + Assert.Equal(expectedRules, descriptors, RequiredAttributeDescriptorComparer.CaseSensitive); } public static TheoryData IsEnumData @@ -323,6 +323,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(EnumTagHelper), TagHelperDescriptorBuilder.Create(typeof(EnumTagHelper).FullName, AssemblyName) + .TypeName(typeof(EnumTagHelper).FullName) .TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName("enum")) .BindAttribute(builder => builder @@ -340,6 +341,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(MultiEnumTagHelper), TagHelperDescriptorBuilder.Create(typeof(MultiEnumTagHelper).FullName, AssemblyName) + .TypeName(typeof(MultiEnumTagHelper).FullName) .TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName("p")) .TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName("input")) .BindAttribute(builder => @@ -358,6 +360,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(NestedEnumTagHelper), TagHelperDescriptorBuilder.Create(typeof(NestedEnumTagHelper).FullName, AssemblyName) + .TypeName(typeof(NestedEnumTagHelper).FullName) .TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName("nested-enum")) .BindAttribute(builder => builder @@ -409,12 +412,14 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(RequiredParentTagHelper), TagHelperDescriptorBuilder.Create(typeof(RequiredParentTagHelper).FullName, AssemblyName) + .TypeName(typeof(RequiredParentTagHelper).FullName) .TagMatchingRule(builder => builder.RequireTagName("input").RequireParentTag("div")) .Build() }, { typeof(MultiSpecifiedRequiredParentTagHelper), TagHelperDescriptorBuilder.Create(typeof(MultiSpecifiedRequiredParentTagHelper).FullName, AssemblyName) + .TypeName(typeof(MultiSpecifiedRequiredParentTagHelper).FullName) .TagMatchingRule(builder => builder.RequireTagName("p").RequireParentTag("div")) .TagMatchingRule(builder => builder.RequireTagName("input").RequireParentTag("section")) .Build() @@ -422,6 +427,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(MultiWithUnspecifiedRequiredParentTagHelper), TagHelperDescriptorBuilder.Create(typeof(MultiWithUnspecifiedRequiredParentTagHelper).FullName, AssemblyName) + .TypeName(typeof(MultiWithUnspecifiedRequiredParentTagHelper).FullName) .TagMatchingRule(builder => builder.RequireTagName("p")) .TagMatchingRule(builder => builder.RequireTagName("input").RequireParentTag("div")) .Build() @@ -457,6 +463,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(RestrictChildrenTagHelper), TagHelperDescriptorBuilder.Create(typeof(RestrictChildrenTagHelper).FullName, AssemblyName) + .TypeName(typeof(RestrictChildrenTagHelper).FullName) .TagMatchingRule(builder => builder.RequireTagName("restrict-children")) .AllowChildTag("p") .Build() @@ -464,6 +471,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(DoubleRestrictChildrenTagHelper), TagHelperDescriptorBuilder.Create(typeof(DoubleRestrictChildrenTagHelper).FullName, AssemblyName) + .TypeName(typeof(DoubleRestrictChildrenTagHelper).FullName) .TagMatchingRule(builder => builder.RequireTagName("double-restrict-children")) .AllowChildTag("p") .AllowChildTag("strong") @@ -472,6 +480,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(MultiTargetRestrictChildrenTagHelper), TagHelperDescriptorBuilder.Create(typeof(MultiTargetRestrictChildrenTagHelper).FullName, AssemblyName) + .TypeName(typeof(MultiTargetRestrictChildrenTagHelper).FullName) .TagMatchingRule(builder => builder.RequireTagName("p")) .TagMatchingRule(builder => builder.RequireTagName("div")) .AllowChildTag("p") @@ -510,6 +519,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(TagStructureTagHelper), TagHelperDescriptorBuilder.Create(typeof(TagStructureTagHelper).FullName, AssemblyName) + .TypeName(typeof(TagStructureTagHelper).FullName) .TagMatchingRule(builder => builder .RequireTagName("input") .RequireTagStructure(TagStructure.WithoutEndTag)) @@ -518,6 +528,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(MultiSpecifiedTagStructureTagHelper), TagHelperDescriptorBuilder.Create(typeof(MultiSpecifiedTagStructureTagHelper).FullName, AssemblyName) + .TypeName(typeof(MultiSpecifiedTagStructureTagHelper).FullName) .TagMatchingRule(builder => builder .RequireTagName("p") .RequireTagStructure(TagStructure.NormalOrSelfClosing)) @@ -529,6 +540,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(MultiWithUnspecifiedTagStructureTagHelper), TagHelperDescriptorBuilder.Create(typeof(MultiWithUnspecifiedTagStructureTagHelper).FullName, AssemblyName) + .TypeName(typeof(MultiWithUnspecifiedTagStructureTagHelper).FullName) .TagMatchingRule(builder => builder .RequireTagName("p")) .TagMatchingRule(builder => builder @@ -747,7 +759,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces TagHelperMatchingConventions.ElementCatchAllName, typeof(AttributeTargetingTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("class")), }) @@ -758,7 +770,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces TagHelperMatchingConventions.ElementCatchAllName, typeof(MultiAttributeTargetingTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => { @@ -774,7 +786,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces TagHelperMatchingConventions.ElementCatchAllName, typeof(MultiAttributeAttributeTargetingTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("custom")), builder => @@ -791,7 +803,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces TagHelperMatchingConventions.ElementCatchAllName, typeof(InheritedAttributeTargetingTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("style")), }) @@ -802,7 +814,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces "input", typeof(RequiredAttributeTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("class")), }) @@ -813,7 +825,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces "div", typeof(InheritedRequiredAttributeTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("class")), }) @@ -824,7 +836,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces "div", typeof(MultiAttributeRequiredAttributeTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireTagName("div") @@ -840,7 +852,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces "input", typeof(MultiAttributeSameTagRequiredAttributeTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder.RequireAttribute(attribute => attribute.Name("style")), builder => builder.RequireAttribute(attribute => attribute.Name("class")), @@ -852,7 +864,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces "input", typeof(MultiRequiredAttributeTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute.Name("class")) @@ -865,7 +877,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces "div", typeof(MultiTagMultiRequiredAttributeTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireTagName("div") @@ -883,7 +895,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces TagHelperMatchingConventions.ElementCatchAllName, typeof(AttributeWildcardTargetingTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute @@ -897,7 +909,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces TagHelperMatchingConventions.ElementCatchAllName, typeof(MultiAttributeWildcardTargetingTagHelper).FullName, AssemblyName, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute @@ -1038,15 +1050,15 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces } [Fact] - public void CreateDescriptor_AllowsOverridenAttributeNameOnUnimplementedVirtual() + public void CreateDescriptor_AllowsOverriddenAttributeNameOnUnimplementedVirtual() { // Arrange var validProperty1 = typeof(InheritedNotOverriddenAttributeTagHelper).GetProperty( nameof(InheritedNotOverriddenAttributeTagHelper.ValidAttribute1)); var validProperty2 = typeof(InheritedNotOverriddenAttributeTagHelper).GetProperty( nameof(InheritedNotOverriddenAttributeTagHelper.ValidAttribute2)); - var expectedDescriptor = - CreateTagHelperDescriptor( + + var expectedDescriptor = CreateTagHelperDescriptor( "inherited-not-overridden-attribute", typeof(InheritedNotOverriddenAttributeTagHelper).FullName, AssemblyName, @@ -1217,7 +1229,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces .PropertyName(nameof(MultiTagTagHelper.ValidAttribute)) .TypeName(typeof(string).FullName), }, - new Action[] + new Action[] { builder => builder.RequireTagName("p"), builder => builder.RequireTagName("div"), @@ -1262,16 +1274,16 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces public void CreateDescriptor_IgnoresDuplicateTagNamesFromAttribute() { // Arrange - var expectedDescriptor = - CreateTagHelperDescriptor( - string.Empty, - typeof(DuplicateTagNameTagHelper).FullName, - AssemblyName, - ruleBuilders: new Action[] - { - builder => builder.RequireTagName("p"), - builder => builder.RequireTagName("div"), - }); + var expectedDescriptor = CreateTagHelperDescriptor( + string.Empty, + typeof(DuplicateTagNameTagHelper).FullName, + AssemblyName, + ruleBuilders: new Action[] + { + builder => builder.RequireTagName("p"), + builder => builder.RequireTagName("div"), + }); + var factory = new DefaultTagHelperDescriptorFactory(Compilation, designTime: false); var typeSymbol = Compilation.GetTypeByMetadataName(typeof(DuplicateTagNameTagHelper).FullName); @@ -1376,6 +1388,9 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { get { + var invalidBoundAttributeBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, nameof(InvalidBoundAttribute), "Test"); + invalidBoundAttributeBuilder.TypeName(typeof(InvalidBoundAttribute).FullName); + // type, expectedAttributeDescriptors return new TheoryData> { @@ -1383,80 +1398,72 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces typeof(InvalidBoundAttribute), new[] { - BoundAttributeDescriptorBuilder.Create(typeof(InvalidBoundAttribute).FullName) + CreateAttributeFor(typeof(InvalidBoundAttribute), attribute => + { + attribute .Name("data-something") .PropertyName(nameof(InvalidBoundAttribute.DataSomething)) - .TypeName(typeof(string).FullName) - .AddDiagnostic( - AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNameStartsWith( - typeof(InvalidBoundAttribute).FullName, - nameof(InvalidBoundAttribute.DataSomething), - "data-something")) - .Build() + .TypeName(typeof(string).FullName); + }), } }, { typeof(InvalidBoundAttributeWithValid), new[] { - BoundAttributeDescriptorBuilder.Create(typeof(InvalidBoundAttributeWithValid).FullName) + CreateAttributeFor(typeof(InvalidBoundAttributeWithValid), attribute => + { + attribute .Name("data-something") .PropertyName(nameof(InvalidBoundAttributeWithValid.DataSomething)) - .TypeName(typeof(string).FullName) - .AddDiagnostic( - AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNameStartsWith( - typeof(InvalidBoundAttributeWithValid).FullName, - nameof(InvalidBoundAttributeWithValid.DataSomething), - "data-something")) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(InvalidBoundAttributeWithValid).FullName) + .TypeName(typeof(string).FullName); ; + }), + CreateAttributeFor(typeof(InvalidBoundAttributeWithValid), attribute => + { + attribute .Name("int-attribute") .PropertyName(nameof(InvalidBoundAttributeWithValid.IntAttribute)) - .TypeName(typeof(int).FullName) - .Build(), + .TypeName(typeof(int).FullName); + }), } }, { typeof(OverriddenInvalidBoundAttributeWithValid), new[] { - BoundAttributeDescriptorBuilder.Create(typeof(OverriddenInvalidBoundAttributeWithValid).FullName) + CreateAttributeFor(typeof(OverriddenInvalidBoundAttributeWithValid), attribute => + { + attribute .Name("valid-something") .PropertyName(nameof(OverriddenInvalidBoundAttributeWithValid.DataSomething)) - .TypeName(typeof(string).FullName) - .Build() + .TypeName(typeof(string).FullName); + }), } }, { typeof(OverriddenValidBoundAttributeWithInvalid), new[] { - BoundAttributeDescriptorBuilder.Create(typeof(OverriddenValidBoundAttributeWithInvalid).FullName) + CreateAttributeFor(typeof(OverriddenValidBoundAttributeWithInvalid), attribute => + { + attribute .Name("data-something") .PropertyName(nameof(OverriddenValidBoundAttributeWithInvalid.ValidSomething)) - .TypeName(typeof(string).FullName) - .AddDiagnostic( - AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNameStartsWith( - typeof(OverriddenValidBoundAttributeWithInvalid).FullName, - nameof(OverriddenValidBoundAttributeWithInvalid.ValidSomething), - "data-something")) - .Build() + .TypeName(typeof(string).FullName); + }), } }, { typeof(OverriddenValidBoundAttributeWithInvalidUpperCase), new[] { - BoundAttributeDescriptorBuilder.Create(typeof(OverriddenValidBoundAttributeWithInvalidUpperCase).FullName) + CreateAttributeFor(typeof(OverriddenValidBoundAttributeWithInvalidUpperCase), attribute => + { + attribute .Name("DATA-SOMETHING") .PropertyName(nameof(OverriddenValidBoundAttributeWithInvalidUpperCase.ValidSomething)) - .TypeName(typeof(string).FullName) - .AddDiagnostic( - AspNetCore.Razor.Language.RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNameStartsWith( - typeof(OverriddenValidBoundAttributeWithInvalidUpperCase).FullName, - nameof(OverriddenValidBoundAttributeWithInvalidUpperCase.ValidSomething), - "DATA-SOMETHING")) - .Build() + .TypeName(typeof(string).FullName); + }), } }, }; @@ -1481,6 +1488,13 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces expectedAttributeDescriptors, descriptor.BoundAttributes, BoundAttributeDescriptorComparer.Default); + + var id = AspNetCore.Razor.Language.RazorDiagnosticFactory.TagHelper_InvalidBoundAttributeNameStartsWith.Id; + foreach (var attribute in descriptor.BoundAttributes.Where(a => a.Name.StartsWith("data-", StringComparison.OrdinalIgnoreCase))) + { + var diagnostic = Assert.Single(attribute.Diagnostics); + Assert.Equal(id, diagnostic.Id); + } } public static TheoryData ValidAttributeNameData @@ -1568,13 +1582,13 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces get { Func onNameError = (invalidText, invalidCharacter) => - "Invalid tag helper bound property 'DynamicTestTagHelper.InvalidProperty'. Tag helpers " + + "Invalid tag helper bound property 'string DynamicTestTagHelper.InvalidProperty' on tag helper 'DynamicTestTagHelper'. Tag helpers " + $"cannot bind to HTML attributes with name '{invalidText}' because the name contains a '{invalidCharacter}' character."; var whitespaceErrorString = - "Invalid tag helper bound property 'DynamicTestTagHelper.InvalidProperty'. Tag helpers cannot " + + "Invalid tag helper bound property 'string DynamicTestTagHelper.InvalidProperty' on tag helper 'DynamicTestTagHelper'. Tag helpers cannot " + "bind to HTML attributes with a null or empty name."; Func onDataError = invalidText => - "Invalid tag helper bound property 'DynamicTestTagHelper.InvalidProperty'. Tag helpers cannot bind "+ + "Invalid tag helper bound property 'string DynamicTestTagHelper.InvalidProperty' on tag helper 'DynamicTestTagHelper'. Tag helpers cannot bind " + $"to HTML attributes with name '{invalidText}' because the name starts with 'data-'."; return GetInvalidNameOrPrefixData(onNameError, whitespaceErrorString, onDataError); @@ -1612,12 +1626,15 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces get { Func onPrefixError = (invalidText, invalidCharacter) => - "Invalid tag helper bound property 'DynamicTestTagHelper.InvalidProperty'. Tag helpers "+ + "Invalid tag helper bound property 'System.Collections.Generic.IDictionary DynamicTestTagHelper.InvalidProperty' " + + "on tag helper 'DynamicTestTagHelper'. Tag helpers " + $"cannot bind to HTML attributes with prefix '{invalidText}' because the prefix contains a '{invalidCharacter}' character."; var whitespaceErrorString = - "Invalid tag helper bound property 'DynamicTestTagHelper.InvalidProperty'. Tag helpers cannot bind to HTML attributes with a null or empty name."; + "Invalid tag helper bound property 'System.Collections.Generic.IDictionary DynamicTestTagHelper.InvalidProperty' " + + "on tag helper 'DynamicTestTagHelper'. Tag helpers cannot bind to HTML attributes with a null or empty name."; Func onDataError = invalidText => - "Invalid tag helper bound property 'DynamicTestTagHelper.InvalidProperty'. Tag helpers cannot bind to HTML attributes "+ + "Invalid tag helper bound property 'System.Collections.Generic.IDictionary DynamicTestTagHelper.InvalidProperty' " + + "on tag helper 'DynamicTestTagHelper'. Tag helpers cannot bind to HTML attributes " + $"with prefix '{invalidText}' because the prefix starts with 'data-'."; return GetInvalidNameOrPrefixData(onPrefixError, whitespaceErrorString, onDataError); @@ -1634,7 +1651,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces public class DynamicTestTagHelper : {typeof(AspNetCore.Razor.TagHelpers.TagHelper).FullName} {{ [{typeof(AspNetCore.Razor.TagHelpers.HtmlAttributeNameAttribute).FullName}({nameof(AspNetCore.Razor.TagHelpers.HtmlAttributeNameAttribute.DictionaryAttributePrefix)} = ""{prefix}"")] - public System.Collections.Generic.IDictionary InvalidProperty {{ get; set; }} + public System.Collections.Generic.IDictionary InvalidProperty {{ get; set; }} }}"; var syntaxTree = CSharpSyntaxTree.ParseText(text); var compilation = TestCompilation.Create(_assembly, syntaxTree); @@ -1654,13 +1671,13 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces get { var nullOrWhiteSpaceError = - AspNetCore.Razor.Language.Resources.FormatInvalidRestrictedChildNullOrWhitespace("DynamicTestTagHelper"); + AspNetCore.Razor.Language.Resources.FormatTagHelper_InvalidRestrictedChildNullOrWhitespace("DynamicTestTagHelper"); return GetInvalidNameOrPrefixData( onNameError: (invalidInput, invalidCharacter) => - AspNetCore.Razor.Language.Resources.FormatInvalidRestrictedChild( - invalidInput, + AspNetCore.Razor.Language.Resources.FormatTagHelper_InvalidRestrictedChild( "DynamicTestTagHelper", + invalidInput, invalidCharacter), whitespaceErrorString: nullOrWhiteSpaceError, onDataError: null); @@ -1696,11 +1713,11 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces get { var nullOrWhiteSpaceError = - AspNetCore.Razor.Language.Resources.InvalidTargetedParentTagNameNullOrWhitespace; + AspNetCore.Razor.Language.Resources.TagHelper_InvalidTargetedParentTagNameNullOrWhitespace; return GetInvalidNameOrPrefixData( onNameError: (invalidInput, invalidCharacter) => - AspNetCore.Razor.Language.Resources.FormatInvalidTargetedParentTagName( + AspNetCore.Razor.Language.Resources.FormatTagHelper_InvalidTargetedParentTagName( invalidInput, invalidCharacter), whitespaceErrorString: nullOrWhiteSpaceError, @@ -1763,12 +1780,14 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces typeof(DefaultValidHtmlAttributePrefix), new[] { - BoundAttributeDescriptorBuilder.Create(typeof(DefaultValidHtmlAttributePrefix).FullName) + CreateAttributeFor(typeof(DefaultValidHtmlAttributePrefix), attribute => + { + attribute .Name("dictionary-property") .PropertyName(nameof(DefaultValidHtmlAttributePrefix.DictionaryProperty)) .TypeName($"{dictionaryNamespace}") - .AsDictionary("dictionary-property-", typeof(string).FullName) - .Build() + .AsDictionary("dictionary-property-", typeof(string).FullName); + }), }, Enumerable.Empty() }, @@ -1776,12 +1795,14 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces typeof(SingleValidHtmlAttributePrefix), new[] { - BoundAttributeDescriptorBuilder.Create(typeof(SingleValidHtmlAttributePrefix).FullName) + CreateAttributeFor(typeof(SingleValidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name") .PropertyName(nameof(SingleValidHtmlAttributePrefix.DictionaryProperty)) .TypeName($"{dictionaryNamespace}") - .AsDictionary("valid-name-", typeof(string).FullName) - .Build() + .AsDictionary("valid-name-", typeof(string).FullName); + }), }, Enumerable.Empty() }, @@ -1789,51 +1810,67 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces typeof(MultipleValidHtmlAttributePrefix), new[] { - BoundAttributeDescriptorBuilder.Create(typeof(MultipleValidHtmlAttributePrefix).FullName) + CreateAttributeFor(typeof(MultipleValidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name1") .PropertyName(nameof(MultipleValidHtmlAttributePrefix.DictionaryProperty)) .TypeName($"{typeof(Dictionary<,>).Namespace}.Dictionary") - .AsDictionary("valid-prefix1-", typeof(object).FullName) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleValidHtmlAttributePrefix).FullName) + .AsDictionary("valid-prefix1-", typeof(object).FullName); + }), + CreateAttributeFor(typeof(MultipleValidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name2") .PropertyName(nameof(MultipleValidHtmlAttributePrefix.DictionarySubclassProperty)) .TypeName(typeof(DictionarySubclass).FullName) - .AsDictionary("valid-prefix2-", typeof(string).FullName) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleValidHtmlAttributePrefix).FullName) + .AsDictionary("valid-prefix2-", typeof(string).FullName); + }), + CreateAttributeFor(typeof(MultipleValidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name3") .PropertyName(nameof(MultipleValidHtmlAttributePrefix.DictionaryWithoutParameterlessConstructorProperty)) .TypeName(typeof(DictionaryWithoutParameterlessConstructor).FullName) - .AsDictionary("valid-prefix3-", typeof(string).FullName) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleValidHtmlAttributePrefix).FullName) + .AsDictionary("valid-prefix3-", typeof(string).FullName); + }), + CreateAttributeFor(typeof(MultipleValidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name4") .PropertyName(nameof(MultipleValidHtmlAttributePrefix.GenericDictionarySubclassProperty)) .TypeName(typeof(GenericDictionarySubclass).Namespace + ".GenericDictionarySubclass") - .AsDictionary("valid-prefix4-", typeof(object).FullName) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleValidHtmlAttributePrefix).FullName) + .AsDictionary("valid-prefix4-", typeof(object).FullName); + }), + CreateAttributeFor(typeof(MultipleValidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name5") .PropertyName(nameof(MultipleValidHtmlAttributePrefix.SortedDictionaryProperty)) .TypeName(typeof(SortedDictionary).Namespace + ".SortedDictionary") - .AsDictionary("valid-prefix5-", typeof(int).FullName) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleValidHtmlAttributePrefix).FullName) + .AsDictionary("valid-prefix5-", typeof(int).FullName); + }), + CreateAttributeFor(typeof(MultipleValidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name6") .PropertyName(nameof(MultipleValidHtmlAttributePrefix.StringProperty)) - .TypeName(typeof(string).FullName) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleValidHtmlAttributePrefix).FullName) + .TypeName(typeof(string).FullName); + }), + CreateAttributeFor(typeof(MultipleValidHtmlAttributePrefix), attribute => + { + attribute .PropertyName(nameof(MultipleValidHtmlAttributePrefix.GetOnlyDictionaryProperty)) .TypeName($"{dictionaryNamespace}") - .AsDictionary("get-only-dictionary-property-", typeof(int).FullName) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleValidHtmlAttributePrefix).FullName) + .AsDictionary("get-only-dictionary-property-", typeof(int).FullName); + }), + CreateAttributeFor(typeof(MultipleValidHtmlAttributePrefix), attribute => + { + attribute .PropertyName(nameof(MultipleValidHtmlAttributePrefix.GetOnlyDictionaryPropertyWithAttributePrefix)) .TypeName($"{dictionaryNamespace}") - .AsDictionary("valid-prefix6", typeof(string).FullName) - .Build() + .AsDictionary("valid-prefix6", typeof(string).FullName); + }), }, Enumerable.Empty() }, @@ -1841,14 +1878,16 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces typeof(SingleInvalidHtmlAttributePrefix), new[] { - BoundAttributeDescriptorBuilder.Create(typeof(SingleInvalidHtmlAttributePrefix).FullName) + CreateAttributeFor(typeof(SingleInvalidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name") .PropertyName(nameof(SingleInvalidHtmlAttributePrefix.StringProperty)) .TypeName(typeof(string).FullName) .AddDiagnostic(RazorDiagnosticFactory.CreateTagHelper_InvalidAttributePrefixNotNull( typeof(SingleInvalidHtmlAttributePrefix).FullName, - nameof(SingleInvalidHtmlAttributePrefix.StringProperty))) - .Build(), + nameof(SingleInvalidHtmlAttributePrefix.StringProperty))); + }), }, new[] { @@ -1861,12 +1900,16 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces typeof(MultipleInvalidHtmlAttributePrefix), new[] { - BoundAttributeDescriptorBuilder.Create(typeof(MultipleInvalidHtmlAttributePrefix).FullName) + CreateAttributeFor(typeof(MultipleInvalidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name1") .PropertyName(nameof(MultipleInvalidHtmlAttributePrefix.LongProperty)) - .TypeName(typeof(long).FullName) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleInvalidHtmlAttributePrefix).FullName) + .TypeName(typeof(long).FullName); + }), + CreateAttributeFor(typeof(MultipleInvalidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name2") .PropertyName(nameof(MultipleInvalidHtmlAttributePrefix.DictionaryOfIntProperty)) .TypeName($"{typeof(Dictionary<,>).Namespace}.Dictionary") @@ -1874,27 +1917,33 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces .AddDiagnostic( RazorDiagnosticFactory.CreateTagHelper_InvalidAttributePrefixNotNull( typeof(MultipleInvalidHtmlAttributePrefix).FullName, - nameof(MultipleInvalidHtmlAttributePrefix.DictionaryOfIntProperty))) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleInvalidHtmlAttributePrefix).FullName) + nameof(MultipleInvalidHtmlAttributePrefix.DictionaryOfIntProperty))); + }), + CreateAttributeFor(typeof(MultipleInvalidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name3") .PropertyName(nameof(MultipleInvalidHtmlAttributePrefix.ReadOnlyDictionaryProperty)) .TypeName($"{typeof(IReadOnlyDictionary<,>).Namespace}.IReadOnlyDictionary") .AddDiagnostic( RazorDiagnosticFactory.CreateTagHelper_InvalidAttributePrefixNotNull( typeof(MultipleInvalidHtmlAttributePrefix).FullName, - nameof(MultipleInvalidHtmlAttributePrefix.ReadOnlyDictionaryProperty))) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleInvalidHtmlAttributePrefix).FullName) + nameof(MultipleInvalidHtmlAttributePrefix.ReadOnlyDictionaryProperty))); + }), + CreateAttributeFor(typeof(MultipleInvalidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name4") .PropertyName(nameof(MultipleInvalidHtmlAttributePrefix.IntProperty)) .TypeName(typeof(int).FullName) .AddDiagnostic( RazorDiagnosticFactory.CreateTagHelper_InvalidAttributePrefixNotNull( typeof(MultipleInvalidHtmlAttributePrefix).FullName, - nameof(MultipleInvalidHtmlAttributePrefix.IntProperty))) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleInvalidHtmlAttributePrefix).FullName) + nameof(MultipleInvalidHtmlAttributePrefix.IntProperty))); + }), + CreateAttributeFor(typeof(MultipleInvalidHtmlAttributePrefix), attribute => + { + attribute .Name("valid-name5") .PropertyName(nameof(MultipleInvalidHtmlAttributePrefix.DictionaryOfIntSubclassProperty)) .TypeName(typeof(DictionaryOfIntSubclass).FullName) @@ -1902,26 +1951,30 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces .AddDiagnostic( RazorDiagnosticFactory.CreateTagHelper_InvalidAttributePrefixNotNull( typeof(MultipleInvalidHtmlAttributePrefix).FullName, - nameof(MultipleInvalidHtmlAttributePrefix.DictionaryOfIntSubclassProperty))) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleInvalidHtmlAttributePrefix).FullName) + nameof(MultipleInvalidHtmlAttributePrefix.DictionaryOfIntSubclassProperty))); + }), + CreateAttributeFor(typeof(MultipleInvalidHtmlAttributePrefix), attribute => + { + attribute .PropertyName(nameof(MultipleInvalidHtmlAttributePrefix.GetOnlyDictionaryAttributePrefix)) .TypeName($"{dictionaryNamespace}") .AsDictionary("valid-prefix6", typeof(string).FullName) .AddDiagnostic( RazorDiagnosticFactory.CreateTagHelper_InvalidAttributePrefixNotNull( typeof(MultipleInvalidHtmlAttributePrefix).FullName, - nameof(MultipleInvalidHtmlAttributePrefix.GetOnlyDictionaryAttributePrefix))) - .Build(), - BoundAttributeDescriptorBuilder.Create(typeof(MultipleInvalidHtmlAttributePrefix).FullName) + nameof(MultipleInvalidHtmlAttributePrefix.GetOnlyDictionaryAttributePrefix))); + }), + CreateAttributeFor(typeof(MultipleInvalidHtmlAttributePrefix), attribute => + { + attribute .PropertyName(nameof(MultipleInvalidHtmlAttributePrefix.GetOnlyDictionaryPropertyWithAttributeName)) .TypeName($"{dictionaryNamespace}") .AsDictionary("invalid-name7-", typeof(object).FullName) .AddDiagnostic( RazorDiagnosticFactory.CreateTagHelper_InvalidAttributePrefixNull( typeof(MultipleInvalidHtmlAttributePrefix).FullName, - nameof(MultipleInvalidHtmlAttributePrefix.GetOnlyDictionaryPropertyWithAttributeName))) - .Build(), + nameof(MultipleInvalidHtmlAttributePrefix.GetOnlyDictionaryPropertyWithAttributeName))); + }), }, new[] { @@ -1981,6 +2034,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(MultipleDescriptorTagHelperWithOutputElementHint), TagHelperDescriptorBuilder.Create(typeof(MultipleDescriptorTagHelperWithOutputElementHint).FullName, AssemblyName) + .TypeName(typeof(MultipleDescriptorTagHelperWithOutputElementHint).FullName) .TagMatchingRule(builder => builder.RequireTagName("a")) .TagMatchingRule(builder => builder.RequireTagName("p")) .TagOutputHint("div") @@ -1989,12 +2043,14 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(InheritedOutputElementHintTagHelper), TagHelperDescriptorBuilder.Create(typeof(InheritedOutputElementHintTagHelper).FullName, AssemblyName) + .TypeName(typeof(InheritedOutputElementHintTagHelper).FullName) .TagMatchingRule(builder => builder.RequireTagName("inherited-output-element-hint")) .Build() }, { typeof(OutputElementHintTagHelper), TagHelperDescriptorBuilder.Create(typeof(OutputElementHintTagHelper).FullName, AssemblyName) + .TypeName(typeof(OutputElementHintTagHelper).FullName) .TagMatchingRule(builder => builder.RequireTagName("output-element-hint")) .TagOutputHint("hinted-value") .Build() @@ -2002,6 +2058,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces { typeof(OverriddenOutputElementHintTagHelper), TagHelperDescriptorBuilder.Create(typeof(OverriddenOutputElementHintTagHelper).FullName, AssemblyName) + .TypeName(typeof(OverriddenOutputElementHintTagHelper).FullName) .TagMatchingRule(builder => builder.RequireTagName("overridden-output-element-hint")) .TagOutputHint("overridden") .Build() @@ -2264,9 +2321,10 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces string typeName, string assemblyName, IEnumerable> attributes = null, - IEnumerable> ruleBuilders = null) + IEnumerable> ruleBuilders = null) { var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName); + builder.TypeName(typeName); if (attributes != null) { @@ -2296,6 +2354,16 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces return descriptor; } + + private static BoundAttributeDescriptor CreateAttributeFor(Type tagHelperType, Action configure) + { + var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, tagHelperType.Name, "Test"); + tagHelperBuilder.TypeName(tagHelperType.FullName); + + var attributeBuilder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind); + configure(attributeBuilder); + return attributeBuilder.Build(); + } } [AspNetCore.Razor.TagHelpers.OutputElementHint("hinted-value")] diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultTagHelperFactsServiceTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultTagHelperFactsServiceTest.cs index acd5a54925..6f9afac1df 100644 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultTagHelperFactsServiceTest.cs +++ b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultTagHelperFactsServiceTest.cs @@ -79,7 +79,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor var descriptor = Assert.Single(binding.Descriptors); Assert.Equal(documentDescriptors[0], descriptor, TagHelperDescriptorComparer.CaseSensitive); var boundRule = Assert.Single(binding.GetBoundRules(descriptor)); - Assert.Equal(documentDescriptors[0].TagMatchingRules.First(), boundRule, TagMatchingRuleComparer.CaseSensitive); + Assert.Equal(documentDescriptors[0].TagMatchingRules.First(), boundRule, TagMatchingRuleDescriptorComparer.CaseSensitive); } [Fact] diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TagHelperDescriptorSerializationTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TagHelperDescriptorSerializationTest.cs index ffd16b23ac..169f69fd58 100644 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TagHelperDescriptorSerializationTest.cs +++ b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TagHelperDescriptorSerializationTest.cs @@ -27,7 +27,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor .PropertyName("TestAttribute") .TypeName("string"), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute @@ -70,7 +70,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor .PropertyName("TestAttribute") .TypeName("string"), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute @@ -122,7 +122,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor .TypeName("SomeDictionary") .AsDictionary("dict-prefix-", "string"), }, - ruleBuilders: new Action[] + ruleBuilders: new Action[] { builder => builder .RequireAttribute(attribute => attribute @@ -150,10 +150,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor string typeName, string assemblyName, IEnumerable> attributes = null, - IEnumerable> ruleBuilders = null, + IEnumerable> ruleBuilders = null, Action configureAction = null) { var builder = TagHelperDescriptorBuilder.Create(typeName, assemblyName); + builder.TypeName(typeName); if (attributes != null) {