Add support for type parameters to class nodes

This change will allow someone extending Razor to use generic type
parameters in generated classes.

There's no user-level extensibility provided here yet, as in there is no
language support for adding type parameters.
This commit is contained in:
Ryan Nowak 2018-04-05 13:23:20 -07:00
parent ae42d7599d
commit d1c0ab587c
10 changed files with 70 additions and 13 deletions

View File

@ -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(

View File

@ -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(

View File

@ -374,7 +374,8 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
IList<string> modifiers,
string name,
string baseType,
IEnumerable<string> interfaces)
IList<string> interfaces,
IList<string> 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)
{

View File

@ -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);
}

View File

@ -16,7 +16,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
public string BaseType { get; set; }
public IList<string> Interfaces { get; set; }
public IList<string> Interfaces { get; set; } = new List<string>();
public IList<TypeParameter> TypeParameters { get; set; } = new List<TypeParameter>();
public override void Accept(IntermediateNodeVisitor visitor)
{

View File

@ -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; }
}
}

View File

@ -218,6 +218,11 @@ namespace TestNamespace
},
BaseType = "TestBase",
Interfaces = new List<string> { "IFoo", "IBar", },
TypeParameters = new List<TypeParameter>
{
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""
// <auto-generated/>
#pragma warning disable 1591
internal class TestClass : TestBase, IFoo, IBar
internal class TestClass<TKey, TValue> : TestBase, IFoo, IBar
{
}
#pragma warning restore 1591

View File

@ -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";
});

View File

@ -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<TValue> : CustomBaseType, global::System.IDisposable
{
#pragma warning disable 1998
public async override global::System.Threading.Tasks.Task ExecuteAsync()

View File

@ -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<SectionIntermediateNode>
{
@ -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<string>()));
var entries = new List<string>()
{
string.Join(" ", node.Modifiers),
node.ClassName,
node.BaseType,
string.Join(", ", node.Interfaces ?? Array.Empty<string>())
};
// 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)