Skip to content

Commit 0591c70

Browse files
authored
Add null suppression to component reference capture (#8320)
* Add nullable enable option * Add tests * Add null suppression to component reference capture * Update baselines
1 parent 25f991c commit 0591c70

File tree

27 files changed

+503
-31
lines changed

27 files changed

+503
-31
lines changed

src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDesignTimeNodeWriter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,7 @@ protected override void WriteReferenceCaptureInnards(CodeRenderingContext contex
11191119
var captureTypeName = node.IsComponentCapture
11201120
? TypeNameHelper.GetGloballyQualifiedNameIfNeeded(node.ComponentCaptureTypeName)
11211121
: ComponentsApi.ElementReference.FullTypeName;
1122+
var nullSuppression = !context.Options.SuppressNullabilityEnforcement ? "!" : string.Empty;
11221123
WriteCSharpCode(context, new CSharpCodeIntermediateNode
11231124
{
11241125
Source = node.Source,
@@ -1128,7 +1129,7 @@ protected override void WriteReferenceCaptureInnards(CodeRenderingContext contex
11281129
new IntermediateToken
11291130
{
11301131
Kind = TokenKind.CSharp,
1131-
Content = $" = default({captureTypeName});"
1132+
Content = $" = default({captureTypeName}){nullSuppression};"
11321133
}
11331134
}
11341135
});

src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7628,6 +7628,58 @@ public class MyComponent : ComponentBase
76287628
CompileToAssembly(generated);
76297629
}
76307630

7631+
[Fact] // https://github.com/dotnet/razor/issues/8170
7632+
public void Component_WithRef_Nullable()
7633+
{
7634+
// Act
7635+
var generated = CompileToCSharp("""
7636+
<TestComponent @ref="myComponent" />
7637+
7638+
@code {
7639+
private TestComponent myComponent = null!;
7640+
public void Use() { System.GC.KeepAlive(myComponent); }
7641+
}
7642+
""",
7643+
nullableEnable: true);
7644+
7645+
// Assert
7646+
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
7647+
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
7648+
CompileToAssembly(generated);
7649+
}
7650+
7651+
[Fact] // https://github.com/dotnet/razor/issues/8170
7652+
public void Component_WithRef_Nullable_Generic()
7653+
{
7654+
// Arrange
7655+
AdditionalSyntaxTrees.Add(Parse("""
7656+
using Microsoft.AspNetCore.Components;
7657+
7658+
namespace Test;
7659+
7660+
public class MyComponent<T> : ComponentBase
7661+
{
7662+
[Parameter] public T MyParameter { get; set; } = default!;
7663+
}
7664+
"""));
7665+
7666+
// Act
7667+
var generated = CompileToCSharp("""
7668+
<MyComponent @ref="myComponent" MyParameter="1" />
7669+
7670+
@code {
7671+
private MyComponent<int> myComponent = null!;
7672+
public void Use() { System.GC.KeepAlive(myComponent); }
7673+
}
7674+
""",
7675+
nullableEnable: true);
7676+
7677+
// Assert
7678+
AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
7679+
AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
7680+
CompileToAssembly(generated);
7681+
}
7682+
76317683
[Fact]
76327684
public void Component_WithRef_WithChildContent()
76337685
{

src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithCssScope/TestComponent.codegen.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.
4848
));
4949
#nullable restore
5050
#line 7 "x:\dir\subdir\Test\TestComponent.cshtml"
51-
myComponentReference = default(global::Test.TemplatedComponent);
51+
myComponentReference = default(global::Test.TemplatedComponent)!;
5252

5353
#line default
5454
#line hidden
@@ -71,7 +71,7 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.
7171
#nullable disable
7272
#nullable restore
7373
#line 13 "x:\dir\subdir\Test\TestComponent.cshtml"
74-
myElementReference = default(Microsoft.AspNetCore.Components.ElementReference);
74+
myElementReference = default(Microsoft.AspNetCore.Components.ElementReference)!;
7575

7676
#line default
7777
#line hidden

