diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLoweringPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLoweringPass.cs index 2954c6361c..3a7e3c748e 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLoweringPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLoweringPass.cs @@ -34,7 +34,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Components { var reference = references[i]; var node = (TagHelperIntermediateNode)reference.Node; + if (node.TagHelpers.Any(t => t.IsChildContentTagHelper())) + { + // This is a child content tag helper. This will be rewritten when we visit its parent. + continue; + } + // The element didn't match any child content descriptors. Look for any matching component descriptors. var count = 0; for (var j = 0; j < node.TagHelpers.Count; j++) { @@ -42,7 +48,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components { // Only allow a single component tag helper per element. If there are multiple, we'll just consider // the first one and ignore the others. - if (count++ > 1) + if (++count > 1) { node.Diagnostics.Add(ComponentDiagnosticFactory.Create_MultipleComponents(node.Source, node.TagName, node.TagHelpers)); break; @@ -54,10 +60,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Components { reference.Replace(RewriteAsComponent(node, node.TagHelpers.First(t => t.IsComponentTagHelper()))); } - else if (node.TagHelpers.Any(t => t.IsChildContentTagHelper())) - { - // Ignore, this will be handled when we rewrite the parent. - } else { reference.Replace(RewriteAsElement(node)); diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs index c10ecd7ccc..a6cc308951 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs @@ -1698,6 +1698,44 @@ namespace Test CompileToAssembly(generated); } + [Fact] + public void MultipleChildContentMatchingComponentName() + { + // Arrange + AdditionalSyntaxTrees.Add(Parse(@" +using Microsoft.AspNetCore.Components; + +namespace Test +{ + public class MyComponent : ComponentBase + { + [Parameter] + public RenderFragment Header { get; set; } + + [Parameter] + public RenderFragment Footer { get; set; } + } + + public class Header : ComponentBase + { + } +} +")); + + // Act + var generated = CompileToCSharp(@" + +
Hi!
+ +
+
Hello!
"); + + // Assert + AssertDocumentNodeMatchesBaseline(generated.CodeDocument); + AssertCSharpDocumentMatchesBaseline(generated.CodeDocument); + CompileToAssembly(generated); + } + #endregion #region Directives @@ -1804,7 +1842,7 @@ namespace Test3 // Assert AssertDocumentNodeMatchesBaseline(generated.CodeDocument); AssertCSharpDocumentMatchesBaseline(generated.CodeDocument); - var result = CompileToAssembly(generated, throwOnFailure: !DesignTime); + var result = CompileToAssembly(generated, throwOnFailure: false); if (DesignTime) { diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithUsingDirectives_AmbiguousImport/TestComponent.diagnostics.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithUsingDirectives_AmbiguousImport/TestComponent.diagnostics.txt new file mode 100644 index 0000000000..a60f089416 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithUsingDirectives_AmbiguousImport/TestComponent.diagnostics.txt @@ -0,0 +1 @@ +x:\dir\subdir\Test\TestComponent.cshtml(4,1): Error RZ9985: Multiple components use the tag 'SomeComponent'. Components: Test2.SomeComponent, Test3.SomeComponent diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.codegen.cs new file mode 100644 index 0000000000..cbbb4df936 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.codegen.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable 1591 +namespace Test +{ + #line hidden + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Components; + public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase + { + #pragma warning disable 219 + private void __RazorDirectiveTokenHelpers__() { + } + #pragma warning restore 219 + #pragma warning disable 0414 + private static System.Object __o = null; + #pragma warning restore 0414 + #pragma warning disable 1998 + protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder) + { + __builder.AddAttribute(-1, "Header", (Microsoft.AspNetCore.Components.RenderFragment)((__builder2) => { + } + )); + __builder.AddAttribute(-1, "Footer", (Microsoft.AspNetCore.Components.RenderFragment)((__builder2) => { + } + )); +#nullable restore +#line 1 "x:\dir\subdir\Test\TestComponent.cshtml" +__o = typeof(MyComponent); + +#line default +#line hidden +#nullable disable + __builder.AddAttribute(-1, "ChildContent", (Microsoft.AspNetCore.Components.RenderFragment)((__builder2) => { + } + )); +#nullable restore +#line 5 "x:\dir\subdir\Test\TestComponent.cshtml" +__o = typeof(Header); + +#line default +#line hidden +#nullable disable + } + #pragma warning restore 1998 + } +} +#pragma warning restore 1591 diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.ir.txt new file mode 100644 index 0000000000..aea9472c47 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.ir.txt @@ -0,0 +1,29 @@ +Document - + NamespaceDeclaration - - Test + UsingDirective - (3:1,1 [12] ) - System + UsingDirective - (18:2,1 [32] ) - System.Collections.Generic + UsingDirective - (53:3,1 [17] ) - System.Linq + UsingDirective - (73:4,1 [28] ) - System.Threading.Tasks + UsingDirective - (104:5,1 [37] ) - Microsoft.AspNetCore.Components + ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase - + DesignTimeDirective - + CSharpCode - + IntermediateToken - - CSharp - #pragma warning disable 0414 + CSharpCode - + IntermediateToken - - CSharp - private static System.Object __o = null; + CSharpCode - + IntermediateToken - - CSharp - #pragma warning restore 0414 + MethodDeclaration - - protected override - void - BuildRenderTree + Component - (0:0,0 [78] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent + ComponentChildContent - (17:1,2 [20] x:\dir\subdir\Test\TestComponent.cshtml) - Header - context + HtmlContent - (25:1,10 [3] x:\dir\subdir\Test\TestComponent.cshtml) + IntermediateToken - (25:1,10 [3] x:\dir\subdir\Test\TestComponent.cshtml) - Html - Hi! + ComponentChildContent - (41:2,2 [21] x:\dir\subdir\Test\TestComponent.cshtml) - Footer - context + HtmlContent - (49:2,10 [4] x:\dir\subdir\Test\TestComponent.cshtml) + IntermediateToken - (49:2,10 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - Bye! + HtmlContent - (78:3,14 [2] x:\dir\subdir\Test\TestComponent.cshtml) + IntermediateToken - (78:3,14 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n + Component - (80:4,0 [23] x:\dir\subdir\Test\TestComponent.cshtml) - Header + ComponentChildContent - - ChildContent - context + HtmlContent - (88:4,8 [6] x:\dir\subdir\Test\TestComponent.cshtml) + IntermediateToken - (88:4,8 [6] x:\dir\subdir\Test\TestComponent.cshtml) - Html - Hello! diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithUsingDirectives_AmbiguousImport/TestComponent.diagnostics.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithUsingDirectives_AmbiguousImport/TestComponent.diagnostics.txt new file mode 100644 index 0000000000..a60f089416 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithUsingDirectives_AmbiguousImport/TestComponent.diagnostics.txt @@ -0,0 +1 @@ +x:\dir\subdir\Test\TestComponent.cshtml(4,1): Error RZ9985: Multiple components use the tag 'SomeComponent'. Components: Test2.SomeComponent, Test3.SomeComponent diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.codegen.cs new file mode 100644 index 0000000000..fb27ee3716 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.codegen.cs @@ -0,0 +1,37 @@ +// +#pragma warning disable 1591 +namespace Test +{ + #line hidden + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Components; + public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase + { + #pragma warning disable 1998 + protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder) + { + __builder.OpenComponent(0); + __builder.AddAttribute(1, "Header", (Microsoft.AspNetCore.Components.RenderFragment)((__builder2) => { + __builder2.AddContent(2, "Hi!"); + } + )); + __builder.AddAttribute(3, "Footer", (Microsoft.AspNetCore.Components.RenderFragment)((__builder2) => { + __builder2.AddContent(4, "Bye!"); + } + )); + __builder.CloseComponent(); + __builder.AddMarkupContent(5, "\r\n"); + __builder.OpenComponent(6); + __builder.AddAttribute(7, "ChildContent", (Microsoft.AspNetCore.Components.RenderFragment)((__builder2) => { + __builder2.AddContent(8, "Hello!"); + } + )); + __builder.CloseComponent(); + } + #pragma warning restore 1998 + } +} +#pragma warning restore 1591 diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.ir.txt new file mode 100644 index 0000000000..bb0c9a9624 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/MultipleChildContentMatchingComponentName/TestComponent.ir.txt @@ -0,0 +1,22 @@ +Document - + NamespaceDeclaration - - Test + UsingDirective - (3:1,1 [14] ) - System + UsingDirective - (18:2,1 [34] ) - System.Collections.Generic + UsingDirective - (53:3,1 [19] ) - System.Linq + UsingDirective - (73:4,1 [30] ) - System.Threading.Tasks + UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components + ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase - + MethodDeclaration - - protected override - void - BuildRenderTree + Component - (0:0,0 [78] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent + ComponentChildContent - (17:1,2 [20] x:\dir\subdir\Test\TestComponent.cshtml) - Header - context + HtmlContent - (25:1,10 [3] x:\dir\subdir\Test\TestComponent.cshtml) + IntermediateToken - (25:1,10 [3] x:\dir\subdir\Test\TestComponent.cshtml) - Html - Hi! + ComponentChildContent - (41:2,2 [21] x:\dir\subdir\Test\TestComponent.cshtml) - Footer - context + HtmlContent - (49:2,10 [4] x:\dir\subdir\Test\TestComponent.cshtml) + IntermediateToken - (49:2,10 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - Bye! + HtmlContent - (78:3,14 [2] x:\dir\subdir\Test\TestComponent.cshtml) + IntermediateToken - (78:3,14 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n + Component - (80:4,0 [23] x:\dir\subdir\Test\TestComponent.cshtml) - Header + ComponentChildContent - - ChildContent - context + HtmlContent - (88:4,8 [6] x:\dir\subdir\Test\TestComponent.cshtml) + IntermediateToken - (88:4,8 [6] x:\dir\subdir\Test\TestComponent.cshtml) - Html - Hello!