From 5f5dab4915f09fdb35901a5f825ab5da6b85061e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 16 Jan 2026 00:11:24 +0000
Subject: [PATCH 1/3] Initial plan
From 540cbc7fd42209928a7497c213f1e452871ccc19 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 16 Jan 2026 00:29:36 +0000
Subject: [PATCH 2/3] WIP: Add field auto-generation for @ref attributes -
implementation needs debugging
Co-authored-by: dbreshears <3432571+dbreshears@users.noreply.github.com>
---
.../ComponentCodeGenerationTestBase.cs | 116 ++++++++++++++++++
.../ComponentReferenceCaptureLoweringPass.cs | 58 ++++++++-
2 files changed, 172 insertions(+), 2 deletions(-)
diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs
index cb969d332a5..dee594f9851 100644
--- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs
+++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs
@@ -10097,6 +10097,122 @@ @namespace X
CompileToAssembly(generated, expectedDiagnostics);
}
+ [IntegrationTestFact]
+ public void Component_WithRef_AutoGeneratedField()
+ {
+ // Arrange
+ AdditionalSyntaxTrees.Add(Parse(@"
+using Microsoft.AspNetCore.Components;
+
+namespace Test
+{
+ public class MyComponent : ComponentBase
+ {
+ }
+}
+"));
+
+ // Arrange/Act - No manual field declaration required
+ var generated = CompileToCSharp(@"
+
+
+@code {
+ public void Foo() { System.GC.KeepAlive(myInstance); }
+}
+");
+
+ // Assert
+ AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
+ AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
+ CompileToAssembly(generated);
+ }
+
+ [IntegrationTestFact]
+ public void Element_WithRef_AutoGeneratedField()
+ {
+ // Arrange/Act - No manual field declaration required
+ var generated = CompileToCSharp(@"
+Hello
+
+@code {
+ public void Foo() { System.GC.KeepAlive(myElem); }
+}
+");
+
+ // Assert
+ AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
+ AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
+ CompileToAssembly(generated);
+ }
+
+ [IntegrationTestFact]
+ public void Component_WithRef_AutoGeneratedField_MultipleRefs()
+ {
+ // Arrange
+ AdditionalSyntaxTrees.Add(Parse(@"
+using Microsoft.AspNetCore.Components;
+
+namespace Test
+{
+ public class MyComponent : ComponentBase
+ {
+ }
+}
+"));
+
+ // Arrange/Act - Multiple refs should all be auto-generated
+ var generated = CompileToCSharp(@"
+
+
+
+
+@code {
+ public void Foo()
+ {
+ System.GC.KeepAlive(comp1);
+ System.GC.KeepAlive(comp2);
+ System.GC.KeepAlive(elem1);
+ }
+}
+");
+
+ // Assert
+ AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
+ AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
+ CompileToAssembly(generated);
+ }
+
+ [IntegrationTestFact]
+ public void Component_WithRef_AutoGeneratedField_SkipsExistingField()
+ {
+ // Arrange
+ AdditionalSyntaxTrees.Add(Parse(@"
+using Microsoft.AspNetCore.Components;
+
+namespace Test
+{
+ public class MyComponent : ComponentBase
+ {
+ }
+}
+"));
+
+ // Arrange/Act - If field already exists, don't generate it
+ var generated = CompileToCSharp(@"
+
+
+@code {
+ private Test.MyComponent myInstance = null!;
+ public void Foo() { System.GC.KeepAlive(myInstance); }
+}
+");
+
+ // Assert - should not duplicate the field
+ AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
+ AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
+ CompileToAssembly(generated);
+ }
+
#endregion
#region Templates
diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentReferenceCaptureLoweringPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentReferenceCaptureLoweringPass.cs
index 694a0170602..051202c05fc 100644
--- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentReferenceCaptureLoweringPass.cs
+++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentReferenceCaptureLoweringPass.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Collections.Generic;
using System.Threading;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
@@ -31,17 +32,23 @@ protected override void ExecuteCore(
}
var references = documentNode.FindDescendantReferences();
+
+ // Track field names to avoid generating duplicates
+ var generatedFields = new HashSet(StringComparer.Ordinal);
foreach (var reference in references)
{
if (reference.Node.TagHelper.Kind == TagHelperKind.Ref)
{
- RewriteUsage(reference);
+ RewriteUsage(classNode, reference, generatedFields);
}
}
}
- private static void RewriteUsage(IntermediateNodeReference reference)
+ private static void RewriteUsage(
+ ClassDeclarationIntermediateNode classNode,
+ IntermediateNodeReference reference,
+ HashSet generatedFields)
{
var (node, parent) = reference;
@@ -59,9 +66,56 @@ private static void RewriteUsage(IntermediateNodeReference
Date: Fri, 16 Jan 2026 00:33:26 +0000
Subject: [PATCH 3/3] Update field auto-generation implementation - still
debugging field insertion issue
Co-authored-by: dbreshears <3432571+dbreshears@users.noreply.github.com>
---
.../ComponentReferenceCaptureLoweringPass.cs | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentReferenceCaptureLoweringPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentReferenceCaptureLoweringPass.cs
index 051202c05fc..b758e279d2c 100644
--- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentReferenceCaptureLoweringPass.cs
+++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentReferenceCaptureLoweringPass.cs
@@ -3,7 +3,9 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading;
+using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
namespace Microsoft.AspNetCore.Razor.Language.Components;
@@ -98,10 +100,19 @@ private static bool FieldOrPropertyExists(ClassDeclarationIntermediateNode class
private static void AddFieldDeclaration(ClassDeclarationIntermediateNode classNode, string fieldName, string fieldType)
{
- // Find the insertion point: after any existing fields
+ // Find the insertion point: after any existing fields and design-time setup code
var children = classNode.Children;
var index = 0;
+ // Skip past design-time directives and initial CSharpCode blocks (like #pragma warning)
+ while (index < children.Count &&
+ (children[index] is DesignTimeDirectiveIntermediateNode ||
+ (children[index] is CSharpCodeIntermediateNode code &&
+ code.Children.OfType().Any(t => t.Content.Contains("#pragma") || t.Content.Contains("__o")))))
+ {
+ index++;
+ }
+
// Skip past any existing field declarations to maintain ordering
while (index < children.Count && children[index] is FieldDeclarationIntermediateNode)
{