diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperTargetExtension.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperTargetExtension.cs index e810b2a087..eb426d0607 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperTargetExtension.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/ViewComponentTagHelperTargetExtension.cs @@ -60,7 +60,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X WriteTargetElementString(context.CodeWriter, node.TagHelper); // Initialize declaration. - using (context.CodeWriter.BuildClassDeclaration(PublicModifiers, node.ClassName, TagHelperTypeName, interfaces: null)) + using (context.CodeWriter.BuildClassDeclaration( + PublicModifiers, + node.ClassName, + TagHelperTypeName, + interfaces: null, + typeParameters: null)) { // Add view component helper. context.CodeWriter.WriteVariableDeclaration( diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperTargetExtension.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperTargetExtension.cs index 298fe23e31..89aba13222 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperTargetExtension.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ViewComponentTagHelperTargetExtension.cs @@ -60,7 +60,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions WriteTargetElementString(context.CodeWriter, node.TagHelper); // Initialize declaration. - using (context.CodeWriter.BuildClassDeclaration(PublicModifiers, node.ClassName, TagHelperTypeName, interfaces: null)) + using (context.CodeWriter.BuildClassDeclaration( + PublicModifiers, + node.ClassName, + TagHelperTypeName, + interfaces: null, + typeParameters: null)) { // Add view component helper. context.CodeWriter.WriteVariableDeclaration( diff --git a/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/CodeWriterExtensions.cs b/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/CodeWriterExtensions.cs index 7d8c846ca2..2da2852c64 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/CodeWriterExtensions.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/CodeWriterExtensions.cs @@ -374,7 +374,8 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration IList modifiers, string name, string baseType, - IEnumerable interfaces) + IList interfaces, + IList typeParameters) { for (var i = 0; i < modifiers.Count; i++) { @@ -385,8 +386,15 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration writer.Write("class "); writer.Write(name); + if (typeParameters != null && typeParameters.Count > 0) + { + writer.Write("<"); + writer.Write(string.Join(", ", typeParameters)); + writer.Write(">"); + } + var hasBaseType = !string.IsNullOrEmpty(baseType); - var hasInterfaces = interfaces != null && interfaces.Count() > 0; + var hasInterfaces = interfaces != null && interfaces.Count > 0; if (hasBaseType || hasInterfaces) { diff --git a/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/DefaultDocumentWriter.cs b/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/DefaultDocumentWriter.cs index 09ed47b6fa..a47295143d 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/DefaultDocumentWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/DefaultDocumentWriter.cs @@ -78,8 +78,8 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration { algorithmId = "{8829d00f-11b8-4213-878b-770e8597ac16}"; } - else if (string.Equals(algorithm, HashAlgorithmName.SHA1.Name, StringComparison.Ordinal) || - + else if (string.Equals(algorithm, HashAlgorithmName.SHA1.Name, StringComparison.Ordinal) || + // In 2.0, we didn't actually expose the name of the algorithm, so it's possible we could get null here. // If that's the case, we just assume SHA1 since that's the only thing we supported in 2.0. algorithm == null) @@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration } else { - var supportedAlgorithms = string.Join(" ", new string[] + var supportedAlgorithms = string.Join(" ", new string[] { HashAlgorithmName.SHA1.Name, HashAlgorithmName.SHA256.Name @@ -143,7 +143,12 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration public override void VisitClassDeclaration(ClassDeclarationIntermediateNode node) { - using (Context.CodeWriter.BuildClassDeclaration(node.Modifiers, node.ClassName, node.BaseType, node.Interfaces)) + using (Context.CodeWriter.BuildClassDeclaration( + node.Modifiers, + node.ClassName, + node.BaseType, + node.Interfaces, + node.TypeParameters.Select(p => p.ParameterName).ToArray())) { VisitDefault(node); } diff --git a/src/Microsoft.AspNetCore.Razor.Language/Intermediate/ClassDeclarationIntermediateNode.cs b/src/Microsoft.AspNetCore.Razor.Language/Intermediate/ClassDeclarationIntermediateNode.cs index 3804ca8a4d..cf923eb429 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Intermediate/ClassDeclarationIntermediateNode.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Intermediate/ClassDeclarationIntermediateNode.cs @@ -16,7 +16,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate public string BaseType { get; set; } - public IList Interfaces { get; set; } + public IList Interfaces { get; set; } = new List(); + + public IList TypeParameters { get; set; } = new List(); public override void Accept(IntermediateNodeVisitor visitor) { diff --git a/src/Microsoft.AspNetCore.Razor.Language/Intermediate/TypeParameter.cs b/src/Microsoft.AspNetCore.Razor.Language/Intermediate/TypeParameter.cs new file mode 100644 index 0000000000..08f953e23a --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Language/Intermediate/TypeParameter.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.Intermediate +{ + public sealed class TypeParameter + { + public string ParameterName { get; set; } + } +} diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/DefaultDocumentWriterTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/DefaultDocumentWriterTest.cs index 67b03f39b8..3537eea4f6 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/DefaultDocumentWriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/CodeGeneration/DefaultDocumentWriterTest.cs @@ -218,6 +218,11 @@ namespace TestNamespace }, BaseType = "TestBase", Interfaces = new List { "IFoo", "IBar", }, + TypeParameters = new List + { + new TypeParameter() { ParameterName = "TKey", }, + new TypeParameter() { ParameterName = "TValue", }, + }, ClassName = "TestClass", }); @@ -236,7 +241,7 @@ namespace TestNamespace @"#pragma checksum ""test.cshtml"" ""{ff1816ec-aa5e-4d10-87f7-6f4963833460}"" ""da39a3ee5e6b4b0d3255bfef95601890afd80709"" // #pragma warning disable 1591 -internal class TestClass : TestBase, IFoo, IBar +internal class TestClass : TestBase, IFoo, IBar { } #pragma warning restore 1591 diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/RazorTemplateEngineIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/RazorTemplateEngineIntegrationTest.cs index ad26823b85..cf547f75e7 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/RazorTemplateEngineIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/IntegrationTests/RazorTemplateEngineIntegrationTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; +using Microsoft.AspNetCore.Razor.Language.Intermediate; using Xunit; namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests @@ -66,6 +67,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests engine.ConfigureClass((document, @class) => { + @class.TypeParameters = new[] { new TypeParameter() { ParameterName = "TValue", }, }; @class.Interfaces = new[] { "global::System.IDisposable" }; @class.BaseType = "CustomBaseType"; }); diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/RazorTemplateEngineIntegrationTest/GenerateCodeWithConfigureClass.codegen.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/RazorTemplateEngineIntegrationTest/GenerateCodeWithConfigureClass.codegen.cs index 2baca26a3f..9c5c3a8a73 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/RazorTemplateEngineIntegrationTest/GenerateCodeWithConfigureClass.codegen.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/RazorTemplateEngineIntegrationTest/GenerateCodeWithConfigureClass.codegen.cs @@ -5,7 +5,7 @@ namespace Razor { #line hidden [global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"38aa8e26c5d2a85c61d8e93fe69dd326fe82671b", @"/TestFiles/IntegrationTests/RazorTemplateEngineIntegrationTest/GenerateCodeWithConfigureClass.cshtml")] - protected internal class MyClass : CustomBaseType, global::System.IDisposable + protected internal class MyClass : CustomBaseType, global::System.IDisposable { #pragma warning disable 1998 public async override global::System.Threading.Tasks.Task ExecuteAsync() diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs index 783d021bf7..4329c0d02d 100644 --- a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs @@ -1,6 +1,7 @@ // 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.IO; using System.Linq; @@ -12,7 +13,7 @@ using Microsoft.AspNetCore.Razor.Language.Intermediate; namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests { // Serializes single IR nodes (shallow). - public class IntermediateNodeWriter : + public class IntermediateNodeWriter : IntermediateNodeVisitor, IExtensionIntermediateNodeVisitor { @@ -32,7 +33,21 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests public override void VisitClassDeclaration(ClassDeclarationIntermediateNode node) { - WriteContentNode(node, string.Join(" ", node.Modifiers), node.ClassName, node.BaseType, string.Join(", ", node.Interfaces ?? new List())); + var entries = new List() + { + string.Join(" ", node.Modifiers), + node.ClassName, + node.BaseType, + string.Join(", ", node.Interfaces ?? Array.Empty()) + }; + + // Avoid adding the type parameters to the baseline if they aren't present. + if (node.TypeParameters != null && node.TypeParameters.Count > 0) + { + entries.Add(string.Join(", ", node.TypeParameters)); + } + + WriteContentNode(node, entries.ToArray()); } public override void VisitCSharpExpressionAttributeValue(CSharpExpressionAttributeValueIntermediateNode node)