src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithCssScope/TestComponent.mappings.txt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,32 @@ Source Location: (439:10,1 [38] x:\dir\subdir\Test\TestComponent.cshtml)
2222
|if (DateTime.Now.Year > 1950)
2323
{
2424
|
25-
Generated Location: (1965:64,1 [38] )
25+
Generated Location: (1966:64,1 [38] )
2626
|if (DateTime.Now.Year > 1950)
2727
{
2828
|
2929

3030
Source Location: (511:12,38 [18] x:\dir\subdir\Test\TestComponent.cshtml)
3131
|myElementReference|
32-
Generated Location: (2164:73,38 [18] )
32+
Generated Location: (2165:73,38 [18] )
3333
|myElementReference|
3434

3535
Source Location: (557:12,84 [6] x:\dir\subdir\Test\TestComponent.cshtml)
3636
|
3737
|
38-
Generated Location: (2379:78,84 [6] )
38+
Generated Location: (2381:78,84 [6] )
3939
|
4040
|
4141

4242
Source Location: (589:13,30 [10] x:\dir\subdir\Test\TestComponent.cshtml)
4343
|myVariable|
44-
Generated Location: (2574:83,30 [10] )
44+
Generated Location: (2576:83,30 [10] )
4545
|myVariable|
4646

4747
Source Location: (637:13,78 [3] x:\dir\subdir\Test\TestComponent.cshtml)
4848
|
4949
}|
50-
Generated Location: (2947:92,78 [3] )
50+
Generated Location: (2949:92,78 [3] )
5151
|
5252
}|
5353

@@ -62,7 +62,7 @@ Source Location: (651:16,7 [245] x:\dir\subdir\Test\TestComponent.cshtml)
6262
for (var i = 0; i < 10; i++)
6363
{
6464
|
65-
Generated Location: (3129:102,7 [245] )
65+
Generated Location: (3131:102,7 [245] )
6666
|
6767
ElementReference myElementReference;
6868
TemplatedComponent myComponentReference;
@@ -76,12 +76,12 @@ Generated Location: (3129:102,7 [245] )
7676

7777
Source Location: (912:25,28 [1] x:\dir\subdir\Test\TestComponent.cshtml)
7878
|i|
79-
Generated Location: (3541:119,28 [1] )
79+
Generated Location: (3543:119,28 [1] )
8080
|i|
8181

8282
Source Location: (925:25,41 [1] x:\dir\subdir\Test\TestComponent.cshtml)
8383
|i|
84-
Generated Location: (3717:127,41 [1] )
84+
Generated Location: (3719:127,41 [1] )
8585
|i|
8686

8787
Source Location: (931:25,47 [166] x:\dir\subdir\Test\TestComponent.cshtml)
@@ -93,7 +93,7 @@ Source Location: (931:25,47 [166] x:\dir\subdir\Test\TestComponent.cshtml)
9393
System.GC.KeepAlive(myVariable);
9494
}
9595
|
96-
Generated Location: (3889:134,47 [166] )
96+
Generated Location: (3891:134,47 [166] )
9797
|
9898
}
9999

src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithRef/TestComponent.codegen.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.
2727
));
2828
#nullable restore
2929
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
30-
myInstance = default(global::Test.MyComponent);
30+
myInstance = default(global::Test.MyComponent)!;
3131

3232
#line default
3333
#line hidden

