Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure generic constraints are emitted in correct order #7965

Merged
merged 2 commits into from Dec 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -6804,6 +6804,68 @@ @using System.Collections.Generic
CompileToAssembly(generated);
}

[Theory] // https://github.com/dotnet/razor/issues/7074
[InlineData("struct", null, "1")]
[InlineData("class", null, "string.Empty")]
[InlineData("notnull", null, "1")]
[InlineData("unmanaged", null, "1")]
[InlineData(null, "new()", "1")]
public void GenericComponent_ConstraintOrdering(string first, string last, string arg)
{
// Arrange
if (first != null)
{
first = $"{first}, ";
}
if (last != null)
{
last = $", {last}";
}
AdditionalSyntaxTrees.Add(Parse($$"""
using Microsoft.AspNetCore.Components;
using System;
namespace Test;
public class MyComponent<T> : ComponentBase where T : {{first}}IComparable{{last}}
{
[Parameter] public T Parameter { get; set; }
}
"""));

// Act
var generated = CompileToCSharp($"""
@using Test
<MyComponent Parameter="{arg}" />
""");

// Assert
CompileToAssembly(generated);
}

[Fact]
public void GenericComponent_UnmanagedConstraint()
{
// Arrange
AdditionalSyntaxTrees.Add(Parse("""
using Microsoft.AspNetCore.Components;
namespace Test;
public class MyComponent<T> : ComponentBase where T : unmanaged
{
[Parameter] public T Parameter { get; set; }
}
"""));

// Act
var generated = CompileToCSharp("""
@using Test
<MyComponent Parameter="1" />
""");

// Assert
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
CompileToAssembly(generated);
}

#endregion

#region Key
Expand Down
@@ -0,0 +1,64 @@
// <auto-generated/>
#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;
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
using Test;

#line default
#line hidden
#nullable disable
public partial class TestComponent : global::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(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
{
global::__Blazor.Test.TestComponent.TypeInference.CreateMyComponent_0(__builder, -1, -1,
#nullable restore
#line 2 "x:\dir\subdir\Test\TestComponent.cshtml"
1

#line default
#line hidden
#nullable disable
);
#nullable restore
#line 2 "x:\dir\subdir\Test\TestComponent.cshtml"
__o = typeof(global::Test.MyComponent<>);

#line default
#line hidden
#nullable disable
}
#pragma warning restore 1998
}
}
namespace __Blazor.Test.TestComponent
{
#line hidden
internal static class TypeInference
{
public static void CreateMyComponent_0<T>(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder, int seq, int __seq0, T __arg0)
where T : unmanaged
{
__builder.OpenComponent<global::Test.MyComponent<T>>(seq);
__builder.AddAttribute(__seq0, "Parameter", __arg0);
__builder.CloseComponent();
}
}
}
#pragma warning restore 1591
@@ -0,0 +1,25 @@
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
UsingDirective - (1:0,1 [10] x:\dir\subdir\Test\TestComponent.cshtml) - Test
ClassDeclaration - - public partial - TestComponent - global::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
HtmlContent - (11:0,11 [2] x:\dir\subdir\Test\TestComponent.cshtml)
LazyIntermediateToken - (11:0,11 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
Component - (13:1,0 [29] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
ComponentAttribute - (37:1,24 [1] x:\dir\subdir\Test\TestComponent.cshtml) - Parameter - Parameter - AttributeStructure.DoubleQuotes
LazyIntermediateToken - (37:1,24 [1] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - 1
NamespaceDeclaration - - __Blazor.Test.TestComponent
ClassDeclaration - - internal static - TypeInference - -
ComponentTypeInferenceMethod - - __Blazor.Test.TestComponent.TypeInference - CreateMyComponent_0
@@ -0,0 +1,10 @@
Source Location: (1:0,1 [10] x:\dir\subdir\Test\TestComponent.cshtml)
|using Test|
Generated Location: (320:12,0 [10] )
|using Test|

Source Location: (37:1,24 [1] x:\dir\subdir\Test\TestComponent.cshtml)
|1|
Generated Location: (1128:32,24 [1] )
|1|

@@ -0,0 +1,50 @@
// <auto-generated/>
#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;
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
using Test;

#line default
#line hidden
#nullable disable
public partial class TestComponent : global::Microsoft.AspNetCore.Components.ComponentBase
{
#pragma warning disable 1998
protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
{
global::__Blazor.Test.TestComponent.TypeInference.CreateMyComponent_0(__builder, 0, 1,
#nullable restore
#line 2 "x:\dir\subdir\Test\TestComponent.cshtml"
1

#line default
#line hidden
#nullable disable
);
}
#pragma warning restore 1998
}
}
namespace __Blazor.Test.TestComponent
{
#line hidden
internal static class TypeInference
{
public static void CreateMyComponent_0<T>(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder, int seq, int __seq0, T __arg0)
where T : unmanaged
{
__builder.OpenComponent<global::Test.MyComponent<T>>(seq);
__builder.AddAttribute(__seq0, "Parameter", __arg0);
__builder.CloseComponent();
}
}
}
#pragma warning restore 1591
@@ -0,0 +1,16 @@
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
UsingDirective - (1:0,1 [12] x:\dir\subdir\Test\TestComponent.cshtml) - Test
ClassDeclaration - - public partial - TestComponent - global::Microsoft.AspNetCore.Components.ComponentBase -
MethodDeclaration - - protected override - void - BuildRenderTree
Component - (13:1,0 [29] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
ComponentAttribute - (37:1,24 [1] x:\dir\subdir\Test\TestComponent.cshtml) - Parameter - Parameter - AttributeStructure.DoubleQuotes
LazyIntermediateToken - (37:1,24 [1] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - 1
NamespaceDeclaration - - __Blazor.Test.TestComponent
ClassDeclaration - - internal static - TypeInference - -
ComponentTypeInferenceMethod - - __Blazor.Test.TestComponent.TypeInference - CreateMyComponent_0
Expand Up @@ -333,27 +333,36 @@ private void CreateTypeParameterProperty(TagHelperDescriptorBuilder builder, ITy
// type parameter so we create a single string representation of all the constraints
// here.
var list = new List<string>();
for (var i = 0; i < typeParameter.ConstraintTypes.Length; i++)
{
var constraintType = typeParameter.ConstraintTypes[i];
list.Add(constraintType.ToDisplayString(GloballyQualifiedFullNameTypeDisplayFormat));
}

// CS0449: The 'class', 'struct', 'unmanaged', 'notnull', and 'default' constraints
// cannot be combined or duplicated, and must be specified first in the constraints list.
if (typeParameter.HasReferenceTypeConstraint)
{
list.Add("class");
}
if (typeParameter.HasValueTypeConstraint)
{
list.Add("struct");
}

if (typeParameter.HasNotNullConstraint)
{
list.Add("notnull");
}

if (typeParameter.HasUnmanagedTypeConstraint)
{
list.Add("unmanaged");
}
else if (typeParameter.HasValueTypeConstraint)
jjonescz marked this conversation as resolved.
Show resolved Hide resolved
{
// `HasValueTypeConstraint` is also true when `unmanaged` constraint is present.
list.Add("struct");
}

for (var i = 0; i < typeParameter.ConstraintTypes.Length; i++)
{
var constraintType = typeParameter.ConstraintTypes[i];
list.Add(constraintType.ToDisplayString(GloballyQualifiedFullNameTypeDisplayFormat));
}

// CS0401: The new() constraint must be the last constraint specified.
if (typeParameter.HasConstructorConstraint)
{
list.Add("new()");
Expand Down