Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,7 @@ private void ParseExtensibleDirective(in SyntaxListBuilder<RazorSyntaxNode> buil
{
if (!At(SyntaxKind.Whitespace) &&
!At(SyntaxKind.NewLine) &&
!At(SyntaxKind.Semicolon) &&
!EndOfFile)
{
// This case should never happen in a real scenario. We're just being defensive.
Expand Down Expand Up @@ -1487,6 +1488,13 @@ private void ParseExtensibleDirective(in SyntaxListBuilder<RazorSyntaxNode> buil
{
while (!At(SyntaxKind.NewLine))
{
if (At(SyntaxKind.Semicolon))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Support ; after @typeparam Something where Something : SomethingElse

{
// Consume the ending ';'
EatCurrentToken();
break;
}

AcceptAndMoveNext();
if (EndOfFile)
{
Expand All @@ -1496,6 +1504,11 @@ private void ParseExtensibleDirective(in SyntaxListBuilder<RazorSyntaxNode> buil
}
}
}
else if (At(SyntaxKind.Semicolon))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Support ; after @typeparam Something

{
// Consume the ending ';'
EatCurrentToken();
}
else
{
Context.ErrorSink.OnError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Testing;
using Xunit;

namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
Expand Down Expand Up @@ -212,6 +211,36 @@ public void ComponentWithTypeParameters()
@typeparam TItem1
@typeparam TItem2

<h1>Item1</h1>
@foreach (var item2 in Items2)
{
<p>
@ChildContent(item2);
</p>
}
@code {
[Parameter] public TItem1 Item1 { get; set; }
[Parameter] public List<TItem2> Items2 { get; set; }
[Parameter] public RenderFragment<TItem2> ChildContent { get; set; }
}");

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

[Fact]
public void ComponentWithTypeParameters_WithSemicolon()
{
// Arrange

// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components;
@typeparam TItem1;
@typeparam TItem2;

<h1>Item1</h1>
@foreach (var item2 in Items2)
{
Expand Down Expand Up @@ -356,6 +385,83 @@ @using Test
<p>@context</p>
</TestComponent>

@code {
Image item1 = new Image() { id = 1, url=""https://example.com""};
static Tag tag1 = new Tag() { description = ""A description.""};
static Tag tag2 = new Tag() { description = ""Another description.""};
List<Tag> items = new List<Tag>() { tag1, tag2 };
}");
AssertDocumentNodeMatchesBaseline(useGenerated.CodeDocument);
AssertCSharpDocumentMatchesBaseline(useGenerated.CodeDocument);
CompileToAssembly(useGenerated);
}

[Fact]
public void ComponentWithConstrainedTypeParameters_WithSemicolon()
{
// Arrange
var classes = @"
public class Image
{
public string url { get; set; }
public int id { get; set; }

public Image()
{
url = ""https://example.com/default.png"";
id = 1;
}
}

public interface ITag
{
string description { get; set; }
}

public class Tag : ITag
{
public string description { get; set; }
}
";

AdditionalSyntaxTrees.Add(Parse(classes));

// Act
var generated = CompileToCSharp(@"
@using Microsoft.AspNetCore.Components;
@typeparam TItem1 where TItem1 : Image;
@typeparam TItem2 where TItem2 : ITag;
@typeparam TItem3 where TItem3 : Image, new();

<h1>Item1</h1>
@foreach (var item2 in Items2)
{
<p>
@ChildContent(item2);
</p>
}

<p>Item3</p>

@code {
[Parameter] public TItem1 Item1 { get; set; }
[Parameter] public List<TItem2> Items2 { get; set; }
[Parameter] public TItem3 Item3 { get; set; }
[Parameter] public RenderFragment<TItem2> ChildContent { get; set; }
}");

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

AdditionalSyntaxTrees.Add(Parse(generated.CodeDocument.GetCSharpDocument().GeneratedCode));
var useGenerated = CompileToCSharp("UseTestComponent.cshtml", @"
@using Test
<TestComponent Item1=@item1 Items2=@items Item3=@item1>
<p>@context</p>
</TestComponent>

@code {
Image item1 = new Image() { id = 1, url=""https://example.com""};
static Tag tag1 = new Tag() { description = ""A description.""};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,23 @@ @typeparam TItem
Assert.Contains(bindings.TagHelpers, t => t.Name == "Test.UniqueName<TItem>");
}

[Fact]
public void ComponentDiscovery_CanFindComponent_WithTypeParameterAndSemicolon()
{
// Arrange

// Act
var result = CompileToCSharp("UniqueName.cshtml", @"
@typeparam TItem;
@functions {
[Parameter] public TItem Item { get; set; }
}");

// Assert
var bindings = result.CodeDocument.GetTagHelperContext();
Assert.Contains(bindings.TagHelpers, t => t.Name == "Test.UniqueName<TItem>");
}

[Fact]
public void ComponentDiscovery_CanFindComponent_WithMultipleTypeParameters()
{
Expand All @@ -110,6 +127,25 @@ public void ComponentDiscovery_CanFindComponent_WithMultipleTypeParameters()
@typeparam TItem1
@typeparam TItem2
@typeparam TItem3
@functions {
[Parameter] public TItem1 Item { get; set; }
}");

// Assert
var bindings = result.CodeDocument.GetTagHelperContext();
Assert.Contains(bindings.TagHelpers, t => t.Name == "Test.UniqueName<TItem1, TItem2, TItem3>");
}

[Fact]
public void ComponentDiscovery_CanFindComponent_WithMultipleTypeParametersAndMixedSemicolons()
{
// Arrange

// Act
var result = CompileToCSharp("UniqueName.cshtml", @"
@typeparam TItem1
@typeparam TItem2;
@typeparam TItem3
@functions {
[Parameter] public TItem1 Item { get; set; }
}");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// <auto-generated/>
#pragma warning disable 1591
namespace Test
{
#line hidden
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
using Microsoft.AspNetCore.Components;

#line default
#line hidden
#nullable disable
public partial class TestComponent<TItem1, TItem2, TItem3> : Microsoft.AspNetCore.Components.ComponentBase
where TItem1 : Image
where TItem2 : ITag
where TItem3 : Image, new()
{
#pragma warning disable 219
private void __RazorDirectiveTokenHelpers__() {
((System.Action)(() => {
#nullable restore
#line 2 "x:\dir\subdir\Test\TestComponent.cshtml"
global::System.Object TItem1 = null!;

#line default
#line hidden
#nullable disable
}
))();
((System.Action)(() => {
#pragma warning disable CS0693
#pragma warning disable CS8321
#nullable restore
#line 2 "x:\dir\subdir\Test\TestComponent.cshtml"
void __TypeConstraints_TItem1<TItem1>() where TItem1 : Image
{
}
#pragma warning restore CS0693
#pragma warning restore CS8321

#line default
#line hidden
#nullable disable
}
))();
((System.Action)(() => {
#nullable restore
#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
global::System.Object TItem2 = null!;

#line default
#line hidden
#nullable disable
}
))();
((System.Action)(() => {
#pragma warning disable CS0693
#pragma warning disable CS8321
#nullable restore
#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
void __TypeConstraints_TItem2<TItem2>() where TItem2 : ITag
{
}
#pragma warning restore CS0693
#pragma warning restore CS8321

#line default
#line hidden
#nullable disable
}
))();
((System.Action)(() => {
#nullable restore
#line 4 "x:\dir\subdir\Test\TestComponent.cshtml"
global::System.Object TItem3 = null!;

#line default
#line hidden
#nullable disable
}
))();
((System.Action)(() => {
#pragma warning disable CS0693
#pragma warning disable CS8321
#nullable restore
#line 4 "x:\dir\subdir\Test\TestComponent.cshtml"
void __TypeConstraints_TItem3<TItem3>() where TItem3 : Image, new()
{
}
#pragma warning restore CS0693
#pragma warning restore CS8321

#line default
#line hidden
#nullable disable
}
))();
}
#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)
{
#nullable restore
#line 6 "x:\dir\subdir\Test\TestComponent.cshtml"
foreach (var item2 in Items2)
{


#line default
#line hidden
#nullable disable
#nullable restore
#line 10 "x:\dir\subdir\Test\TestComponent.cshtml"
__o = ChildContent(item2);

#line default
#line hidden
#nullable disable
#nullable restore
#line 11 "x:\dir\subdir\Test\TestComponent.cshtml"

}

#line default
#line hidden
#nullable disable
}
#pragma warning restore 1998
#nullable restore
#line 16 "x:\dir\subdir\Test\TestComponent.cshtml"

[Parameter] public TItem1 Item1 { get; set; }
[Parameter] public List<TItem2> Items2 { get; set; }
[Parameter] public TItem3 Item3 { get; set; }
[Parameter] public RenderFragment<TItem2> ChildContent { get; set; }

#line default
#line hidden
#nullable disable
}
}
#pragma warning restore 1591
Loading