src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithRef/TestComponent.mappings.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Source Location: (84:2,7 [104] x:\dir\subdir\Test\TestComponent.cshtml)
88
private Test.MyComponent myInstance;
99
public void Foo() { System.GC.KeepAlive(myInstance); }
1010
|
11-
Generated Location: (1496:45,7 [104] )
11+
Generated Location: (1497:45,7 [104] )
1212
|
1313
private Test.MyComponent myInstance;
1414
public void Foo() { System.GC.KeepAlive(myInstance); }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// <auto-generated/>
2+
#pragma warning disable 1591
3+
namespace Test
4+
{
5+
#line hidden
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Linq;
9+
using System.Threading.Tasks;
10+
using Microsoft.AspNetCore.Components;
11+
public partial class TestComponent : global::Microsoft.AspNetCore.Components.ComponentBase
12+
{
13+
#pragma warning disable 219
14+
private void __RazorDirectiveTokenHelpers__() {
15+
}
16+
#pragma warning restore 219
17+
#pragma warning disable 0414
18+
private static object __o = null;
19+
#pragma warning restore 0414
20+
#pragma warning disable 1998
21+
protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
22+
{
23+
__builder.AddAttribute(-1, "ChildContent", (global::Microsoft.AspNetCore.Components.RenderFragment)((__builder2) => {
24+
}
25+
));
26+
#nullable restore
27+
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
28+
myComponent = default(global::Test.TestComponent)!;
29+
30+
#line default
31+
#line hidden
32+
#nullable disable
33+
#nullable restore
34+
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
35+
__o = typeof(global::Test.TestComponent);
36+
37+
#line default
38+
#line hidden
39+
#nullable disable
40+
}
41+
#pragma warning restore 1998
42+
#nullable restore
43+
#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
44+
45+
private TestComponent myComponent = null!;
46+
public void Use() { System.GC.KeepAlive(myComponent); }
47+
48+
#line default
49+
#line hidden
50+
#nullable disable
51+
}
52+
}
53+
#pragma warning restore 1591
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Document -
2+
NamespaceDeclaration - - Test
3+
UsingDirective - (3:1,1 [12] ) - System
4+
UsingDirective - (18:2,1 [32] ) - System.Collections.Generic
5+
UsingDirective - (53:3,1 [17] ) - System.Linq
6+
UsingDirective - (73:4,1 [28] ) - System.Threading.Tasks
7+
UsingDirective - (104:5,1 [37] ) - Microsoft.AspNetCore.Components
8+
ClassDeclaration - - public partial - TestComponent - global::Microsoft.AspNetCore.Components.ComponentBase -
9+
DesignTimeDirective -
10+
CSharpCode -
11+
IntermediateToken - - CSharp - #pragma warning disable 0414
12+
CSharpCode -
13+
IntermediateToken - - CSharp - private static object __o = null;
14+
CSharpCode -
15+
IntermediateToken - - CSharp - #pragma warning restore 0414
16+
MethodDeclaration - - protected override - void - BuildRenderTree
17+
Component - (0:0,0 [36] x:\dir\subdir\Test\TestComponent.cshtml) - TestComponent
18+
ReferenceCapture - (21:0,21 [11] x:\dir\subdir\Test\TestComponent.cshtml) - myComponent
19+
HtmlContent - (36:0,36 [4] x:\dir\subdir\Test\TestComponent.cshtml)
20+
LazyIntermediateToken - (36:0,36 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n\n
21+
CSharpCode - (47:2,7 [111] x:\dir\subdir\Test\TestComponent.cshtml)
22+
LazyIntermediateToken - (47:2,7 [111] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private TestComponent myComponent = null!;\n public void Use() { System.GC.KeepAlive(myComponent); }\n
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Source Location: (21:0,21 [11] x:\dir\subdir\Test\TestComponent.cshtml)
2+
|myComponent|
3+
Generated Location: (1045:27,21 [11] )
4+
|myComponent|
5+
6+
Source Location: (47:2,7 [111] x:\dir\subdir\Test\TestComponent.cshtml)
7+
|
8+
private TestComponent myComponent = null!;
9+
public void Use() { System.GC.KeepAlive(myComponent); }
10+
|
11+
Generated Location: (1437:43,7 [111] )
12+
|
13+
private TestComponent myComponent = null!;
14+
public void Use() { System.GC.KeepAlive(myComponent); }
15+
|
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// <auto-generated/>
2+
#pragma warning disable 1591
3+
namespace Test
4+
{
5+
#line hidden
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Linq;
9+
using System.Threading.Tasks;
10+
using Microsoft.AspNetCore.Components;
11+
public partial class TestComponent : global::Microsoft.AspNetCore.Components.ComponentBase
12+
{
13+
#pragma warning disable 219
14+
private void __RazorDirectiveTokenHelpers__() {
15+
}
16+
#pragma warning restore 219
17+
#pragma warning disable 0414
18+
private static object __o = null;
19+
#pragma warning restore 0414
20+
#pragma warning disable 1998
21+
protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
22+
{
23+
var __typeInference_CreateMyComponent_0 = global::__Blazor.Test.TestComponent.TypeInference.CreateMyComponent_0(__builder, -1, -1,
24+
#nullable restore
25+
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
26+
1
27+
28+
#line default
29+
#line hidden
30+
#nullable disable
31+
, -1, (__value) => {
32+
#nullable restore
33+
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
34+
myComponent = __value;
35+
36+
#line default
37+
#line hidden
38+
#nullable disable
39+
}
40+
);
41+
__o = __typeInference_CreateMyComponent_0.
42+
#nullable restore
43+
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
44+
MyParameter
45+
46+
#line default
47+
#line hidden
48+
#nullable disable
49+
;
50+
#nullable restore
51+
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
52+
__o = typeof(global::Test.MyComponent<>);
53+
54+
#line default
55+
#line hidden
56+
#nullable disable
57+
}
58+
#pragma warning restore 1998
59+
#nullable restore
60+
#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
61+
62+
private MyComponent<int> myComponent = null!;
63+
public void Use() { System.GC.KeepAlive(myComponent); }
64+
65+
#line default
66+
#line hidden
67+
#nullable disable
68+
}
69+
}
70+
namespace __Blazor.Test.TestComponent
71+
{
72+
#line hidden
73+
internal static class TypeInference
74+
{
75+
public static global::Test.MyComponent<T> CreateMyComponent_0<T>(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder, int seq, int __seq0, T __arg0, int __seq1, System.Action<global::Test.MyComponent<T>> __arg1)
76+
{
77+
__builder.OpenComponent<global::Test.MyComponent<T>>(seq);
78+
__builder.AddAttribute(__seq0, "MyParameter", __arg0);
79+
__builder.AddComponentReferenceCapture(__seq1, (__value) => { __arg1((global::Test.MyComponent<T>)__value); });
80+
__builder.CloseComponent();
81+
return default;
82+
}
83+
}
84+
}
85+
#pragma warning restore 1591
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Document -
2+
NamespaceDeclaration - - Test
3+
UsingDirective - (3:1,1 [12] ) - System
4+
UsingDirective - (18:2,1 [32] ) - System.Collections.Generic
5+
UsingDirective - (53:3,1 [17] ) - System.Linq
6+
UsingDirective - (73:4,1 [28] ) - System.Threading.Tasks
7+
UsingDirective - (104:5,1 [37] ) - Microsoft.AspNetCore.Components
8+
ClassDeclaration - - public partial - TestComponent - global::Microsoft.AspNetCore.Components.ComponentBase -
9+
DesignTimeDirective -
10+
CSharpCode -
11+
IntermediateToken - - CSharp - #pragma warning disable 0414
12+
CSharpCode -
13+
IntermediateToken - - CSharp - private static object __o = null;
14+
CSharpCode -
15+
IntermediateToken - - CSharp - #pragma warning restore 0414
16+
MethodDeclaration - - protected override - void - BuildRenderTree
17+
Component - (0:0,0 [50] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
18+
ReferenceCapture - (19:0,19 [11] x:\dir\subdir\Test\TestComponent.cshtml) - myComponent
19+
ComponentAttribute - (45:0,45 [1] x:\dir\subdir\Test\TestComponent.cshtml) - MyParameter - MyParameter - AttributeStructure.DoubleQuotes
20+
LazyIntermediateToken - (45:0,45 [1] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - 1
21+
HtmlContent - (50:0,50 [4] x:\dir\subdir\Test\TestComponent.cshtml)
22+
LazyIntermediateToken - (50:0,50 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n\n
23+
CSharpCode - (61:2,7 [114] x:\dir\subdir\Test\TestComponent.cshtml)
24+
LazyIntermediateToken - (61:2,7 [114] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private MyComponent<int> myComponent = null!;\n public void Use() { System.GC.KeepAlive(myComponent); }\n
25+
NamespaceDeclaration - - __Blazor.Test.TestComponent
26+
ClassDeclaration - - internal static - TypeInference - -
27+
ComponentTypeInferenceMethod - - __Blazor.Test.TestComponent.TypeInference - CreateMyComponent_0

0 commit comments

Comments
 (0)