diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorComparer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorComparer.cs
index abac00462c..5c4e4ca403 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorComparer.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorComparer.cs
@@ -19,6 +19,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
///
public static readonly TagHelperDescriptorComparer Default = new TagHelperDescriptorComparer();
+ ///
+ /// An instance of that only compares
+ /// .
+ ///
+ public static readonly TagHelperDescriptorComparer TypeName = new TypeNameTagHelperDescriptorComparer();
+
///
/// Initializes a new instance.
///
@@ -101,5 +107,34 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
return hashCodeCombiner.CombinedHash;
}
+
+ private class TypeNameTagHelperDescriptorComparer : TagHelperDescriptorComparer
+ {
+ public override bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
+ {
+ if (object.ReferenceEquals(descriptorX, descriptorY))
+ {
+ return true;
+ }
+ else if (descriptorX == null ^ descriptorY == null)
+ {
+ return false;
+ }
+ else
+ {
+ return string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal);
+ }
+ }
+
+ public override int GetHashCode(TagHelperDescriptor descriptor)
+ {
+ if (descriptor == null)
+ {
+ throw new ArgumentNullException(nameof(descriptor));
+ }
+
+ return descriptor.TypeName == null ? 0 : StringComparer.Ordinal.GetHashCode(descriptor.TypeName);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorProvider.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorProvider.cs
index 3d7356f81c..744e400fb1 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorProvider.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorProvider.cs
@@ -86,7 +86,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
}
}
- return applicableDescriptors;
+ return applicableDescriptors.Distinct(TagHelperDescriptorComparer.TypeName);
}
private bool HasRequiredParentTag(
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperDescriptorProviderTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperDescriptorProviderTest.cs
index a8ebd5f399..bd0ac4c64c 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperDescriptorProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperDescriptorProviderTest.cs
@@ -137,7 +137,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var catchAllDescriptor2 = new TagHelperDescriptor
{
TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
- TypeName = "CatchAllTagHelper",
+ TypeName = "CatchAllTagHelper2",
AssemblyName = "SomeAssembly",
RequiredAttributes = new[]
{
@@ -268,7 +268,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var provider = new TagHelperDescriptorProvider((IEnumerable)availableDescriptors);
// Act
- var resolvedDescriptors = provider.GetDescriptors(tagName, providedAttributes, parentTagName: "p");
+ var resolvedDescriptors = provider.GetDescriptors(tagName, providedAttributes, parentTagName: "p").ToArray();
// Assert
Assert.Equal((IEnumerable)expectedDescriptors, resolvedDescriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
@@ -295,6 +295,57 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
Assert.Empty(resolvedDescriptors);
}
+ [Fact]
+ public void GetDescriptors_DeduplicatesTagHelpersByTypeName()
+ {
+ // Arrange
+ var descriptors = new[]
+ {
+ new TagHelperDescriptor
+ {
+ AssemblyName = "TestAssembly",
+ TagName = "form",
+ TypeName = "TestFormTagHelper",
+ RequiredAttributes = new List()
+ {
+ new TagHelperRequiredAttributeDescriptor()
+ {
+ Name = "a",
+ NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
+ }
+ },
+ },
+ new TagHelperDescriptor
+ {
+ AssemblyName = "TestAssembly",
+ TagName = "form",
+ TypeName = "TestFormTagHelper",
+ RequiredAttributes = new List()
+ {
+ new TagHelperRequiredAttributeDescriptor()
+ {
+ Name = "b",
+ NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
+ }
+ },
+ },
+ };
+ var provider = new TagHelperDescriptorProvider(descriptors);
+
+ // Act
+ var resolvedDescriptors = provider.GetDescriptors(
+ tagName: "form",
+ attributes: new List>()
+ {
+ new KeyValuePair("a", "hi" ),
+ new KeyValuePair("b", "there"),
+ },
+ parentTagName: "p");
+
+ // Assert
+ Assert.Same(descriptors[0], Assert.Single(resolvedDescriptors));
+ }
+
[Fact]
public void GetDescriptors_OnlyUnderstandsSinglePrefix()
{
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TagHelperBinderSyntaxTreePassTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TagHelperBinderSyntaxTreePassTest.cs
index 6d4590cd48..885429c3f2 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TagHelperBinderSyntaxTreePassTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TagHelperBinderSyntaxTreePassTest.cs
@@ -55,6 +55,71 @@ namespace Microsoft.AspNetCore.Razor.Evolution
Assert.Equal("input", inputTagHelper.TagName);
}
+ [Fact]
+ public void Execute_RewritesTagHelpers_TagHelperMatchesElementTwice()
+ {
+ // Arrange
+ var engine = RazorEngine.Create(builder =>
+ {
+ builder.AddTagHelpers(new[]
+ {
+ new TagHelperDescriptor
+ {
+ AssemblyName = "TestAssembly",
+ TagName = "form",
+ TypeName = "TestFormTagHelper",
+ RequiredAttributes = new List()
+ {
+ new TagHelperRequiredAttributeDescriptor()
+ {
+ Name = "a",
+ NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
+ }
+ },
+ },
+ new TagHelperDescriptor
+ {
+ AssemblyName = "TestAssembly",
+ TagName = "form",
+ TypeName = "TestFormTagHelper",
+ RequiredAttributes = new List()
+ {
+ new TagHelperRequiredAttributeDescriptor()
+ {
+ Name = "b",
+ NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
+ }
+ },
+ }
+ });
+ });
+
+ var pass = new TagHelperBinderSyntaxTreePass()
+ {
+ Engine = engine,
+ };
+
+ var content =
+@"
+@addTagHelper *, TestAssembly
+";
+ var sourceDocument = TestRazorSourceDocument.Create(content);
+ var codeDocument = RazorCodeDocument.Create(sourceDocument);
+ var originalTree = RazorSyntaxTree.Parse(sourceDocument);
+
+ // Act
+ var rewrittenTree = pass.Execute(codeDocument, originalTree);
+
+ // Assert
+ Assert.Empty(rewrittenTree.Diagnostics);
+ Assert.Equal(3, rewrittenTree.Root.Children.Count);
+
+ var formTagHelper = Assert.IsType(rewrittenTree.Root.Children[2]);
+ Assert.Equal("form", formTagHelper.TagName);
+ Assert.Single(formTagHelper.Descriptors);
+ }
+
[Fact]
public void Execute_NoopsWhenNoTagHelperFeature()
{
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_DesignTime.codegen.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_DesignTime.codegen.cs
index eea441ba7f..bd9dbfd51e 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_DesignTime.codegen.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_DesignTime.codegen.cs
@@ -16,20 +16,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
public async System.Threading.Tasks.Task ExecuteAsync()
{
__TestNamespace_InputTagHelper = CreateTagHelper();
- __TestNamespace_InputTagHelper = CreateTagHelper();
- __TestNamespace_CatchAllTagHelper = CreateTagHelper();
__TestNamespace_CatchAllTagHelper = CreateTagHelper();
__TestNamespace_InputTagHelper.Type = "checkbox";
- __TestNamespace_InputTagHelper.Type = __TestNamespace_InputTagHelper.Type;
- __TestNamespace_CatchAllTagHelper.Type = __TestNamespace_InputTagHelper.Type;
__TestNamespace_CatchAllTagHelper.Type = __TestNamespace_InputTagHelper.Type;
#line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper.cshtml"
__TestNamespace_InputTagHelper.Checked = true;
#line default
#line hidden
- __TestNamespace_InputTagHelper.Checked = __TestNamespace_InputTagHelper.Checked;
- __TestNamespace_CatchAllTagHelper.Checked = __TestNamespace_InputTagHelper.Checked;
__TestNamespace_CatchAllTagHelper.Checked = __TestNamespace_InputTagHelper.Checked;
}
#pragma warning restore 1998
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_DesignTime.mappings.txt b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_DesignTime.mappings.txt
index 1d5624408a..282cffc3b7 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_DesignTime.mappings.txt
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_DesignTime.mappings.txt
@@ -1,5 +1,5 @@
Source Location: (67:2,32 [4] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper.cshtml)
|true|
-Generated Location: (1664:26,41 [4] )
+Generated Location: (1273:22,41 [4] )
|true|
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_Runtime.codegen.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_Runtime.codegen.cs
index 7c0e710c8f..b009254072 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_Runtime.codegen.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper_Runtime.codegen.cs
@@ -36,18 +36,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
);
__TestNamespace_InputTagHelper = CreateTagHelper();
__tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper);
- __TestNamespace_InputTagHelper = CreateTagHelper();
- __tagHelperExecutionContext.Add(__TestNamespace_InputTagHelper);
- __TestNamespace_CatchAllTagHelper = CreateTagHelper();
- __tagHelperExecutionContext.Add(__TestNamespace_CatchAllTagHelper);
__TestNamespace_CatchAllTagHelper = CreateTagHelper();
__tagHelperExecutionContext.Add(__TestNamespace_CatchAllTagHelper);
__TestNamespace_InputTagHelper.Type = (string)__tagHelperAttribute_0.Value;
__tagHelperExecutionContext.AddTagHelperAttribute(__tagHelperAttribute_0);
- __TestNamespace_InputTagHelper.Type = (string)__tagHelperAttribute_0.Value;
- __tagHelperExecutionContext.AddTagHelperAttribute(__tagHelperAttribute_0);
- __TestNamespace_CatchAllTagHelper.Type = (string)__tagHelperAttribute_0.Value;
- __tagHelperExecutionContext.AddTagHelperAttribute(__tagHelperAttribute_0);
__TestNamespace_CatchAllTagHelper.Type = (string)__tagHelperAttribute_0.Value;
__tagHelperExecutionContext.AddTagHelperAttribute(__tagHelperAttribute_0);
#line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/DuplicateTargetTagHelper.cshtml"
@@ -56,8 +48,6 @@ __TestNamespace_InputTagHelper.Checked = true;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __TestNamespace_InputTagHelper.Checked, global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.DoubleQuotes);
- __TestNamespace_InputTagHelper.Checked = __TestNamespace_InputTagHelper.Checked;
- __TestNamespace_CatchAllTagHelper.Checked = __TestNamespace_InputTagHelper.Checked;
__TestNamespace_CatchAllTagHelper.Checked = __TestNamespace_InputTagHelper.Checked;
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)