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) {