diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ModelDirectiveTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ModelDirectiveTest.cs index 7fc3fdf339e..de56a2a3af0 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ModelDirectiveTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ModelDirectiveTest.cs @@ -86,6 +86,7 @@ @model Type1 var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -114,6 +115,7 @@ @model Type2 var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -141,6 +143,7 @@ @model Type1 var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -166,6 +169,7 @@ @inherits BaseType var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -193,6 +197,7 @@ @inherits BaseType var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.Null(baseType.BaseType.Source); @@ -224,6 +229,7 @@ @model SomeType var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.Null(baseType.BaseType.Source); diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcViewDocumentClassifierPassTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcViewDocumentClassifierPassTest.cs index a17fe825513..422e53ae8fe 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcViewDocumentClassifierPassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcViewDocumentClassifierPassTest.cs @@ -86,7 +86,7 @@ public void MvcViewDocumentClassifierPass_SetsClass() Assert.Equal("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", baseNode.BaseType.Content); Assert.NotNull(baseNode.ModelType); Assert.Equal("TModel", baseNode.ModelType.Content); - Assert.Equal(["public"], classNode.Modifiers); + Assert.Equal(["public"], classNode.Modifiers); Assert.Equal("Test", classNode.ClassName); } @@ -109,7 +109,7 @@ public void MvcViewDocumentClassifierPass_NullFilePath_SetsClass() Assert.Equal("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", baseNode.BaseType.Content); Assert.NotNull(baseNode.ModelType); Assert.Equal("TModel", baseNode.ModelType.Content); - Assert.Equal(["public"], classNode.Modifiers); + Assert.Equal(["public"], classNode.Modifiers); AssertEx.Equal("AspNetCore_ec563e63d931b806184cb02f79875e4f3b21d1ca043ad06699424459128b58c0", classNode.ClassName); } @@ -186,6 +186,6 @@ public void MvcViewDocumentClassifierPass_SetsUpExecuteAsyncMethod() Assert.Equal("ExecuteAsync", methodNode.MethodName); Assert.Equal("global::System.Threading.Tasks.Task", methodNode.ReturnType); - Assert.Equal(["public", "async", "override"], methodNode.Modifiers); + Assert.Equal(["public", "async", "override"], methodNode.Modifiers); } } diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/AssemblyAttributeInjectionPassTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/AssemblyAttributeInjectionPassTest.cs index 364471cef82..25e0565eb43 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/AssemblyAttributeInjectionPassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/AssemblyAttributeInjectionPassTest.cs @@ -33,10 +33,9 @@ public void Execute_NoOps_IfNamespaceNodeHasEmptyContent() var documentNode = new DocumentIntermediateNode() { Options = codeDocument.CodeGenerationOptions }; var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode() + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - Content = string.Empty, - IsPrimaryNamespace = true, + Content = string.Empty }; builder.Push(@namespace); @@ -77,18 +76,14 @@ public void Execute_NoOps_IfClassNameIsEmpty() var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode() + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - Content = "SomeNamespace", - IsPrimaryNamespace = true, + Content = "SomeNamespace" }; builder.Push(@namespace); - builder.Add(new ClassDeclarationIntermediateNode - { - IsPrimaryClass = true, - }); + builder.Add(new ClassDeclarationIntermediateNode(isPrimaryClass: true)); // Act ProjectEngine.ExecutePass(codeDocument, documentNode); @@ -113,10 +108,9 @@ public void Execute_NoOps_IfDocumentIsNotViewOrPage() var @namespace = new NamespaceDeclarationIntermediateNode() { Content = "SomeNamespace" }; builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - ClassName = "SomeName", - IsPrimaryClass = true, + ClassName = "SomeName" }; builder.Add(@class); @@ -143,18 +137,16 @@ public void Execute_NoOps_ForDesignTime() }; var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - Content = "SomeNamespace", - IsPrimaryNamespace = true, + Content = "SomeNamespace" }; builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - ClassName = "SomeName", - IsPrimaryClass = true, + ClassName = "SomeName" }; builder.Add(@class); @@ -184,17 +176,15 @@ public void Execute_AddsRazorViewAttribute_ToViews() var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - Content = "SomeNamespace", - IsPrimaryNamespace = true, + Content = "SomeNamespace" }; builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - ClassName = "SomeName", - IsPrimaryClass = true, + ClassName = "SomeName" }; builder.Add(@class); @@ -231,18 +221,16 @@ public void Execute_EscapesViewPathWhenAddingAttributeToViews() var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - Content = "SomeNamespace", - IsPrimaryNamespace = true, + Content = "SomeNamespace" }; builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - ClassName = "SomeName", - IsPrimaryClass = true, + ClassName = "SomeName" }; builder.Add(@class); @@ -286,18 +274,16 @@ public void Execute_AddsRazorPagettribute_ToPage() builder.Add(pageDirective); - var @namespace = new NamespaceDeclarationIntermediateNode + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - Content = "SomeNamespace", - IsPrimaryNamespace = true, + Content = "SomeNamespace" }; builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - ClassName = "SomeName", - IsPrimaryClass = true, + ClassName = "SomeName" }; builder.Add(@class); @@ -335,17 +321,16 @@ public void Execute_EscapesViewPathAndRouteWhenAddingAttributeToPage() var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - Content = "SomeNamespace", - IsPrimaryNamespace = true, + Content = "SomeNamespace" }; + builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - ClassName = "SomeName", - IsPrimaryClass = true, + ClassName = "SomeName" }; builder.Add(@class); diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ModelDirectiveTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ModelDirectiveTest.cs index 7e5c1e7625e..e341a9f7097 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ModelDirectiveTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ModelDirectiveTest.cs @@ -86,6 +86,7 @@ @model Type1 var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -114,6 +115,7 @@ @model Type2 var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -141,6 +143,7 @@ @model Type1 var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -166,6 +169,7 @@ @inherits BaseType var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -193,6 +197,7 @@ @inherits BaseType var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.Null(baseType.BaseType.Source); @@ -224,6 +229,7 @@ @model SomeType var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.Null(baseType.BaseType.Source); diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/MvcViewDocumentClassifierPassTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/MvcViewDocumentClassifierPassTest.cs index c2dc16f8e28..93b2a92f4e5 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/MvcViewDocumentClassifierPassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/MvcViewDocumentClassifierPassTest.cs @@ -86,7 +86,7 @@ public void MvcViewDocumentClassifierPass_SetsClass() Assert.Equal("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", baseNode.BaseType.Content); Assert.NotNull(baseNode.ModelType); Assert.Equal("TModel", baseNode.ModelType.Content); - Assert.Equal(["public"], classNode.Modifiers); + Assert.Equal(["public"], classNode.Modifiers); Assert.Equal("Test", classNode.ClassName); } @@ -109,7 +109,7 @@ public void MvcViewDocumentClassifierPass_NullFilePath_SetsClass() Assert.Equal("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", baseNode.BaseType.Content); Assert.NotNull(baseNode.ModelType); Assert.Equal("TModel", baseNode.ModelType.Content); - Assert.Equal(["public"], classNode.Modifiers); + Assert.Equal(["public"], classNode.Modifiers); AssertEx.Equal("AspNetCore_ec563e63d931b806184cb02f79875e4f3b21d1ca043ad06699424459128b58c0", classNode.ClassName); } @@ -185,6 +185,6 @@ public void MvcViewDocumentClassifierPass_SetsUpExecuteAsyncMethod() Assert.Equal("ExecuteAsync", methodNode.MethodName); Assert.Equal("global::System.Threading.Tasks.Task", methodNode.ReturnType); - Assert.Equal(["public", "async", "override"], methodNode.Modifiers); + Assert.Equal(["public", "async", "override"], methodNode.Modifiers); } } diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/RazorPageDocumentClassifierPassTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/RazorPageDocumentClassifierPassTest.cs index ae6fe666ea0..7c1988beae7 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/RazorPageDocumentClassifierPassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/RazorPageDocumentClassifierPassTest.cs @@ -198,8 +198,8 @@ public void RazorPageDocumentClassifierPass_SetsClass() var documentNode = processor.GetDocumentNode(); var classNode = documentNode.GetClassNode(); - Assert.Equal("global::Microsoft.AspNetCore.Mvc.RazorPages.Page", classNode.BaseType.BaseType.Content); - Assert.Equal(["public"], classNode.Modifiers); + Assert.Equal("global::Microsoft.AspNetCore.Mvc.RazorPages.Page", classNode.BaseType?.BaseType.Content); + Assert.Equal(["public"], classNode.Modifiers); Assert.Equal("Test", classNode.ClassName); } @@ -218,8 +218,8 @@ public void RazorPageDocumentClassifierPass_NullFilePath_SetsClass() var documentNode = processor.GetDocumentNode(); var classNode = documentNode.GetClassNode(); - Assert.Equal("global::Microsoft.AspNetCore.Mvc.RazorPages.Page", classNode.BaseType.BaseType.Content); - Assert.Equal(["public"], classNode.Modifiers); + Assert.Equal("global::Microsoft.AspNetCore.Mvc.RazorPages.Page", classNode.BaseType?.BaseType.Content); + Assert.Equal(["public"], classNode.Modifiers); AssertEx.Equal("AspNetCore_c3b458108610c1a2aa6eede0a5685ede853e036732db515609b2a23ca15359e1", classNode.ClassName); } @@ -295,7 +295,7 @@ public void RazorPageDocumentClassifierPass_SetsUpExecuteAsyncMethod() Assert.Equal("ExecuteAsync", methodNode.MethodName); Assert.Equal("global::System.Threading.Tasks.Task", methodNode.ReturnType); - Assert.Equal(["public", "async", "override"], methodNode.Modifiers); + Assert.Equal(["public", "async", "override"], methodNode.Modifiers); } [Fact] diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ConsolidatedMvcViewDocumentClassifierPassTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ConsolidatedMvcViewDocumentClassifierPassTest.cs index ebc4f6aa3b8..4559fb3e296 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ConsolidatedMvcViewDocumentClassifierPassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ConsolidatedMvcViewDocumentClassifierPassTest.cs @@ -53,7 +53,7 @@ public void ConsolidatedMvcViewDocumentClassifierPass_SetsClass() Assert.Equal("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", baseNode.BaseType.Content); Assert.NotNull(baseNode.ModelType); Assert.Equal("TModel", baseNode.ModelType.Content); - Assert.Equal(["internal", "sealed"], classNode.Modifiers); + Assert.Equal(["internal", "sealed"], classNode.Modifiers); Assert.Equal("Test", classNode.ClassName); } @@ -76,7 +76,7 @@ public void MvcViewDocumentClassifierPass_NullFilePath_SetsClass() Assert.Equal("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", baseNode.BaseType.Content); Assert.NotNull(baseNode.ModelType); Assert.Equal("TModel", baseNode.ModelType.Content); - Assert.Equal(["internal", "sealed"], classNode.Modifiers); + Assert.Equal(["internal", "sealed"], classNode.Modifiers); AssertEx.Equal("AspNetCore_ec563e63d931b806184cb02f79875e4f3b21d1ca043ad06699424459128b58c0", classNode.ClassName); } @@ -98,7 +98,7 @@ public void ConsolidatedMvcViewDocumentClassifierPass_UsesRelativePathToGenerate var classNode = documentNode.GetClassNode(); Assert.Equal(expected, classNode.ClassName); - Assert.Equal(["internal", "sealed"], classNode.Modifiers); + Assert.Equal(["internal", "sealed"], classNode.Modifiers); } [Fact] @@ -117,6 +117,6 @@ public void ConsolidatedMvcViewDocumentClassifierPass_SetsUpExecuteAsyncMethod() Assert.Equal("ExecuteAsync", methodNode.MethodName); Assert.Equal("global::System.Threading.Tasks.Task", methodNode.ReturnType); - Assert.Equal(["public", "async", "override"], methodNode.Modifiers); + Assert.Equal(["public", "async", "override"], methodNode.Modifiers); } } diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ModelDirectiveTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ModelDirectiveTest.cs index b8139297f51..30bf11c9a65 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ModelDirectiveTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ModelDirectiveTest.cs @@ -83,6 +83,7 @@ @model Type1 var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -111,6 +112,7 @@ @model Type2 var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -138,6 +140,7 @@ @model Type1 var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -163,6 +166,7 @@ @inherits BaseType var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.NotNull(baseType.BaseType.Source); @@ -190,6 +194,7 @@ @inherits BaseType var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.Null(baseType.BaseType.Source); @@ -221,6 +226,7 @@ @model SomeType var classNode = documentNode.GetClassNode(); var baseType = classNode.BaseType; + Assert.NotNull(baseType); Assert.Equal("BaseType", baseType.BaseType.Content); Assert.Null(baseType.BaseType.Source); diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcViewDocumentClassifierPassTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcViewDocumentClassifierPassTest.cs index 2e052371be7..e049ab0b8c7 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcViewDocumentClassifierPassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcViewDocumentClassifierPassTest.cs @@ -86,7 +86,7 @@ public void MvcViewDocumentClassifierPass_SetsClass() Assert.Equal("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", baseNode.BaseType.Content); Assert.NotNull(baseNode.ModelType); Assert.Equal("TModel", baseNode.ModelType.Content); - Assert.Equal(["public"], classNode.Modifiers); + Assert.Equal(["public"], classNode.Modifiers); Assert.Equal("Test", classNode.ClassName); } @@ -109,7 +109,7 @@ public void MvcViewDocumentClassifierPass_NullFilePath_SetsClass() Assert.Equal("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", baseNode.BaseType.Content); Assert.NotNull(baseNode.ModelType); Assert.Equal("TModel", baseNode.ModelType.Content); - Assert.Equal(["public"], classNode.Modifiers); + Assert.Equal(["public"], classNode.Modifiers); AssertEx.Equal("AspNetCore_ec563e63d931b806184cb02f79875e4f3b21d1ca043ad06699424459128b58c0", classNode.ClassName); } @@ -185,6 +185,6 @@ public void MvcViewDocumentClassifierPass_SetsUpExecuteAsyncMethod() Assert.Equal("ExecuteAsync", methodNode.MethodName); Assert.Equal("global::System.Threading.Tasks.Task", methodNode.ReturnType); - Assert.Equal(["public", "async", "override"], methodNode.Modifiers); + Assert.Equal(["public", "async", "override"], methodNode.Modifiers); } } diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/RazorPageDocumentClassifierPassTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/RazorPageDocumentClassifierPassTest.cs index 895409162a0..ca928052cd6 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/RazorPageDocumentClassifierPassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/RazorPageDocumentClassifierPassTest.cs @@ -197,8 +197,8 @@ public void RazorPageDocumentClassifierPass_SetsClass() var documentNode = processor.GetDocumentNode(); var classNode = documentNode.GetClassNode(); - Assert.Equal("global::Microsoft.AspNetCore.Mvc.RazorPages.Page", classNode.BaseType.BaseType.Content); - Assert.Equal(["public"], classNode.Modifiers); + Assert.Equal("global::Microsoft.AspNetCore.Mvc.RazorPages.Page", classNode.BaseType?.BaseType.Content); + Assert.Equal(["public"], classNode.Modifiers); Assert.Equal("Test", classNode.ClassName); } @@ -217,8 +217,8 @@ public void RazorPageDocumentClassifierPass_NullFilePath_SetsClass() var documentNode = processor.GetDocumentNode(); var classNode = documentNode.GetClassNode(); - Assert.Equal("global::Microsoft.AspNetCore.Mvc.RazorPages.Page", classNode.BaseType.BaseType.Content); - Assert.Equal(["public"], classNode.Modifiers); + Assert.Equal("global::Microsoft.AspNetCore.Mvc.RazorPages.Page", classNode.BaseType?.BaseType.Content); + Assert.Equal(["public"], classNode.Modifiers); AssertEx.Equal("AspNetCore_c3b458108610c1a2aa6eede0a5685ede853e036732db515609b2a23ca15359e1", classNode.ClassName); } @@ -294,7 +294,7 @@ public void RazorPageDocumentClassifierPass_SetsUpExecuteAsyncMethod() Assert.Equal("ExecuteAsync", methodNode.MethodName); Assert.Equal("global::System.Threading.Tasks.Task", methodNode.ReturnType); - Assert.Equal(["public", "async", "override"], methodNode.Modifiers); + Assert.Equal(["public", "async", "override"], methodNode.Modifiers); } [Fact] diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/CSharpCodeWriterTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/CSharpCodeWriterTest.cs index 967941700ce..91be524277c 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/CSharpCodeWriterTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/CSharpCodeWriterTest.cs @@ -1,11 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System; using System.Collections.Generic; -using Microsoft.AspNetCore.Razor.Language.Intermediate; using Roslyn.Test.Utilities; using Xunit; @@ -306,7 +303,11 @@ public void WriteField_WritesFieldDeclaration() using var writer = new CodeWriter(); // Act - writer.WriteField(Array.Empty(), new[] { "private" }, "global::System.String", "_myString"); + writer.WriteField( + suppressWarnings: [], + modifiers: ["private"], + typeName: "global::System.String", + fieldName: "_myString"); // Assert var output = writer.GetText().ToString(); @@ -323,7 +324,11 @@ public void WriteField_WithModifiers_WritesFieldDeclaration() using var writer = new CodeWriter(); // Act - writer.WriteField(Array.Empty(), new[] { "private", "readonly", "static" }, "global::System.String", "_myString"); + writer.WriteField( + suppressWarnings: [], + modifiers: ["private", "readonly", "static"], + typeName: "global::System.String", + fieldName: "_myString"); // Assert var output = writer.GetText().ToString(); @@ -341,10 +346,10 @@ public void WriteField_WithModifiersAndSupressions_WritesFieldDeclaration() // Act writer.WriteField( - new[] { "0001", "0002", }, - new[] { "private", "readonly", "static" }, - "global::System.String", - "_myString"); + suppressWarnings: ["0001", "0002"], + modifiers: ["private", "readonly", "static"], + typeName: "global::System.String", + fieldName: "_myString"); // Assert var output = writer.GetText().ToString(); @@ -363,13 +368,13 @@ public void WriteField_WithModifiersAndSupressions_WritesFieldDeclaration() public void WriteAutoPropertyDeclaration_WritesPropertyDeclaration() { // Arrange - using var writer = new CodeWriter(); + using var context = TestCodeRenderingContext.Create(); // Act - writer.WriteAutoPropertyDeclaration(new[] { "public" }, "global::System.String", "MyString"); + context.WriteAutoPropertyDeclaration(["public"], "global::System.String", "MyString"); // Assert - var output = writer.GetText().ToString(); + var output = context.CodeWriter.GetText().ToString(); Assert.Equal(""" public global::System.String MyString { get; set; } @@ -380,13 +385,13 @@ public void WriteAutoPropertyDeclaration_WritesPropertyDeclaration() public void WriteAutoPropertyDeclaration_WithModifiers_WritesPropertyDeclaration() { // Arrange - using var writer = new CodeWriter(); + using var context = TestCodeRenderingContext.Create(); // Act - writer.WriteAutoPropertyDeclaration(new[] { "public", "static" }, "global::System.String", "MyString"); + context.WriteAutoPropertyDeclaration(["public", "static"], "global::System.String", "MyString"); // Assert - var output = writer.GetText().ToString(); + var output = context.CodeWriter.GetText().ToString(); Assert.Equal(""" public static global::System.String MyString { get; set; } @@ -401,11 +406,12 @@ public void CSharpCodeWriter_RespectTabSetting() .WithIndentSize(4) .WithFlags(indentWithTabs: true); - using var writer = new CodeWriter(options); + using var context = TestCodeRenderingContext.Create(options); + var writer = context.CodeWriter; // Act - writer.BuildClassDeclaration(Array.Empty(), "C", null, Array.Empty(), Array.Empty(), context: null); - writer.WriteField(Array.Empty(), Array.Empty(), "int", "f"); + context.BuildClassDeclaration(modifiers: [], "C", null, interfaces: [], typeParameters: []); + writer.WriteField(suppressWarnings: [], modifiers: [], typeName: "int", fieldName: "f"); // Assert var output = writer.GetText().ToString(); @@ -425,11 +431,12 @@ public void CSharpCodeWriter_RespectSpaceSetting() .WithIndentSize(4) .WithFlags(indentWithTabs: false); - using var writer = new CodeWriter(options); + using var context = TestCodeRenderingContext.Create(options); + var writer = context.CodeWriter; // Act - writer.BuildClassDeclaration(Array.Empty(), "C", null, Array.Empty(), Array.Empty(), context: null); - writer.WriteField(Array.Empty(), Array.Empty(), "int", "f"); + context.BuildClassDeclaration(modifiers: [], "C", null, interfaces: [], typeParameters: []); + writer.WriteField(suppressWarnings: [], modifiers: [], typeName: "int", fieldName: "f"); // Assert var output = writer.GetText().ToString(); diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DefaultDocumentWriterTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DefaultDocumentWriterTest.cs index 49a20dd1bbf..fa4d621c841 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DefaultDocumentWriterTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DefaultDocumentWriterTest.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System.Collections.Generic; using Microsoft.AspNetCore.Razor.Language.Intermediate; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; @@ -175,17 +172,13 @@ public void WriteDocument_WritesClass() var builder = IntermediateNodeBuilder.Create(document); builder.Add(new ClassDeclarationIntermediateNode() { - Modifiers = - { - "internal" - }, + Modifiers = ["internal"], BaseType = new BaseTypeWithModel("TestBase"), Interfaces = [IntermediateToken.CreateCSharpToken("IFoo"), IntermediateToken.CreateCSharpToken("IBar")], - TypeParameters = new List - { - new TypeParameter() { ParameterName = "TKey", }, - new TypeParameter() { ParameterName = "TValue", }, - }, + TypeParameters = [ + new TypeParameter("TKey"), + new TypeParameter("TValue") + ], ClassName = "TestClass", }); @@ -222,17 +215,13 @@ public void WriteDocument_WithNullableContext_WritesClass() var builder = IntermediateNodeBuilder.Create(document); builder.Add(new ClassDeclarationIntermediateNode() { - Modifiers = - { - "internal" - }, + Modifiers = ["internal"], BaseType = new BaseTypeWithModel("TestBase"), Interfaces = [IntermediateToken.CreateCSharpToken("IFoo"), IntermediateToken.CreateCSharpToken("IBar")], - TypeParameters = new List - { - new TypeParameter() { ParameterName = "TKey", }, - new TypeParameter() { ParameterName = "TValue", }, - }, + TypeParameters = [ + new TypeParameter("TKey"), + new TypeParameter("TValue") + ], ClassName = "TestClass", NullableContext = true, }); @@ -272,17 +261,13 @@ public void WriteDocument_WritesClass_ConstrainedGenericTypeParameters() var builder = IntermediateNodeBuilder.Create(document); builder.Add(new ClassDeclarationIntermediateNode() { - Modifiers = - { - "internal" - }, + Modifiers = ["internal"], BaseType = new BaseTypeWithModel("TestBase"), Interfaces = [IntermediateToken.CreateCSharpToken("IFoo"), IntermediateToken.CreateCSharpToken("IBar")], - TypeParameters = new List - { - new TypeParameter() { ParameterName = "TKey", Constraints = "where TKey : class" }, - new TypeParameter() { ParameterName = "TValue", Constraints = "where TValue : class" }, - }, + TypeParameters = [ + new TypeParameter("TKey", constraints: "where TKey : class"), + new TypeParameter("TValue", constraints: "where TValue : class") + ], ClassName = "TestClass", }); @@ -321,31 +306,12 @@ public void WriteDocument_WritesMethod() var builder = IntermediateNodeBuilder.Create(document); builder.Add(new MethodDeclarationIntermediateNode() { - Modifiers = - { - "internal", - "virtual", - "async", - }, + Modifiers = ["internal", "virtual", "async"], MethodName = "TestMethod", - Parameters = - { - new MethodParameter() - { - Modifiers = - { - "readonly", - "ref", - }, - ParameterName = "a", - TypeName = "int", - }, - new MethodParameter() - { - ParameterName = "b", - TypeName = "string", - } - }, + Parameters = [ + new MethodParameter(modifiers: ["readonly", "ref"], typeName: "int", parameterName: "a"), + new MethodParameter(typeName: "string", parameterName: "b") + ], ReturnType = "string", }); @@ -382,16 +348,10 @@ public void WriteDocument_WritesField() // Arrange var document = new DocumentIntermediateNode(); var builder = IntermediateNodeBuilder.Create(document); - builder.Add(new FieldDeclarationIntermediateNode() - { - Modifiers = - { - "internal", - "readonly", - }, - FieldName = "_foo", - FieldType = "string", - }); + builder.Add(new FieldDeclarationIntermediateNode( + fieldName: "_foo", + fieldType: "string", + modifiers: ["internal", "readonly"])); var codeDocument = TestRazorCodeDocument.CreateEmpty(); var options = RazorCodeGenerationOptions.Default; @@ -422,17 +382,11 @@ public void WriteDocument_WritesProperty() // Arrange var document = new DocumentIntermediateNode(); var builder = IntermediateNodeBuilder.Create(document); - builder.Add(new PropertyDeclarationIntermediateNode() - { - Modifiers = - { - "internal", - "virtual", - }, - PropertyName = "Foo", - PropertyType = IntermediateToken.CreateCSharpToken("string"), - PropertyExpression = "default" - }); + builder.Add(new PropertyDeclarationIntermediateNode( + propertyName: "Foo", + propertyType: IntermediateToken.CreateCSharpToken("string"), + propertyExpression: "default", + modifiers: ["internal", "virtual"])); var codeDocument = TestRazorCodeDocument.CreateEmpty(); var options = RazorCodeGenerationOptions.Default; diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Components/ComponentDocumentClassifierPassTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Components/ComponentDocumentClassifierPassTest.cs index 001356837c9..491386de091 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Components/ComponentDocumentClassifierPassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Components/ComponentDocumentClassifierPassTest.cs @@ -75,8 +75,8 @@ public void ComponentDocumentClassifierPass_SetsClass() var documentNode = processor.GetDocumentNode(); var classNode = documentNode.GetClassNode(); - Assert.Equal($"global::{ComponentsApi.ComponentBase.FullTypeName}", classNode.BaseType.BaseType.Content); - Assert.Equal(["public", "partial"], classNode.Modifiers); + Assert.Equal($"global::{ComponentsApi.ComponentBase.FullTypeName}", classNode.BaseType?.BaseType.Content); + Assert.Equal(["public", "partial"], classNode.Modifiers); Assert.Equal("Test", classNode.ClassName); } @@ -148,6 +148,6 @@ public void ComponentDocumentClassifierPass_SetsUpMainMethod() Assert.Equal(ComponentsApi.ComponentBase.BuildRenderTree, methodNode.MethodName); Assert.Equal("void", methodNode.ReturnType); - Assert.Equal(["protected", "override"], methodNode.Modifiers); + Assert.Equal(["protected", "override"], methodNode.Modifiers); } } diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/InheritsDirectivePassTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/InheritsDirectivePassTest.cs index 11a2e3cfb0c..ecae326cca3 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/InheritsDirectivePassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/InheritsDirectivePassTest.cs @@ -55,6 +55,7 @@ public void Execute_Inherits_SetsClassDeclarationBaseType() node => Assert.IsType(node)); var @class = (ClassDeclarationIntermediateNode)@namespace.Children[0]; + Assert.NotNull(@class.BaseType); Assert.Equal("Hello", @class.BaseType.BaseType.Content); } } diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/MetadataAttributePassTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/MetadataAttributePassTest.cs index 3209a546d0b..6b2959a6965 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/MetadataAttributePassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/MetadataAttributePassTest.cs @@ -101,16 +101,12 @@ public void Execute_NoNamespaceSet_Noops() }; var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode - { - IsPrimaryNamespace = true, - }; + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true); builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - IsPrimaryClass = true, ClassName = "Test" }; @@ -150,18 +146,14 @@ public void Execute_NoClassNameSet_Noops() }; var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - IsPrimaryNamespace = true, Content = "Some.Namespace" }; builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode - { - IsPrimaryClass = true, - }; + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true); builder.Add(@class); @@ -182,17 +174,15 @@ public void Execute_NoDocumentKind_Noops() var documentNode = new DocumentIntermediateNode(); var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - IsPrimaryNamespace = true, Content = "Some.Namespace" }; builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - IsPrimaryClass = true, ClassName = "Test" }; @@ -219,17 +209,15 @@ public void Execute_NoIdentifier_Noops() }; var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - IsPrimaryNamespace = true, Content = "Some.Namespace" }; builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - IsPrimaryClass = true, ClassName = "Test" }; @@ -256,18 +244,16 @@ public void Execute_HasRequiredInfo_AddsItemAndSourceChecksum() }; var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - IsPrimaryNamespace = true, Content = "Some.Namespace" }; builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - IsPrimaryClass = true, - ClassName = "Test", + ClassName = "Test" }; builder.Add(@class); @@ -304,18 +290,16 @@ public void Execute_HasRequiredInfo_AndImport_AddsItemAndSourceChecksum() }; var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - IsPrimaryNamespace = true, Content = "Some.Namespace" }; builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - IsPrimaryClass = true, - ClassName = "Test", + ClassName = "Test" }; builder.Add(@class); @@ -364,17 +348,15 @@ public void Execute_SuppressMetadataSourceChecksumAttributes_DoesNotGenerateSour }; var builder = IntermediateNodeBuilder.Create(documentNode); - var @namespace = new NamespaceDeclarationIntermediateNode + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true) { - IsPrimaryNamespace = true, Content = "Some.Namespace" }; builder.Push(@namespace); - var @class = new ClassDeclarationIntermediateNode + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true) { - IsPrimaryClass = true, ClassName = "Test" }; diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Intermediate/DocumentIntermediateNodeExtensionsTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Intermediate/DocumentIntermediateNodeExtensionsTest.cs index b9f05bb6701..2ea62de7f10 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Intermediate/DocumentIntermediateNodeExtensionsTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Intermediate/DocumentIntermediateNodeExtensionsTest.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using Xunit; namespace Microsoft.AspNetCore.Razor.Language.Intermediate; @@ -14,10 +12,7 @@ public void FindPrimaryClass_FindsClassWithAnnotation() { // Arrange var document = new DocumentIntermediateNode(); - var @class = new ClassDeclarationIntermediateNode - { - IsPrimaryClass = true - }; + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true); var builder = IntermediateNodeBuilder.Create(document); builder.Add(@class); @@ -34,10 +29,7 @@ public void FindPrimaryMethod_FindsMethodWithAnnotation() { // Arrange var document = new DocumentIntermediateNode(); - var method = new MethodDeclarationIntermediateNode - { - IsPrimaryMethod = true - }; + var method = new MethodDeclarationIntermediateNode(isPrimaryMethod: true); var builder = IntermediateNodeBuilder.Create(document); builder.Add(method); @@ -54,10 +46,7 @@ public void FindPrimaryNamespace_FindsNamespaceWithAnnotation() { // Arrange var document = new DocumentIntermediateNode(); - var @namespace = new NamespaceDeclarationIntermediateNode - { - IsPrimaryNamespace = true - }; + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true); var builder = IntermediateNodeBuilder.Create(document); builder.Add(@namespace); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/CodeRenderingContextExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/CodeRenderingContextExtensions.cs new file mode 100644 index 00000000000..1e637f42477 --- /dev/null +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/CodeRenderingContextExtensions.cs @@ -0,0 +1,460 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using Microsoft.AspNetCore.Razor.Language.Intermediate; +using Microsoft.AspNetCore.Razor.Utilities; +using Microsoft.CodeAnalysis.Text; +using static Microsoft.AspNetCore.Razor.Language.CodeGeneration.CodeWriterExtensions; + +namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration; + +internal static class CodeRenderingContextExtensions +{ + public static void WritePadding(this CodeRenderingContext context, int offset, SourceSpan? span) + { + if (span is not SourceSpan spanValue) + { + return; + } + + var sourceDocument = context.SourceDocument; + + if (sourceDocument.FilePath != null && + !string.Equals(sourceDocument.FilePath, spanValue.FilePath, StringComparison.OrdinalIgnoreCase)) + { + // We don't want to generate padding for nodes from imports. + return; + } + + var basePadding = CalculatePadding(sourceDocument.Text, spanValue); + var resolvedPadding = Math.Max(basePadding - offset, 0); + + context.CodeWriter.Indent(resolvedPadding); + + static int CalculatePadding(SourceText text, SourceSpan span) + { + var spaceCount = 0; + for (var i = span.AbsoluteIndex - 1; i >= 0; i--) + { + var @char = text[i]; + if (@char == '\n' || @char == '\r') + { + break; + } + else + { + // Note that a tab is also replaced with a single space so character indices match. + spaceCount++; + } + } + + return spaceCount; + } + } + + public static CSharpCodeWritingScope BuildNamespace(this CodeRenderingContext context, string? name, SourceSpan? span) + { + var writer = context.CodeWriter; + + if (name.IsNullOrEmpty()) + { + return new CSharpCodeWritingScope(writer, writeBraces: false); + } + + writer.Write("namespace "); + + if (context.Options.DesignTime || span is null) + { + writer.WriteLine(name); + } + else + { + writer.WriteLine(); + using (context.BuildEnhancedLinePragma(span)) + { + writer.WriteLine(name); + } + } + + return new CSharpCodeWritingScope(writer); + } + + public static CSharpCodeWritingScope BuildClassDeclaration( + this CodeRenderingContext context, + ImmutableArray modifiers, + string name, + BaseTypeWithModel? baseType, + ImmutableArray interfaces, + ImmutableArray typeParameters, + bool useNullableContext = false) + { + var writer = context.CodeWriter; + + if (useNullableContext) + { + writer.WriteLine("#nullable restore"); + } + + foreach (var modifier in modifiers) + { + writer.Write(modifier); + writer.Write(" "); + } + + writer.Write("class "); + writer.Write(name); + + if (!typeParameters.IsDefaultOrEmpty) + { + writer.Write("<"); + + var first = true; + + foreach (var typeParameter in typeParameters) + { + if (first) + { + first = false; + } + else + { + writer.Write(","); + } + + if (typeParameter.ParameterNameSource is { } source) + { + WriteWithPragma(context, typeParameter.ParameterName, source); + } + else + { + writer.Write((string)typeParameter.ParameterName); + } + } + + writer.Write(">"); + } + + var hasBaseType = !string.IsNullOrWhiteSpace(baseType?.BaseType.Content); + var hasInterfaces = !interfaces.IsDefaultOrEmpty; + + if (hasBaseType || hasInterfaces) + { + writer.Write(" : "); + + if (hasBaseType) + { + Debug.Assert(baseType != null); + + WriteToken(baseType.BaseType); + WriteOptionalToken(baseType.GreaterThan); + WriteOptionalToken(baseType.ModelType); + WriteOptionalToken(baseType.LessThan); + + if (hasInterfaces) + { + writer.WriteParameterSeparator(); + } + } + + if (hasInterfaces) + { + WriteToken(interfaces[0]); + + for (var i = 1; i < interfaces.Length; i++) + { + writer.Write(", "); + WriteToken(interfaces[i]); + } + } + } + + writer.WriteLine(); + if (typeParameters != null) + { + foreach (var typeParameter in typeParameters) + { + var constraint = typeParameter.Constraints; + if (constraint != null) + { + if (typeParameter.ConstraintsSource is { } source) + { + Debug.Assert(context != null); + WriteWithPragma(context, constraint, source); + } + else + { + writer.Write(constraint); + writer.WriteLine(); + } + } + } + } + + if (useNullableContext) + { + writer.WriteLine("#nullable disable"); + } + + return new CSharpCodeWritingScope(writer); + + void WriteOptionalToken(IntermediateToken? token) + { + if (token is not null) + { + WriteToken(token); + } + } + + void WriteToken(IntermediateToken token) + { + if (token.Source is { } source) + { + WriteWithPragma(context, token.Content, source); + } + else + { + writer.Write(token.Content); + } + } + + static void WriteWithPragma(CodeRenderingContext context, string content, SourceSpan source) + { + var writer = context.CodeWriter; + + if (context.Options.DesignTime) + { + using (context.BuildLinePragma(source)) + { + context.AddSourceMappingFor(source); + writer.Write(content); + } + } + else + { + using (context.BuildEnhancedLinePragma(source)) + { + writer.Write(content); + } + } + } + } + + public static void WritePropertyDeclaration( + this CodeRenderingContext context, + ImmutableArray modifiers, + IntermediateToken type, + string propertyName, + string propertyExpression) + { + context.WritePropertyDeclarationPreamble(modifiers, type.Content, propertyName, type.Source, propertySpan: null); + + var writer = context.CodeWriter; + writer.Write(" => "); + writer.Write(propertyExpression); + writer.WriteLine(";"); + } + + public static void WriteAutoPropertyDeclaration( + this CodeRenderingContext context, + ImmutableArray modifiers, + string typeName, + string propertyName, + SourceSpan? typeSpan = null, + SourceSpan? propertySpan = null, + bool privateSetter = false, + bool defaultValue = false) + { + context.WritePropertyDeclarationPreamble(modifiers, typeName, propertyName, typeSpan, propertySpan); + + var writer = context.CodeWriter; + + writer.Write(" { get;"); + + if (privateSetter) + { + writer.Write(" private"); + } + + writer.Write(" set; }"); + writer.WriteLine(); + + if (defaultValue && context?.Options.SuppressNullabilityEnforcement == false && context?.Options.DesignTime == false) + { + writer.WriteLine(" = default!;"); + } + } + + private static void WritePropertyDeclarationPreamble( + this CodeRenderingContext context, + ImmutableArray modifiers, + string typeName, + string propertyName, + SourceSpan? typeSpan, + SourceSpan? propertySpan) + { + var writer = context.CodeWriter; + + foreach (var modifier in modifiers) + { + writer.Write(modifier); + writer.Write(" "); + } + + WriteToken(context, typeName, typeSpan); + writer.Write(" "); + WriteToken(context, propertyName, propertySpan); + + static void WriteToken(CodeRenderingContext context, string content, SourceSpan? span) + { + var writer = context.CodeWriter; + + if (span is not null && context.Options.DesignTime == false) + { + using (context.BuildEnhancedLinePragma(span)) + { + writer.Write(content); + } + } + else + { + writer.Write(content); + } + } + } + + public static LinePragmaScope BuildLinePragma( + this CodeRenderingContext context, + SourceSpan? span, + bool suppressLineDefaultAndHidden = false) + { + if (string.IsNullOrEmpty(span?.FilePath)) + { + // Can't build a valid line pragma without a file path. + return default; + } + + return new LinePragmaScope(context, span.Value, 0, useEnhancedLinePragma: false, suppressLineDefaultAndHidden); + } + + public static LinePragmaScope BuildEnhancedLinePragma( + this CodeRenderingContext context, + SourceSpan? span, + int characterOffset = 0, + bool suppressLineDefaultAndHidden = false) + { + if (string.IsNullOrEmpty(span?.FilePath)) + { + // Can't build a valid line pragma without a file path. + return default; + } + + return new LinePragmaScope(context, span.Value, characterOffset, useEnhancedLinePragma: true, suppressLineDefaultAndHidden); + } + + public readonly ref struct LinePragmaScope + { + private readonly CodeRenderingContext _context; + private readonly int _startIndent; + private readonly int _startLineIndex; + private readonly SourceSpan _span; + private readonly bool _suppressLineDefaultAndHidden; + + public LinePragmaScope( + CodeRenderingContext context, + SourceSpan span, + int characterOffset, + bool useEnhancedLinePragma = false, + bool suppressLineDefaultAndHidden = false) + { + Debug.Assert(context.Options.DesignTime || useEnhancedLinePragma, "Runtime generation should only use enhanced line pragmas"); + + _context = context; + _suppressLineDefaultAndHidden = suppressLineDefaultAndHidden; + _span = span; + + var writer = context.CodeWriter; + _startIndent = writer.CurrentIndent; + writer.CurrentIndent = 0; + + var endsWithNewline = writer.LastChar is '\n'; + if (!endsWithNewline) + { + writer.WriteLine(); + } + + if (!_context.Options.SuppressNullabilityEnforcement) + { + writer.WriteLine("#nullable restore"); + } + + var ensurePathBackslashes = context.Options.RemapLinePragmaPathsOnWindows && PlatformInformation.IsWindows; + if (useEnhancedLinePragma && _context.Options.UseEnhancedLinePragma) + { + writer.WriteEnhancedLineNumberDirective(span, characterOffset, ensurePathBackslashes); + } + else + { + writer.WriteLineNumberDirective(span, ensurePathBackslashes); + } + + // Capture the line index after writing the #line directive. + _startLineIndex = writer.Location.LineIndex; + + if (useEnhancedLinePragma) + { + // If the caller requested an enhanced line directive, but we fell back to regular ones, write out the extra padding that is required + if (!_context.Options.UseEnhancedLinePragma) + { + context.WritePadding(offset: 0, span); + characterOffset = 0; + } + + context.AddSourceMappingFor(span, characterOffset); + } + } + + public void Dispose() + { + if (_context is null) + { + return; + } + + var writer = _context.CodeWriter; + + // Need to add an additional line at the end IF there wasn't one already written. + // This is needed to work with the C# editor's handling of #line ... + var endsWithNewline = writer.LastChar is '\n'; + + // Always write at least 1 empty line to potentially separate code from pragmas. + writer.WriteLine(); + + // Check if the previous empty line wasn't enough to separate code from pragmas. + if (!endsWithNewline) + { + writer.WriteLine(); + } + + var lineCount = writer.Location.LineIndex - _startLineIndex; + var linePragma = new LinePragma(_span.LineIndex, lineCount, _span.FilePath, _span.EndCharacterIndex, _span.EndCharacterIndex, _span.CharacterIndex); + _context.AddLinePragma(linePragma); + + if (!_suppressLineDefaultAndHidden) + { + writer + .WriteLine("#line default") + .WriteLine("#line hidden"); + } + + if (!_context.Options.SuppressNullabilityEnforcement) + { + writer.WriteLine("#nullable disable"); + } + + writer.CurrentIndent = _startIndent; + } + } +} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/CodeWriterExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/CodeWriterExtensions.cs index be71cb4ce6a..84a9539242e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/CodeWriterExtensions.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/CodeWriterExtensions.cs @@ -5,11 +5,10 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; using System.Linq; -using Microsoft.AspNetCore.Razor.Language.Intermediate; -using Microsoft.AspNetCore.Razor.Utilities; namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration; @@ -38,48 +37,6 @@ public static bool IsAtBeginningOfLine(this CodeWriter writer) return writer.LastChar is '\n'; } - public static CodeWriter WritePadding(this CodeWriter writer, int offset, SourceSpan? span, CodeRenderingContext context) - { - if (span == null) - { - return writer; - } - - if (context.SourceDocument.FilePath != null && - !string.Equals(context.SourceDocument.FilePath, span.Value.FilePath, StringComparison.OrdinalIgnoreCase)) - { - // We don't want to generate padding for nodes from imports. - return writer; - } - - var basePadding = CalculatePadding(); - var resolvedPadding = Math.Max(basePadding - offset, 0); - - writer.Indent(resolvedPadding); - - return writer; - - int CalculatePadding() - { - var spaceCount = 0; - for (var i = span.Value.AbsoluteIndex - 1; i >= 0; i--) - { - var @char = context.SourceDocument.Text[i]; - if (@char == '\n' || @char == '\r') - { - break; - } - else - { - // Note that a tab is also replaced with a single space so character indices match. - spaceCount++; - } - } - - return spaceCount; - } - } - public static CodeWriter WriteVariableDeclaration(this CodeWriter writer, string type, string name, string value) { writer.Write(type).Write(" ").Write(name); @@ -303,35 +260,20 @@ public static CodeWriter WriteStartInstanceMethodInvocation(this CodeWriter writ string.Format(CultureInfo.InvariantCulture, InstanceMethodFormat, instanceName, methodName)); } - public static CodeWriter WriteField(this CodeWriter writer, IList suppressWarnings, IList modifiers, string typeName, string fieldName) + public static CodeWriter WriteField( + this CodeWriter writer, + ImmutableArray suppressWarnings, + ImmutableArray modifiers, + string typeName, + string fieldName) { - if (suppressWarnings == null) - { - throw new ArgumentNullException(nameof(suppressWarnings)); - } - - if (modifiers == null) - { - throw new ArgumentNullException(nameof(modifiers)); - } - - if (typeName == null) - { - throw new ArgumentNullException(nameof(typeName)); - } - - if (fieldName == null) - { - throw new ArgumentNullException(nameof(fieldName)); - } - - for (var i = 0; i < suppressWarnings.Count; i++) + for (var i = 0; i < suppressWarnings.Length; i++) { writer.Write("#pragma warning disable "); writer.WriteLine(suppressWarnings[i]); } - for (var i = 0; i < modifiers.Count; i++) + for (var i = 0; i < modifiers.Length; i++) { writer.Write(modifiers[i]); writer.Write(" "); @@ -343,7 +285,7 @@ public static CodeWriter WriteField(this CodeWriter writer, IList suppre writer.Write(";"); writer.WriteLine(); - for (var i = suppressWarnings.Count - 1; i >= 0; i--) + for (var i = suppressWarnings.Length - 1; i >= 0; i--) { writer.Write("#pragma warning restore "); writer.WriteLine(suppressWarnings[i]); @@ -365,67 +307,6 @@ public static CodeWriter WriteMethodInvocation(this CodeWriter writer, string me .WriteEndMethodInvocation(endLine); } - public static CodeWriter WritePropertyDeclaration(this CodeWriter writer, IList modifiers, IntermediateToken type, string propertyName, string propertyExpression, CodeRenderingContext context) - { - WritePropertyDeclarationPreamble(writer, modifiers, type.Content, propertyName, type.Source, propertySpan: null, context); - writer.Write(" => "); - writer.Write(propertyExpression); - writer.WriteLine(";"); - return writer; - } - - public static CodeWriter WriteAutoPropertyDeclaration(this CodeWriter writer, IList modifiers, string typeName, string propertyName, SourceSpan? typeSpan = null, SourceSpan? propertySpan = null, CodeRenderingContext context = null, bool privateSetter = false, bool defaultValue = false) - { - ArgHelper.ThrowIfNull(modifiers); - ArgHelper.ThrowIfNull(typeName); - ArgHelper.ThrowIfNull(propertyName); - - WritePropertyDeclarationPreamble(writer, modifiers, typeName, propertyName, typeSpan, propertySpan, context); - - writer.Write(" { get;"); - if (privateSetter) - { - writer.Write(" private"); - } - writer.Write(" set; }"); - writer.WriteLine(); - - if (defaultValue && context?.Options.SuppressNullabilityEnforcement == false && context?.Options.DesignTime == false) - { - writer.WriteLine(" = default!;"); - } - - return writer; - } - - private static void WritePropertyDeclarationPreamble(CodeWriter writer, IList modifiers, string typeName, string propertyName, SourceSpan? typeSpan, SourceSpan? propertySpan, CodeRenderingContext context) - { - for (var i = 0; i < modifiers.Count; i++) - { - writer.Write(modifiers[i]); - writer.Write(" "); - } - - WriteToken(writer, typeName, typeSpan, context); - writer.Write(" "); - WriteToken(writer, propertyName, propertySpan, context); - - static void WriteToken(CodeWriter writer, string content, SourceSpan? span, CodeRenderingContext context) - { - if (span is not null && context?.Options.DesignTime == false) - { - using (writer.BuildEnhancedLinePragma(span, context)) - { - writer.Write(content); - } - } - else - { - writer.Write(content); - } - } - } - /// /// Writes an "@" character if the provided identifier needs escaping in c# /// @@ -473,183 +354,6 @@ private static CSharpCodeWritingScope BuildLambda(CodeWriter writer, bool async, return scope; } -#nullable enable - public static CSharpCodeWritingScope BuildNamespace(this CodeWriter writer, string? name, SourceSpan? span, CodeRenderingContext context) - { - if (name.IsNullOrEmpty()) - { - return new CSharpCodeWritingScope(writer, writeBraces: false); - } - - writer.Write("namespace "); - if (context.Options.DesignTime || span is null) - { - writer.WriteLine(name); - } - else - { - writer.WriteLine(); - using (writer.BuildEnhancedLinePragma(span, context)) - { - writer.WriteLine(name); - } - } - return new CSharpCodeWritingScope(writer); - } -#nullable disable - - public static CSharpCodeWritingScope BuildClassDeclaration( - this CodeWriter writer, - IList modifiers, - string name, - BaseTypeWithModel baseType, - IList interfaces, - IList typeParameters, - CodeRenderingContext context, - bool useNullableContext = false) - { - Debug.Assert(context == null || context.CodeWriter == writer); - - if (useNullableContext) - { - writer.WriteLine("#nullable restore"); - } - - for (var i = 0; i < modifiers.Count; i++) - { - writer.Write(modifiers[i]); - writer.Write(" "); - } - - writer.Write("class "); - writer.Write(name); - - if (typeParameters != null && typeParameters.Count > 0) - { - writer.Write("<"); - - for (var i = 0; i < typeParameters.Count; i++) - { - var typeParameter = typeParameters[i]; - if (typeParameter.ParameterNameSource is { } source) - { - WriteWithPragma(writer, typeParameter.ParameterName, context, source); - } - else - { - writer.Write(typeParameter.ParameterName); - } - - // Write ',' between parameters, but not after them - if (i < typeParameters.Count - 1) - { - writer.Write(","); - } - } - - writer.Write(">"); - } - - var hasBaseType = !string.IsNullOrWhiteSpace(baseType?.BaseType.Content); - var hasInterfaces = interfaces != null && interfaces.Count > 0; - - if (hasBaseType || hasInterfaces) - { - writer.Write(" : "); - - if (hasBaseType) - { - WriteToken(baseType.BaseType); - WriteOptionalToken(baseType.GreaterThan); - WriteOptionalToken(baseType.ModelType); - WriteOptionalToken(baseType.LessThan); - - if (hasInterfaces) - { - WriteParameterSeparator(writer); - } - } - - if (hasInterfaces) - { - WriteToken(interfaces[0]); - for (var i = 1; i < interfaces.Count; i++) - { - writer.Write(", "); - WriteToken(interfaces[i]); - } - } - } - - writer.WriteLine(); - if (typeParameters != null) - { - foreach (var typeParameter in typeParameters) - { - var constraint = typeParameter.Constraints; - if (constraint != null) - { - if (typeParameter.ConstraintsSource is { } source) - { - Debug.Assert(context != null); - WriteWithPragma(writer, constraint, context, source); - } - else - { - writer.Write(constraint); - writer.WriteLine(); - } - } - } - } - - if (useNullableContext) - { - writer.WriteLine("#nullable disable"); - } - - return new CSharpCodeWritingScope(writer); - - void WriteOptionalToken(IntermediateToken token) - { - if (token is not null) - { - WriteToken(token); - } - } - - void WriteToken(IntermediateToken token) - { - if (token.Source is { } source) - { - WriteWithPragma(writer, token.Content, context, source); - } - else - { - writer.Write(token.Content); - } - } - - static void WriteWithPragma(CodeWriter writer, string content, CodeRenderingContext context, SourceSpan source) - { - if (context.Options.DesignTime) - { - using (writer.BuildLinePragma(source, context)) - { - context.AddSourceMappingFor(source); - writer.Write(content); - } - } - else - { - using (writer.BuildEnhancedLinePragma(source, context)) - { - writer.Write(content); - } - } - } - } - public static CSharpCodeWritingScope BuildMethodDeclaration( this CodeWriter writer, string accessibility, @@ -669,28 +373,6 @@ public static CSharpCodeWritingScope BuildMethodDeclaration( return new CSharpCodeWritingScope(writer); } - public static IDisposable BuildLinePragma(this CodeWriter writer, SourceSpan? span, CodeRenderingContext context, bool suppressLineDefaultAndHidden = false) - { - if (string.IsNullOrEmpty(span?.FilePath)) - { - // Can't build a valid line pragma without a file path. - return NullDisposable.Default; - } - - return new LinePragmaWriter(writer, span.Value, context, 0, useEnhancedLinePragma: false, suppressLineDefaultAndHidden); - } - - public static IDisposable BuildEnhancedLinePragma(this CodeWriter writer, SourceSpan? span, CodeRenderingContext context, int characterOffset = 0, bool suppressLineDefaultAndHidden = false) - { - if (string.IsNullOrEmpty(span?.FilePath)) - { - // Can't build a valid line pragma without a file path. - return NullDisposable.Default; - } - - return new LinePragmaWriter(writer, span.Value, context, characterOffset, useEnhancedLinePragma: true, suppressLineDefaultAndHidden); - } - private static void WriteVerbatimStringLiteral(CodeWriter writer, ReadOnlyMemory literal) { writer.Write("@\""); @@ -847,121 +529,4 @@ _writer.LastChar is char ch && } } } - - private class LinePragmaWriter : IDisposable - { - private readonly CodeWriter _writer; - private readonly CodeRenderingContext _context; - private readonly int _startIndent; - private readonly int _startLineIndex; - private readonly SourceSpan _span; - private readonly bool _suppressLineDefaultAndHidden; - - public LinePragmaWriter( - CodeWriter writer, - SourceSpan span, - CodeRenderingContext context, - int characterOffset, - bool useEnhancedLinePragma = false, - bool suppressLineDefaultAndHidden = false) - { - Debug.Assert(context.Options.DesignTime || useEnhancedLinePragma, "Runtime generation should only use enhanced line pragmas"); - - if (writer == null) - { - throw new ArgumentNullException(nameof(writer)); - } - - _writer = writer; - _context = context; - _suppressLineDefaultAndHidden = suppressLineDefaultAndHidden; - _startIndent = _writer.CurrentIndent; - _writer.CurrentIndent = 0; - _span = span; - - var endsWithNewline = _writer.LastChar is '\n'; - if (!endsWithNewline) - { - _writer.WriteLine(); - } - - if (!_context.Options.SuppressNullabilityEnforcement) - { - _writer.WriteLine("#nullable restore"); - } - - var ensurePathBackslashes = context.Options.RemapLinePragmaPathsOnWindows && PlatformInformation.IsWindows; - if (useEnhancedLinePragma && _context.Options.UseEnhancedLinePragma) - { - WriteEnhancedLineNumberDirective(writer, span, characterOffset, ensurePathBackslashes); - } - else - { - WriteLineNumberDirective(writer, span, ensurePathBackslashes); - } - - // Capture the line index after writing the #line directive. - _startLineIndex = writer.Location.LineIndex; - - if (useEnhancedLinePragma) - { - // If the caller requested an enhanced line directive, but we fell back to regular ones, write out the extra padding that is required - if (!_context.Options.UseEnhancedLinePragma) - { - context.CodeWriter.WritePadding(0, span, context); - characterOffset = 0; - } - - context.AddSourceMappingFor(span, characterOffset); - } - } - - public void Dispose() - { - // Need to add an additional line at the end IF there wasn't one already written. - // This is needed to work with the C# editor's handling of #line ... - var endsWithNewline = _writer.LastChar is '\n'; - - // Always write at least 1 empty line to potentially separate code from pragmas. - _writer.WriteLine(); - - // Check if the previous empty line wasn't enough to separate code from pragmas. - if (!endsWithNewline) - { - _writer.WriteLine(); - } - - var lineCount = _writer.Location.LineIndex - _startLineIndex; - var linePragma = new LinePragma(_span.LineIndex, lineCount, _span.FilePath, _span.EndCharacterIndex, _span.EndCharacterIndex, _span.CharacterIndex); - _context.AddLinePragma(linePragma); - - if (!_suppressLineDefaultAndHidden) - { - _writer - .WriteLine("#line default") - .WriteLine("#line hidden"); - } - - if (!_context.Options.SuppressNullabilityEnforcement) - { - _writer.WriteLine("#nullable disable"); - } - - _writer.CurrentIndent = _startIndent; - - } - } - - private class NullDisposable : IDisposable - { - public static readonly NullDisposable Default = new NullDisposable(); - - private NullDisposable() - { - } - - public void Dispose() - { - } - } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DefaultDocumentWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DefaultDocumentWriter.cs index 0da17ab69ef..91f4d47d9a9 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DefaultDocumentWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DefaultDocumentWriter.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.Linq; using System.Security.Cryptography; using Microsoft.AspNetCore.Razor.Language.Intermediate; @@ -109,21 +110,21 @@ public override void VisitUsingDirective(UsingDirectiveIntermediateNode node) public override void VisitNamespaceDeclaration(NamespaceDeclarationIntermediateNode node) { - var codeWriter = CodeWriter; - - using (codeWriter.BuildNamespace(node.Content, node.Source, _context)) + using (_context.BuildNamespace(node.Content, node.Source)) { + var writer = CodeWriter; + if (node.Children.OfType().Any()) { // Tooling needs at least one line directive before using directives, otherwise Roslyn will // not offer to create a new one. The last using in the group will output a hidden line // directive after itself. - codeWriter.WriteLine("#line default"); + writer.WriteLine("#line default"); } else { // If there are no using directives, we output the hidden directive here. - codeWriter.WriteLine("#line hidden"); + writer.WriteLine("#line hidden"); } VisitDefault(node); @@ -132,13 +133,14 @@ public override void VisitNamespaceDeclaration(NamespaceDeclarationIntermediateN public override void VisitClassDeclaration(ClassDeclarationIntermediateNode node) { - using (CodeWriter.BuildClassDeclaration( + Debug.Assert(node.ClassName != null); + + using (_context.BuildClassDeclaration( node.Modifiers, node.ClassName, node.BaseType, node.Interfaces, node.TypeParameters, - _context, useNullableContext: !Options.SuppressNullabilityEnforcement && node.NullableContext)) { VisitDefault(node); @@ -151,9 +153,9 @@ public override void VisitMethodDeclaration(MethodDeclarationIntermediateNode no codeWriter.WriteLine("#pragma warning disable 1998"); - for (var i = 0; i < node.Modifiers.Count; i++) + foreach (var modifier in node.Modifiers) { - codeWriter.Write($"{node.Modifiers[i]} "); + codeWriter.Write($"{modifier} "); } codeWriter.Write($"{node.ReturnType} "); @@ -161,10 +163,8 @@ public override void VisitMethodDeclaration(MethodDeclarationIntermediateNode no var isFirst = true; - for (var i = 0; i < node.Parameters.Count; i++) + foreach (var parameter in node.Parameters) { - var parameter = node.Parameters[i]; - if (isFirst) { isFirst = false; @@ -174,9 +174,9 @@ public override void VisitMethodDeclaration(MethodDeclarationIntermediateNode no codeWriter.Write(", "); } - for (var j = 0; j < parameter.Modifiers.Count; j++) + foreach (var modifier in parameter.Modifiers) { - codeWriter.Write($"{parameter.Modifiers[j]} "); + codeWriter.Write($"{modifier} "); } codeWriter.Write($"{parameter.TypeName} {parameter.ParameterName}"); @@ -199,7 +199,7 @@ public override void VisitFieldDeclaration(FieldDeclarationIntermediateNode node public override void VisitPropertyDeclaration(PropertyDeclarationIntermediateNode node) { - CodeWriter.WritePropertyDeclaration(node.Modifiers, node.PropertyType, node.PropertyName, node.PropertyExpression, _context); + _context.WritePropertyDeclaration(node.Modifiers, node.PropertyType, node.PropertyName, node.PropertyExpression); } public override void VisitExtension(ExtensionIntermediateNode node) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs index 87ca77e6600..5d92f50509e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs @@ -13,9 +13,9 @@ public class DesignTimeNodeWriter : IntermediateNodeWriter { public override void WriteUsingDirective(CodeRenderingContext context, UsingDirectiveIntermediateNode node) { - if (node.Source is { FilePath: not null } sourceSpan) + if (node.Source is { FilePath: not null } source) { - using (context.CodeWriter.BuildLinePragma(sourceSpan, context, suppressLineDefaultAndHidden: !node.AppendLineDefaultAndHidden)) + using (context.BuildLinePragma(source, suppressLineDefaultAndHidden: !node.AppendLineDefaultAndHidden)) { context.AddSourceMappingFor(node); context.CodeWriter.WriteUsing(node.Content); @@ -50,81 +50,87 @@ public override void WriteCSharpExpression(CodeRenderingContext context, CSharpE return; } - if (node.Source != null) + var writer = context.CodeWriter; + + if (node.Source is SourceSpan source) { - using (context.CodeWriter.BuildLinePragma(node.Source.Value, context)) + using (context.BuildLinePragma(source)) { var offset = DesignTimeDirectivePass.DesignTimeVariable.Length + " = ".Length; - context.CodeWriter.WritePadding(offset, node.Source, context); - context.CodeWriter.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable); + context.WritePadding(offset, source); + writer.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable); - for (var i = 0; i < node.Children.Count; i++) + foreach (var child in node.Children) { - if (node.Children[i] is IntermediateToken token && token.IsCSharp) + if (child is IntermediateToken { IsCSharp: true } token) { context.AddSourceMappingFor(token); - context.CodeWriter.Write(token.Content); + writer.Write(token.Content); } else { // There may be something else inside the expression like a Template or another extension node. - context.RenderNode(node.Children[i]); + context.RenderNode(child); } } - context.CodeWriter.WriteLine(";"); + writer.WriteLine(";"); } } else { - context.CodeWriter.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable); - for (var i = 0; i < node.Children.Count; i++) + writer.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable); + + foreach (var child in node.Children) { - if (node.Children[i] is IntermediateToken token && token.IsCSharp) + if (child is IntermediateToken { IsCSharp: true } token) { - context.CodeWriter.Write(token.Content); + writer.Write(token.Content); } else { // There may be something else inside the expression like a Template or another extension node. - context.RenderNode(node.Children[i]); + context.RenderNode(child); } } - context.CodeWriter.WriteLine(";"); + + writer.WriteLine(";"); } } public override void WriteCSharpCode(CodeRenderingContext context, CSharpCodeIntermediateNode node) { - IDisposable linePragmaScope = null; - if (node.Source != null) - { - linePragmaScope = context.CodeWriter.BuildLinePragma(node.Source.Value, context); - - context.CodeWriter.WritePadding(0, node.Source.Value, context); - } + var writer = context.CodeWriter; - for (var i = 0; i < node.Children.Count; i++) + if (node.Source is SourceSpan source) { - if (node.Children[i] is IntermediateToken token && token.IsCSharp) + using (context.BuildLinePragma(source)) { - context.AddSourceMappingFor(token); - context.CodeWriter.Write(token.Content); - } - else - { - // There may be something else inside the statement like an extension node. - context.RenderNode(node.Children[i]); + context.WritePadding(offset: 0, source); + RenderChildren(context, node); } } - - if (linePragmaScope != null) + else { - linePragmaScope.Dispose(); + RenderChildren(context, node); + writer.WriteLine(); } - else + + static void RenderChildren(CodeRenderingContext context, CSharpCodeIntermediateNode node) { - context.CodeWriter.WriteLine(); + foreach (var child in node.Children) + { + if (child is IntermediateToken { IsCSharp: true } token) + { + context.AddSourceMappingFor(token); + context.CodeWriter.Write(token.Content); + } + else + { + // There may be something else inside the statement like an extension node. + context.RenderNode(child); + } + } } } @@ -155,35 +161,38 @@ public override void WriteCSharpExpressionAttributeValue(CodeRenderingContext co return; } + var writer = context.CodeWriter; + var firstChild = node.Children[0]; - if (firstChild.Source != null) + if (firstChild.Source is SourceSpan source) { - using (context.CodeWriter.BuildLinePragma(firstChild.Source.Value, context)) + using (context.BuildLinePragma(source)) { var offset = DesignTimeDirectivePass.DesignTimeVariable.Length + " = ".Length; - context.CodeWriter.WritePadding(offset, firstChild.Source, context); - context.CodeWriter.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable); + context.WritePadding(offset, source); + writer.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable); - for (var i = 0; i < node.Children.Count; i++) + foreach (var child in node.Children) { - if (node.Children[i] is IntermediateToken token && token.IsCSharp) + if (child is IntermediateToken { IsCSharp: true } token) { context.AddSourceMappingFor(token); - context.CodeWriter.Write(token.Content); + writer.Write(token.Content); } else { // There may be something else inside the expression like a Template or another extension node. - context.RenderNode(node.Children[i]); + context.RenderNode(child); } } - context.CodeWriter.WriteLine(";"); + writer.WriteLine(";"); } } else { - context.CodeWriter.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable); + writer.WriteStartAssignment(DesignTimeDirectivePass.DesignTimeVariable); + for (var i = 0; i < node.Children.Count; i++) { if (node.Children[i] is IntermediateToken token && token.IsCSharp) @@ -193,7 +202,7 @@ public override void WriteCSharpExpressionAttributeValue(CodeRenderingContext co context.AddSourceMappingFor(token); } - context.CodeWriter.Write(token.Content); + writer.Write(token.Content); } else { @@ -201,27 +210,35 @@ public override void WriteCSharpExpressionAttributeValue(CodeRenderingContext co context.RenderNode(node.Children[i]); } } - context.CodeWriter.WriteLine(";"); + + writer.WriteLine(";"); } } public override void WriteCSharpCodeAttributeValue(CodeRenderingContext context, CSharpCodeAttributeValueIntermediateNode node) { - for (var i = 0; i < node.Children.Count; i++) + foreach (var child in node.Children) { - if (node.Children[i] is IntermediateToken token && token.IsCSharp) + if (child is IntermediateToken { IsCSharp: true } token) { - IDisposable linePragmaScope = null; var isWhitespaceStatement = string.IsNullOrWhiteSpace(token.Content); - if (token.Source != null) + if (token.Source is SourceSpan source) { if (!isWhitespaceStatement) { - linePragmaScope = context.CodeWriter.BuildLinePragma(token.Source.Value, context); + using (context.BuildLinePragma(source)) + { + context.WritePadding(offset: 0, source); + + context.AddSourceMappingFor(token); + context.CodeWriter.Write(token.Content); + } + + continue; } - context.CodeWriter.WritePadding(0, token.Source.Value, context); + context.WritePadding(offset: 0, source); } else if (isWhitespaceStatement) { @@ -231,20 +248,12 @@ public override void WriteCSharpCodeAttributeValue(CodeRenderingContext context, context.AddSourceMappingFor(token); context.CodeWriter.Write(token.Content); - - if (linePragmaScope != null) - { - linePragmaScope.Dispose(); - } - else - { - context.CodeWriter.WriteLine(); - } + context.CodeWriter.WriteLine(); } else { // There may be something else inside the statement like an extension node. - context.RenderNode(node.Children[i]); + context.RenderNode(child); } } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs index d5889429e49..c7909f1097e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs @@ -33,7 +33,7 @@ public override void WriteUsingDirective(CodeRenderingContext context, UsingDire { if (node.Source is { FilePath: not null } sourceSpan) { - using (context.CodeWriter.BuildEnhancedLinePragma(sourceSpan, context, suppressLineDefaultAndHidden: true)) + using (context.BuildEnhancedLinePragma(sourceSpan, suppressLineDefaultAndHidden: true)) { context.CodeWriter.WriteUsing(node.Content, endLine: node.HasExplicitSemicolon); } @@ -105,7 +105,7 @@ private static void WriteCSharpChildren(IntermediateNodeCollection children, Cod { if (children[i] is IntermediateToken token && token.IsCSharp) { - using (context.CodeWriter.BuildEnhancedLinePragma(token.Source, context)) + using (context.BuildEnhancedLinePragma(token.Source)) { context.CodeWriter.Write(token.Content); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDesignTimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDesignTimeNodeWriter.cs index 742fb1eab1a..c0a408f9bc3 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDesignTimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDesignTimeNodeWriter.cs @@ -67,9 +67,9 @@ public override void WriteUsingDirective(CodeRenderingContext context, UsingDire throw new ArgumentNullException(nameof(node)); } - if (node.Source is { FilePath: not null } sourceSpan) + if (node.Source is { FilePath: not null } source) { - using (context.CodeWriter.BuildLinePragma(sourceSpan, context, suppressLineDefaultAndHidden: !node.AppendLineDefaultAndHidden)) + using (context.BuildLinePragma(source, suppressLineDefaultAndHidden: !node.AppendLineDefaultAndHidden)) { context.AddSourceMappingFor(node); context.CodeWriter.WriteUsing(node.Content); @@ -109,9 +109,11 @@ private void WriteCSharpExpressionInnards(CodeRenderingContext context, CSharpEx return; } - if (node.Source != null) + var writer = context.CodeWriter; + + if (node.Source is SourceSpan source) { - using (context.CodeWriter.BuildLinePragma(node.Source.Value, context)) + using (context.BuildLinePragma(source)) { var offset = DesignTimeVariable.Length + " = ".Length; @@ -120,84 +122,80 @@ private void WriteCSharpExpressionInnards(CodeRenderingContext context, CSharpEx offset += type.Length + 2; // two parenthesis } - context.CodeWriter.WritePadding(offset, node.Source, context); - context.CodeWriter.WriteStartAssignment(DesignTimeVariable); + context.WritePadding(offset, source); + writer.WriteStartAssignment(DesignTimeVariable); if (type != null) { - context.CodeWriter.Write("("); - TypeNameHelper.WriteGloballyQualifiedName(context.CodeWriter, type); - context.CodeWriter.Write(")"); + writer.Write("("); + TypeNameHelper.WriteGloballyQualifiedName(writer, type); + writer.Write(")"); } - for (var i = 0; i < node.Children.Count; i++) + foreach (var child in node.Children) { - if (node.Children[i] is IntermediateToken token && token.IsCSharp) + if (child is IntermediateToken { IsCSharp: true } token) { context.AddSourceMappingFor(token); - context.CodeWriter.Write(token.Content); + writer.Write(token.Content); } else { // There may be something else inside the expression like a Template or another extension node. - context.RenderNode(node.Children[i]); + context.RenderNode(child); } } - context.CodeWriter.WriteLine(";"); + writer.WriteLine(";"); } } else { - context.CodeWriter.WriteStartAssignment(DesignTimeVariable); - for (var i = 0; i < node.Children.Count; i++) + writer.WriteStartAssignment(DesignTimeVariable); + + foreach (var child in node.Children) { - if (node.Children[i] is IntermediateToken token && token.IsCSharp) + if (child is IntermediateToken { IsCSharp: true } token) { - context.CodeWriter.Write(token.Content); + writer.Write(token.Content); } else { // There may be something else inside the expression like a Template or another extension node. - context.RenderNode(node.Children[i]); + context.RenderNode(child); } } - context.CodeWriter.WriteLine(";"); + + writer.WriteLine(";"); } } public override void WriteCSharpCode(CodeRenderingContext context, CSharpCodeIntermediateNode node) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - if (node == null) - { - throw new ArgumentNullException(nameof(node)); - } - var isWhitespaceStatement = true; - for (var i = 0; i < node.Children.Count; i++) + foreach (var child in node.Children) { - var token = node.Children[i] as IntermediateToken; - if (token == null || !string.IsNullOrWhiteSpace(token.Content)) + if (child is not IntermediateToken token || !string.IsNullOrWhiteSpace(token.Content)) { isWhitespaceStatement = false; break; } } - IDisposable? linePragmaScope = null; - if (node.Source != null) + if (node.Source is SourceSpan source) { if (!isWhitespaceStatement) { - linePragmaScope = context.CodeWriter.BuildLinePragma(node.Source.Value, context); + using (context.BuildLinePragma(source)) + { + context.WritePadding(offset: 0, source); + RenderChildren(context, node); + } + + return; } - context.CodeWriter.WritePadding(0, node.Source.Value, context); + context.WritePadding(offset: 0, source); } else if (isWhitespaceStatement) { @@ -205,28 +203,25 @@ public override void WriteCSharpCode(CodeRenderingContext context, CSharpCodeInt return; } - for (var i = 0; i < node.Children.Count; i++) + RenderChildren(context, node); + context.CodeWriter.WriteLine(); + + static void RenderChildren(CodeRenderingContext context, CSharpCodeIntermediateNode node) { - if (node.Children[i] is IntermediateToken token && token.IsCSharp) - { - context.AddSourceMappingFor(token); - context.CodeWriter.Write(token.Content); - } - else + foreach (var child in node.Children) { - // There may be something else inside the statement like an extension node. - context.RenderNode(node.Children[i]); + if (child is IntermediateToken { IsCSharp: true } token) + { + context.AddSourceMappingFor(token); + context.CodeWriter.Write(token.Content); + } + else + { + // There may be something else inside the statement like an extension node. + context.RenderNode(child); + } } } - - if (linePragmaScope != null) - { - linePragmaScope.Dispose(); - } - else - { - context.CodeWriter.WriteLine(); - } } public override void WriteHtmlAttribute(CodeRenderingContext context, HtmlAttributeIntermediateNode node) @@ -561,7 +556,7 @@ public override void WriteComponent(CodeRenderingContext context, ComponentInter // the "usings directive is unnecessary" message. // Looks like: // __o = typeof(SomeNamespace.SomeComponent); - using (context.CodeWriter.BuildLinePragma(node.Source.AssumeNotNull(), context)) + using (context.BuildLinePragma(node.Source.AssumeNotNull())) { context.CodeWriter.Write(DesignTimeVariable); context.CodeWriter.Write(" = "); @@ -738,9 +733,9 @@ private void WritePropertyAccess(CodeRenderingContext context, ComponentAttribut context.CodeWriter.Write("."); context.CodeWriter.WriteLine(); - using (context.CodeWriter.BuildLinePragma(attributeSourceSpan, context)) + using (context.BuildLinePragma(attributeSourceSpan)) { - context.CodeWriter.WritePadding(0, attributeSourceSpan, context); + context.WritePadding(offset: 0, attributeSourceSpan); context.CodeWriter.WriteIdentifierEscapeIfNeeded(node.PropertyName); context.AddSourceMappingFor(attributeSourceSpan); context.CodeWriter.WriteLine(node.PropertyName); @@ -1219,7 +1214,7 @@ public override void WriteRenderMode(CodeRenderingContext context, RenderModeInt new IntermediateToken { Kind = TokenKind.CSharp, - Content = $"{DesignTimeVariable} = (global::{ComponentsApi.IComponentRenderMode.FullTypeName})(" + Content = $"{DesignTimeVariable} = (global::{ComponentsApi.IComponentRenderMode.FullTypeName})(" }, new CSharpCodeIntermediateNode { @@ -1242,15 +1237,16 @@ private void WriteCSharpToken(CodeRenderingContext context, IntermediateToken to return; } - if (token.Source?.FilePath == null) + if (token.Source is not SourceSpan source || + source.FilePath == null) { context.CodeWriter.Write(token.Content); return; } - using (context.CodeWriter.BuildLinePragma(token.Source, context)) + using (context.BuildLinePragma(source)) { - context.CodeWriter.WritePadding(0, token.Source.Value, context); + context.WritePadding(offset: 0, source); context.AddSourceMappingFor(token); context.CodeWriter.Write(token.Content); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs index 2a2dbf05f7e..e919ef0fcf3 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.AspNetCore.Razor.Language.CodeGeneration; using Microsoft.AspNetCore.Razor.Language.Intermediate; +using Microsoft.AspNetCore.Razor.PooledObjects; namespace Microsoft.AspNetCore.Razor.Language.Components; @@ -90,9 +91,7 @@ protected override void OnDocumentStructureCreated( @namespace.Content = computedNamespace; @namespace.Source = computedNamespaceSpan; @class.ClassName = computedClass; - @class.Modifiers.Clear(); - @class.Modifiers.Add("public"); - @class.Modifiers.Add("partial"); + @class.UpdateModifiers("public", "partial"); if (codeDocument.FileKind.IsComponentImport()) { @@ -102,10 +101,8 @@ protected override void OnDocumentStructureCreated( method.ReturnType = "void"; method.MethodName = "Execute"; - method.Modifiers.Clear(); - method.Modifiers.Add("protected"); - - method.Parameters.Clear(); + method.UpdateModifiers("protected"); + method.UpdateParameters([]); } else { @@ -117,6 +114,8 @@ protected override void OnDocumentStructureCreated( ? ComponentConstrainedTypeParamDirective.Directive : ComponentTypeParamDirective.Directive; + using var typeParameters = new PooledArrayBuilder(); + foreach (var typeParamReference in documentNode.FindDirectiveReferences(directiveType)) { var typeParamNode = (DirectiveIntermediateNode)typeParamReference.Node; @@ -129,27 +128,22 @@ protected override void OnDocumentStructureCreated( var typeParameter = typeParamNode.Tokens.First(); var constraints = typeParamNode.Tokens.Skip(1).FirstOrDefault(); - @class.TypeParameters.Add(new TypeParameter() - { - ParameterName = typeParameter.Content, - ParameterNameSource = typeParameter.Source, - Constraints = constraints?.Content, - ConstraintsSource = constraints?.Source, - }); + typeParameters.Add(new TypeParameter( + typeParameter.Content, + typeParameter.Source, + constraints?.Content, + constraints?.Source)); } + @class.UpdateTypeParameters(typeParameters.ToImmutableAndClear()); + method.ReturnType = "void"; method.MethodName = ComponentsApi.ComponentBase.BuildRenderTree; - method.Modifiers.Clear(); - method.Modifiers.Add("protected"); - method.Modifiers.Add("override"); + method.UpdateModifiers("protected", "override"); - method.Parameters.Clear(); - method.Parameters.Add(new MethodParameter() - { - ParameterName = ComponentsApi.RenderTreeBuilder.BuilderParameter, - TypeName = $"global::{ComponentsApi.RenderTreeBuilder.FullTypeName}", - }); + method.UpdateParameters(new MethodParameter( + typeName: $"global::{ComponentsApi.RenderTreeBuilder.FullTypeName}", + parameterName: ComponentsApi.RenderTreeBuilder.BuilderParameter)); } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentGenericTypePass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentGenericTypePass.cs index f921bbb6d84..6777aad6468 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentGenericTypePass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentGenericTypePass.cs @@ -459,11 +459,7 @@ private void CreateTypeInferenceMethod(DocumentIntermediateNode documentNode, Co classNode = new ClassDeclarationIntermediateNode() { ClassName = "TypeInference", - Modifiers = - { - "internal", - "static", - }, + Modifiers = ["internal", "static"], }; namespaceNode.Children.Add(classNode); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentInjectIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentInjectIntermediateNode.cs index 3642e635ce1..85d6ebd9673 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentInjectIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentInjectIntermediateNode.cs @@ -4,7 +4,7 @@ #nullable disable using System; -using System.Collections.Generic; +using System.Collections.Immutable; using Microsoft.AspNetCore.Razor.Language.CodeGeneration; using Microsoft.AspNetCore.Razor.Language.Extensions; using Microsoft.AspNetCore.Razor.Language.Intermediate; @@ -13,11 +13,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components; internal class ComponentInjectIntermediateNode : ExtensionIntermediateNode { - private static readonly IList _injectedPropertyModifiers = new[] - { - $"[global::{ComponentsApi.InjectAttribute.FullTypeName}]", - "private" // Encapsulation is the default - }; + private static readonly ImmutableArray s_injectedPropertyModifiers = + [ + $"[global::{ComponentsApi.InjectAttribute.FullTypeName}]", + "private" // Encapsulation is the default + ]; public ComponentInjectIntermediateNode(string typeName, string memberName, SourceSpan? typeSpan, SourceSpan? memberSpan, bool isMalformed) { @@ -26,7 +26,7 @@ public ComponentInjectIntermediateNode(string typeName, string memberName, Sourc TypeSpan = typeSpan; MemberSpan = memberSpan; IsMalformed = isMalformed; - } + } public string TypeName { get; } @@ -65,7 +65,7 @@ public override void WriteNode(CodeTarget target, CodeRenderingContext context) if (TypeName == string.Empty && TypeSpan.HasValue && !context.Options.DesignTime) { // if we don't even have a type name, just emit an empty mapped region so that intellisense still works - context.CodeWriter.BuildEnhancedLinePragma(TypeSpan.Value, context).Dispose(); + using (context.BuildEnhancedLinePragma(TypeSpan.Value)) { } } else { @@ -73,13 +73,12 @@ public override void WriteNode(CodeTarget target, CodeRenderingContext context) if (!context.Options.DesignTime || !IsMalformed) { - context.CodeWriter.WriteAutoPropertyDeclaration( - _injectedPropertyModifiers, + context.WriteAutoPropertyDeclaration( + s_injectedPropertyModifiers, TypeName, memberName, TypeSpan, MemberSpan, - context, defaultValue: true); } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentNodeWriter.cs index e9ea8c00dbb..a29b21dbe3a 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentNodeWriter.cs @@ -366,7 +366,7 @@ protected static void WriteComponentAttributeName(CodeRenderingContext context, { var attributeSourceSpan = (SourceSpan)(attribute.PropertySpan ?? attribute.OriginalAttributeSpan); var requiresEscaping = attribute.PropertyName.IdentifierRequiresEscaping(); - using (context.CodeWriter.BuildEnhancedLinePragma(attributeSourceSpan, context, characterOffset: requiresEscaping ? 1 : 0)) + using (context.BuildEnhancedLinePragma(attributeSourceSpan, characterOffset: requiresEscaping ? 1 : 0)) { context.CodeWriter.WriteIdentifierEscapeIfNeeded(attribute.PropertyName); context.CodeWriter.WriteLine(attribute.PropertyName); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRenderModeDirectivePass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRenderModeDirectivePass.cs index 0c9b0a43c64..483ea2cbed0 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRenderModeDirectivePass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRenderModeDirectivePass.cs @@ -40,9 +40,9 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte { ClassName = GeneratedRenderModeAttributeName, BaseType = new BaseTypeWithModel($"global::{ComponentsApi.RenderModeAttribute.FullTypeName}"), + Modifiers = ["private", "sealed"] }; - classDecl.Modifiers.Add("private"); - classDecl.Modifiers.Add("sealed"); + classDecl.Children.Add(new CSharpCodeIntermediateNode() { Children = diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRuntimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRuntimeNodeWriter.cs index a15963d3a1f..dc230f13a4b 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRuntimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRuntimeNodeWriter.cs @@ -97,7 +97,7 @@ public override void WriteCSharpExpression(CodeRenderingContext context, CSharpE // such as closing parenthesis. // - Error cases: there are no nodes, so we do nothing var firstCSharpChild = node.Children.FirstOrDefault(IsCSharpToken) as IntermediateToken; - using (context.CodeWriter.BuildEnhancedLinePragma(firstCSharpChild?.Source, context, characterOffset: methodInvocation.Length + 2)) + using (context.BuildEnhancedLinePragma(firstCSharpChild?.Source, characterOffset: methodInvocation.Length + 2)) { context.CodeWriter .Write(methodInvocation) @@ -352,7 +352,7 @@ public override void WriteUsingDirective(CodeRenderingContext context, UsingDire if (node.Source is { FilePath: not null } sourceSpan) { - using (context.CodeWriter.BuildEnhancedLinePragma(sourceSpan, context, suppressLineDefaultAndHidden: true)) + using (context.BuildEnhancedLinePragma(sourceSpan, suppressLineDefaultAndHidden: true)) { context.CodeWriter.WriteUsing(node.Content, endLine: node.HasExplicitSemicolon); } @@ -1247,7 +1247,7 @@ private static void WriteCSharpToken(CodeRenderingContext context, IntermediateT return; } - using (context.CodeWriter.BuildEnhancedLinePragma(token.Source, context)) + using (context.BuildEnhancedLinePragma(token.Source)) { context.CodeWriter.Write(token.Content); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/RouteAttributeExtensionNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/RouteAttributeExtensionNode.cs index 1541eb659ad..86d5a400a21 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/RouteAttributeExtensionNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/RouteAttributeExtensionNode.cs @@ -27,7 +27,7 @@ public override void WriteNode(CodeTarget target, CodeRenderingContext context) { context.CodeWriter.WriteLine("("); context.CodeWriter.WriteLine("// language=Route,Component"); - using (context.CodeWriter.BuildEnhancedLinePragma(Source, context)) + using (context.BuildEnhancedLinePragma(Source)) { context.CodeWriter.WriteLine(Template); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DocumentClassifierPassBase.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DocumentClassifierPassBase.cs index 205abb2ba84..a59fbc995c6 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DocumentClassifierPassBase.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DocumentClassifierPassBase.cs @@ -53,20 +53,9 @@ private void Rewrite(RazorCodeDocument codeDocument, DocumentIntermediateNode do var children = new List(documentNode.Children); documentNode.Children.Clear(); - var @namespace = new NamespaceDeclarationIntermediateNode - { - IsPrimaryNamespace = true - }; - - var @class = new ClassDeclarationIntermediateNode - { - IsPrimaryClass = true - }; - - var method = new MethodDeclarationIntermediateNode - { - IsPrimaryMethod = true - }; + var @namespace = new NamespaceDeclarationIntermediateNode(isPrimaryNamespace: true); + var @class = new ClassDeclarationIntermediateNode(isPrimaryClass: true); + var method = new MethodDeclarationIntermediateNode(isPrimaryMethod: true); var documentBuilder = IntermediateNodeBuilder.Create(documentNode); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperOptimizationPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperOptimizationPass.cs index 4d3715d3d50..3fd05ed5bac 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperOptimizationPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperOptimizationPass.cs @@ -191,16 +191,13 @@ private void AddField(Context context, TagHelperDescriptor tagHelper) i++; } - context.Class.Children.Insert(i, new FieldDeclarationIntermediateNode() - { - IsTagHelperField = true, - Modifiers = - { - "private", - }, - FieldName = context.GetFieldName(tagHelper), - FieldType = "global::" + tagHelper.GetTypeName(), - }); + var fieldNode = new FieldDeclarationIntermediateNode( + fieldName: context.GetFieldName(tagHelper), + fieldType: "global::" + tagHelper.GetTypeName(), + modifiers: ["private"], + isTagHelperField: true); + + context.Class.Children.Insert(i, fieldNode); } private bool IsTagHelperRuntimeNode(TagHelperIntermediateNode node) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperTargetExtension.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperTargetExtension.cs index 2df263902ae..f1a1c8a2ee7 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperTargetExtension.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperTargetExtension.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; using System.Linq; @@ -15,11 +16,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions; internal sealed class DefaultTagHelperTargetExtension : IDefaultTagHelperTargetExtension { - private static readonly string[] FieldUnintializedModifiers = { "0649", }; + private static readonly ImmutableArray s_fieldUninitializedWarnings = ["0649"]; - private static readonly string[] FieldUnusedModifiers = { "0169", }; + private static readonly ImmutableArray s_fieldUnusedWarnings = ["0169"]; - private static readonly string[] PrivateModifiers = new string[] { "private" }; + private static readonly ImmutableArray s_privateModifiers = ["private"]; public string RunnerVariableName { get; set; } = "__tagHelperRunner"; @@ -102,7 +103,7 @@ public void WriteTagHelperBody(CodeRenderingContext context, DefaultTagHelperBod // Assign a unique ID for this instance of the source HTML tag. This must be unique // per call site, e.g. if the tag is on the view twice, there should be two IDs. var uniqueId = GetDeterministicId(context); - + context.CodeWriter.WriteStringLiteral(node.TagName) .WriteParameterSeparator() .Write(TagModeTypeName) @@ -374,10 +375,10 @@ public void WriteTagHelperProperty(CodeRenderingContext context, DefaultTagHelpe { if (context.Options.DesignTime) { - var firstMappedChild = node.Children.FirstOrDefault(child => child.Source != null) as IntermediateNode; + var firstMappedChild = node.Children.FirstOrDefault(child => child.Source != null); var valueStart = firstMappedChild?.Source; - using (context.CodeWriter.BuildLinePragma(node.Source, context)) + using (context.BuildLinePragma(node.Source)) { var accessor = GetPropertyAccessor(node); var assignmentPrefixLength = accessor.Length + " = ".Length; @@ -390,7 +391,7 @@ public void WriteTagHelperProperty(CodeRenderingContext context, DefaultTagHelpe if (valueStart != null) { - context.CodeWriter.WritePadding(assignmentPrefixLength, node.Source, context); + context.WritePadding(assignmentPrefixLength, node.Source); } context.CodeWriter @@ -403,7 +404,7 @@ public void WriteTagHelperProperty(CodeRenderingContext context, DefaultTagHelpe { if (valueStart != null) { - context.CodeWriter.WritePadding(assignmentPrefixLength, node.Source, context); + context.WritePadding(assignmentPrefixLength, node.Source); } context.CodeWriter.WriteStartAssignment(GetPropertyAccessor(node)); @@ -474,7 +475,7 @@ public void WriteTagHelperProperty(CodeRenderingContext context, DefaultTagHelpe public void WriteTagHelperRuntime(CodeRenderingContext context, DefaultTagHelperRuntimeIntermediateNode node) { context.CodeWriter.WriteLine("#line hidden"); - context.CodeWriter.WriteField(FieldUnintializedModifiers, PrivateModifiers, ExecutionContextTypeName, ExecutionContextVariableName); + context.CodeWriter.WriteField(s_fieldUninitializedWarnings, s_privateModifiers, ExecutionContextTypeName, ExecutionContextVariableName); context.CodeWriter .Write("private ") @@ -487,7 +488,7 @@ public void WriteTagHelperRuntime(CodeRenderingContext context, DefaultTagHelper if (!context.Options.DesignTime) { - context.CodeWriter.WriteField(FieldUnusedModifiers, PrivateModifiers, "string", StringValueBufferVariableName); + context.CodeWriter.WriteField(s_fieldUnusedWarnings, s_privateModifiers, "string", StringValueBufferVariableName); var backedScopeManageVariableName = "__backed" + ScopeManagerVariableName; context.CodeWriter @@ -571,7 +572,7 @@ internal void RenderTagHelperAttributeInline( } else { - using (context.CodeWriter.BuildEnhancedLinePragma(token.Source, context)) + using (context.BuildEnhancedLinePragma(token.Source)) { context.CodeWriter.Write(token.Content); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DesignTimeDirectiveTargetExtension.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DesignTimeDirectiveTargetExtension.cs index a805dc68422..5c054c85092 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DesignTimeDirectiveTargetExtension.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DesignTimeDirectiveTargetExtension.cs @@ -58,6 +58,7 @@ private void WriteDesignTimeDirectiveToken(CodeRenderingContext context, DesignT .Write("((global::") .Write(typeof(Action).FullName) .Write(")("); + using (context.CodeWriter.BuildLambda()) { var originalIndent = context.CodeWriter.CurrentIndent; @@ -74,7 +75,7 @@ private void WriteDesignTimeDirectiveToken(CodeRenderingContext context, DesignT } // {node.Content} __typeHelper = default({node.Content}); - using (context.CodeWriter.BuildLinePragma(node.Source, context)) + using (context.BuildLinePragma(node.Source)) { context.AddSourceMappingFor(node); context.CodeWriter @@ -90,6 +91,7 @@ private void WriteDesignTimeDirectiveToken(CodeRenderingContext context, DesignT context.CodeWriter.WriteLine(";"); } + break; case DirectiveTokenKind.Member: @@ -108,12 +110,12 @@ private void WriteDesignTimeDirectiveToken(CodeRenderingContext context, DesignT } // global::System.Object {node.content} = null; - using (context.CodeWriter.BuildLinePragma(node.Source, context)) + using (context.BuildLinePragma(node.Source)) { context.CodeWriter - .Write("global::") - .Write(typeof(object).FullName) - .Write(" "); + .Write("global::") + .Write(typeof(object).FullName) + .Write(" "); context.AddSourceMappingFor(node); context.CodeWriter @@ -127,11 +129,10 @@ private void WriteDesignTimeDirectiveToken(CodeRenderingContext context, DesignT context.CodeWriter.WriteLine(";"); } - break; - case DirectiveTokenKind.Namespace - or DirectiveTokenKind.IdentifierOrExpression: + break; + case DirectiveTokenKind.Namespace or DirectiveTokenKind.IdentifierOrExpression: if (string.IsNullOrEmpty(node.Content)) { // This is most likely a marker token. @@ -140,13 +141,13 @@ private void WriteDesignTimeDirectiveToken(CodeRenderingContext context, DesignT } // global::System.Object __typeHelper = nameof({node.Content}); - using (context.CodeWriter.BuildLinePragma(node.Source, context)) + using (context.BuildLinePragma(node.Source)) { context.CodeWriter - .Write("global::") - .Write(typeof(object).FullName) - .Write(" ") - .WriteStartAssignment(TypeHelper); + .Write("global::") + .Write(typeof(object).FullName) + .Write(" ") + .WriteStartAssignment(TypeHelper); context.CodeWriter.Write("nameof("); @@ -155,6 +156,7 @@ private void WriteDesignTimeDirectiveToken(CodeRenderingContext context, DesignT .Write(node.Content) .WriteLine(");"); } + break; case DirectiveTokenKind.String: @@ -168,19 +170,20 @@ private void WriteDesignTimeDirectiveToken(CodeRenderingContext context, DesignT "component.1.0" => "Route,Component", _ => null }; + if (stringSyntax is not null) { context.CodeWriter.Write("// language=").Write(stringSyntax); } // global::System.Object __typeHelper = "{node.Content}"; - using (context.CodeWriter.BuildLinePragma(node.Source, context)) + using (context.BuildLinePragma(node.Source)) { context.CodeWriter - .Write("global::") - .Write(typeof(object).FullName) - .Write(" ") - .WriteStartAssignment(TypeHelper); + .Write("global::") + .Write(typeof(object).FullName) + .Write(" ") + .WriteStartAssignment(TypeHelper); if (node.Content.StartsWith("\"", StringComparison.Ordinal)) { @@ -198,17 +201,18 @@ private void WriteDesignTimeDirectiveToken(CodeRenderingContext context, DesignT context.CodeWriter.WriteLine(";"); } + break; case DirectiveTokenKind.Boolean: // global::System.Boolean __typeHelper = {node.Content}; - using (context.CodeWriter.BuildLinePragma(node.Source, context)) + using (context.BuildLinePragma(node.Source)) { context.CodeWriter - .Write("global::") - .Write(typeof(bool).FullName) - .Write(" ") - .WriteStartAssignment(TypeHelper); + .Write("global::") + .Write(typeof(bool).FullName) + .Write(" ") + .WriteStartAssignment(TypeHelper); context.AddSourceMappingFor(node); context.CodeWriter.Write(node.Content); @@ -231,7 +235,7 @@ private void WriteMarkerToken(CodeRenderingContext context, DirectiveTokenInterm // for consistency so when a C# completion session starts, filling user code doesn't result in // a previously non-existent line pragma from being added and destroying the context in which // the completion session was started. - using (context.CodeWriter.BuildLinePragma(node.Source, context)) + using (context.BuildLinePragma(node.Source)) { context.AddSourceMappingFor(node); context.CodeWriter.Write(" "); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/ImplementsDirectivePass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/ImplementsDirectivePass.cs index c2392f2e76d..98c7353391e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/ImplementsDirectivePass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/ImplementsDirectivePass.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Razor.Language.Intermediate; +using Microsoft.AspNetCore.Razor.PooledObjects; namespace Microsoft.AspNetCore.Razor.Language.Extensions; @@ -17,14 +18,18 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte return; } + using var interfaces = new PooledArrayBuilder(); + foreach (var implements in documentNode.FindDirectiveReferences(ImplementsDirective.Directive)) { var token = ((DirectiveIntermediateNode)implements.Node).Tokens.FirstOrDefault(); if (token != null) { var source = codeDocument.ParserOptions.DesignTime ? null : token.Source; - @class.Interfaces.Add(IntermediateToken.CreateCSharpToken(token.Content, source)); + interfaces.Add(IntermediateToken.CreateCSharpToken(token.Content, source)); } } + + @class.UpdateInterfaces(interfaces.ToImmutableAndClear()); } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/MetadataAttributeTargetExtension.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/MetadataAttributeTargetExtension.cs index 9b8750749a9..5603caae411 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/MetadataAttributeTargetExtension.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/MetadataAttributeTargetExtension.cs @@ -67,7 +67,7 @@ public void WriteRazorCompiledItemMetadataAttribute(CodeRenderingContext context context.CodeWriter.Write("// language="); context.CodeWriter.WriteLine(node.ValueStringSyntax); } - using (context.CodeWriter.BuildEnhancedLinePragma(node.Source, context)) + using (context.BuildEnhancedLinePragma(node.Source)) { context.AddSourceMappingFor(node); context.CodeWriter.WriteStringLiteral(node.Value); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ClassDeclarationIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ClassDeclarationIntermediateNode.cs index 3893129ec6a..9b0fcb0bb33 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ClassDeclarationIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ClassDeclarationIntermediateNode.cs @@ -1,41 +1,58 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System; -using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; namespace Microsoft.AspNetCore.Razor.Language.Intermediate; -public sealed class ClassDeclarationIntermediateNode : MemberDeclarationIntermediateNode +public sealed class ClassDeclarationIntermediateNode(bool isPrimaryClass = false) : MemberDeclarationIntermediateNode { - public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection(); + private IntermediateNodeCollection? _children; + private ImmutableArray _modifiers = []; + private ImmutableArray _interfaces = []; + private ImmutableArray _typeParameters = []; - public IList Modifiers { get; } = new List(); + public bool IsPrimaryClass { get; } = isPrimaryClass; - public string ClassName { get; set; } + public ImmutableArray Modifiers + { + get => _modifiers; + init => _modifiers = value.NullToEmpty(); + } - public BaseTypeWithModel BaseType { get; set; } + public string? ClassName { get; set; } - public IList Interfaces { get; set; } = new List(); + public BaseTypeWithModel? BaseType { get; set; } - public IList TypeParameters { get; set; } = new List(); + public ImmutableArray Interfaces + { + get => _interfaces; + init => _interfaces = value.NullToEmpty(); + } - public bool IsPrimaryClass { get; set; } + public ImmutableArray TypeParameters + { + get => _typeParameters; + init => _typeParameters = value.NullToEmpty(); + } public bool NullableContext { get; set; } - public override void Accept(IntermediateNodeVisitor visitor) - { - if (visitor == null) - { - throw new ArgumentNullException(nameof(visitor)); - } + public override IntermediateNodeCollection Children + => _children ??= []; - visitor.VisitClassDeclaration(this); - } + public void UpdateModifiers(params ImmutableArray modifiers) + => _modifiers = modifiers.NullToEmpty(); + + public void UpdateInterfaces(params ImmutableArray interfaces) + => _interfaces = interfaces.NullToEmpty(); + + public void UpdateTypeParameters(params ImmutableArray typeParameters) + => _typeParameters = typeParameters.NullToEmpty(); + + public override void Accept(IntermediateNodeVisitor visitor) + => visitor.VisitClassDeclaration(this); public override void FormatNode(IntermediateNodeFormatter formatter) { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/FieldDeclarationIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/FieldDeclarationIntermediateNode.cs index 5e19744d793..e4639d26ba8 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/FieldDeclarationIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/FieldDeclarationIntermediateNode.cs @@ -1,37 +1,39 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System; -using System.Collections.Generic; +using System.Collections.Immutable; namespace Microsoft.AspNetCore.Razor.Language.Intermediate; -public sealed class FieldDeclarationIntermediateNode : MemberDeclarationIntermediateNode +public sealed class FieldDeclarationIntermediateNode( + string fieldName, + string fieldType, + ImmutableArray modifiers, + ImmutableArray suppressWarnings, + bool isTagHelperField = false) : MemberDeclarationIntermediateNode { - public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly; - - public IList Modifiers { get; } = new List(); + public string FieldName { get; } = fieldName; + public string FieldType { get; } = fieldType; - public IList SuppressWarnings { get; } = new List(); + public bool IsTagHelperField { get; } = isTagHelperField; - public string FieldName { get; set; } + public ImmutableArray Modifiers { get; } = modifiers.NullToEmpty(); + public ImmutableArray SuppressWarnings { get; } = suppressWarnings.NullToEmpty(); - public string FieldType { get; set; } - - public bool IsTagHelperField { get; set; } + public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly; - public override void Accept(IntermediateNodeVisitor visitor) + public FieldDeclarationIntermediateNode( + string fieldName, + string fieldType, + ImmutableArray modifiers, + bool isTagHelperField = false) + : this(fieldName, fieldType, modifiers, suppressWarnings: [], isTagHelperField) { - if (visitor == null) - { - throw new ArgumentNullException(nameof(visitor)); - } - - visitor.VisitFieldDeclaration(this); } + public override void Accept(IntermediateNodeVisitor visitor) + => visitor.VisitFieldDeclaration(this); + public override void FormatNode(IntermediateNodeFormatter formatter) { formatter.WriteContent(FieldName); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MemberDeclarationIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MemberDeclarationIntermediateNode.cs index 1853f9b98a5..d850d6ed9f2 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MemberDeclarationIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MemberDeclarationIntermediateNode.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - namespace Microsoft.AspNetCore.Razor.Language.Intermediate; public abstract class MemberDeclarationIntermediateNode : IntermediateNode diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MethodDeclarationIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MethodDeclarationIntermediateNode.cs index 6f0782d43ac..e012f61dc41 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MethodDeclarationIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MethodDeclarationIntermediateNode.cs @@ -1,38 +1,47 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System; -using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; -using System.Text; +using Microsoft.AspNetCore.Razor.PooledObjects; namespace Microsoft.AspNetCore.Razor.Language.Intermediate; -public sealed class MethodDeclarationIntermediateNode : MemberDeclarationIntermediateNode +public sealed class MethodDeclarationIntermediateNode(bool isPrimaryMethod = false) : MemberDeclarationIntermediateNode { - public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection(); + private IntermediateNodeCollection? _children; + private ImmutableArray _modifiers = []; + private ImmutableArray _parameters = []; - public IList Modifiers { get; } = new List(); + public bool IsPrimaryMethod { get; } = isPrimaryMethod; - public string MethodName { get; set; } + public ImmutableArray Modifiers + { + get => _modifiers; + init => _modifiers = value.NullToEmpty(); + } - public IList Parameters { get; } = new List(); + public string? MethodName { get; set; } - public string ReturnType { get; set; } + public ImmutableArray Parameters + { + get => _parameters; + init => _parameters = value.NullToEmpty(); + } - public bool IsPrimaryMethod { get; set; } + public string? ReturnType { get; set; } - public override void Accept(IntermediateNodeVisitor visitor) - { - if (visitor == null) - { - throw new ArgumentNullException(nameof(visitor)); - } + public override IntermediateNodeCollection Children + => _children ??= []; - visitor.VisitMethodDeclaration(this); - } + public void UpdateModifiers(params ImmutableArray modifiers) + => _modifiers = modifiers.NullToEmpty(); + + public void UpdateParameters(params ImmutableArray parameters) + => _parameters = parameters.NullToEmpty(); + + public override void Accept(IntermediateNodeVisitor visitor) + => visitor.VisitMethodDeclaration(this); public override void FormatNode(IntermediateNodeFormatter formatter) { @@ -46,10 +55,11 @@ public override void FormatNode(IntermediateNodeFormatter formatter) private static string FormatMethodParameter(MethodParameter parameter) { - var builder = new StringBuilder(); - for (var i = 0; i < parameter.Modifiers.Count; i++) + using var _ = StringBuilderPool.GetPooledObject(out var builder); + + foreach (var modifier in parameter.Modifiers) { - builder.Append(parameter.Modifiers[i]); + builder.Append(modifier); builder.Append(' '); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MethodParameter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MethodParameter.cs index deb9e92ccc4..1186ff5b8ae 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MethodParameter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/MethodParameter.cs @@ -1,17 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System.Collections.Generic; +using System.Collections.Immutable; namespace Microsoft.AspNetCore.Razor.Language.Intermediate; -public sealed class MethodParameter +public sealed class MethodParameter(ImmutableArray modifiers, string typeName, string parameterName) { - public IList Modifiers { get; } = new List(); - - public string TypeName { get; set; } + public ImmutableArray Modifiers { get; } = modifiers.NullToEmpty(); + public string TypeName { get; } = typeName; + public string ParameterName { get; } = parameterName; - public string ParameterName { get; set; } + public MethodParameter(string typeName, string parameterName) + : this(modifiers: [], typeName, parameterName) + { + } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/NamespaceDeclarationIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/NamespaceDeclarationIntermediateNode.cs index 60e52aff390..cc13c5bf0cf 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/NamespaceDeclarationIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/NamespaceDeclarationIntermediateNode.cs @@ -1,31 +1,23 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System; - namespace Microsoft.AspNetCore.Razor.Language.Intermediate; -public sealed class NamespaceDeclarationIntermediateNode : IntermediateNode +public sealed class NamespaceDeclarationIntermediateNode(bool isPrimaryNamespace = false) : IntermediateNode { - public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection(); + private IntermediateNodeCollection? _children; - public string Content { get; set; } + public bool IsPrimaryNamespace { get; } = isPrimaryNamespace; - public bool IsPrimaryNamespace { get; set; } + public string? Content { get; set; } public bool IsGenericTyped { get; set; } - public override void Accept(IntermediateNodeVisitor visitor) - { - if (visitor == null) - { - throw new ArgumentNullException(nameof(visitor)); - } + public override IntermediateNodeCollection Children + => _children ??= []; - visitor.VisitNamespaceDeclaration(this); - } + public override void Accept(IntermediateNodeVisitor visitor) + => visitor.VisitNamespaceDeclaration(this); public override void FormatNode(IntermediateNodeFormatter formatter) { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/PropertyDeclarationIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/PropertyDeclarationIntermediateNode.cs index da0c4ab8e65..97d78db36a2 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/PropertyDeclarationIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/PropertyDeclarationIntermediateNode.cs @@ -1,32 +1,24 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System; -using System.Collections.Generic; +using System.Collections.Immutable; namespace Microsoft.AspNetCore.Razor.Language.Intermediate; -public sealed class PropertyDeclarationIntermediateNode : MemberDeclarationIntermediateNode +public sealed class PropertyDeclarationIntermediateNode( + string propertyName, + IntermediateToken propertyType, + string propertyExpression, + ImmutableArray modifiers) : MemberDeclarationIntermediateNode { - public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly; - - public IList Modifiers { get; } = new List(); + public string PropertyName { get; } = propertyName; + public IntermediateToken PropertyType { get; } = propertyType; + public string PropertyExpression { get; } = propertyExpression; - public string PropertyName { get; set; } + public ImmutableArray Modifiers { get; } = modifiers.NullToEmpty(); - public IntermediateToken PropertyType { get; set; } - - public string PropertyExpression { get; set; } + public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly; public override void Accept(IntermediateNodeVisitor visitor) - { - if (visitor == null) - { - throw new ArgumentNullException(nameof(visitor)); - } - - visitor.VisitPropertyDeclaration(this); - } + => visitor.VisitPropertyDeclaration(this); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TypeParameter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TypeParameter.cs index 80864f59acf..f5128a49c3a 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TypeParameter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TypeParameter.cs @@ -1,14 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - namespace Microsoft.AspNetCore.Razor.Language.Intermediate; -public sealed class TypeParameter +public sealed class TypeParameter( + string parameterName, + SourceSpan? parameterNameSource = null, + string? constraints = null, + SourceSpan? constraintsSource = null) { - public string ParameterName { get; set; } - public SourceSpan? ParameterNameSource { get; init; } - public string Constraints { get; set; } - public SourceSpan? ConstraintsSource { get; init; } + public string ParameterName { get; } = parameterName; + public SourceSpan? ParameterNameSource { get; } = parameterNameSource; + public string? Constraints { get; } = constraints; + public SourceSpan? ConstraintsSource { get; } = constraintsSource; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorProjectEngine.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorProjectEngine.cs index 8d894e17b39..de460558518 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorProjectEngine.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorProjectEngine.cs @@ -387,7 +387,7 @@ private static void AddDefaultFeatures(ImmutableArray.Builder fea configurationFeature.ConfigureClass.Add((document, @class) => { @class.ClassName = "Template"; - @class.Modifiers.Add("public"); + @class.UpdateModifiers("public"); }); configurationFeature.ConfigureNamespace.Add((document, @namespace) => @@ -400,9 +400,7 @@ private static void AddDefaultFeatures(ImmutableArray.Builder fea method.MethodName = "ExecuteAsync"; method.ReturnType = $"global::{typeof(Task).FullName}"; - method.Modifiers.Add("public"); - method.Modifiers.Add("async"); - method.Modifiers.Add("override"); + method.UpdateModifiers("public", "async", "override"); }); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/MvcViewDocumentClassifierPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/MvcViewDocumentClassifierPass.cs index 9b43cd8579b..329a5140a87 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/MvcViewDocumentClassifierPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/MvcViewDocumentClassifierPass.cs @@ -4,7 +4,6 @@ #nullable disable using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.Language.Extensions; using Microsoft.AspNetCore.Razor.Language.Intermediate; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X; @@ -41,14 +40,10 @@ protected override void OnDocumentStructureCreated( } @class.BaseType = new BaseTypeWithModel("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", location: null); - @class.Modifiers.Clear(); - @class.Modifiers.Add("public"); + @class.UpdateModifiers("public"); method.MethodName = "ExecuteAsync"; - method.Modifiers.Clear(); - method.Modifiers.Add("public"); - method.Modifiers.Add("async"); - method.Modifiers.Add("override"); + method.UpdateModifiers("public", "async", "override"); method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}"; } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperTargetExtension.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperTargetExtension.cs index ec066a4d4ae..dd6b0d19fb3 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperTargetExtension.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperTargetExtension.cs @@ -4,6 +4,7 @@ #nullable disable using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; @@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X; internal class ViewComponentTagHelperTargetExtension : IViewComponentTagHelperTargetExtension { - private static readonly string[] PublicModifiers = new[] { "public" }; + private static readonly ImmutableArray s_modifiers = ["public"]; public string TagHelperTypeName { get; set; } = "Microsoft.AspNetCore.Razor.TagHelpers.TagHelper"; @@ -63,13 +64,12 @@ public void WriteViewComponentTagHelper(CodeRenderingContext context, ViewCompon WriteTargetElementString(context.CodeWriter, node.TagHelper); // Initialize declaration. - using (context.CodeWriter.BuildClassDeclaration( - PublicModifiers, + using (context.BuildClassDeclaration( + s_modifiers, node.ClassName, new BaseTypeWithModel(TagHelperTypeName), - interfaces: null, - typeParameters: null, - context)) + interfaces: default, + typeParameters: default)) { // Add view component helper. context.CodeWriter.WriteVariableDeclaration( @@ -78,23 +78,26 @@ public void WriteViewComponentTagHelper(CodeRenderingContext context, ViewCompon value: null); // Add constructor. - WriteConstructorString(context.CodeWriter, node.ClassName); + WriteConstructorString(context, node.ClassName); // Add attributes. - WriteAttributeDeclarations(context.CodeWriter, node.TagHelper); + WriteAttributeDeclarations(context, node.TagHelper); // Add process method. - WriteProcessMethodString(context.CodeWriter, node.TagHelper); + WriteProcessMethodString(context, node.TagHelper); } } - private void WriteConstructorString(CodeWriter writer, string className) + private void WriteConstructorString(CodeRenderingContext context, string className) { + var writer = context.CodeWriter; + writer.Write("public ") .Write(className) .Write("(") .Write($"{ViewComponentHelperTypeName} helper") .WriteLine(")"); + using (writer.BuildScope()) { writer.WriteStartAssignment(ViewComponentHelperVariableName) @@ -103,23 +106,25 @@ private void WriteConstructorString(CodeWriter writer, string className) } } - private void WriteAttributeDeclarations(CodeWriter writer, TagHelperDescriptor tagHelper) + private void WriteAttributeDeclarations(CodeRenderingContext context, TagHelperDescriptor tagHelper) { + var writer = context.CodeWriter; + writer.Write("[") .Write(HtmlAttributeNotBoundAttributeTypeName) .WriteParameterSeparator() .Write(ViewContextAttributeTypeName) .WriteLine("]"); - writer.WriteAutoPropertyDeclaration( - PublicModifiers, + context.WriteAutoPropertyDeclaration( + s_modifiers, ViewContextTypeName, ViewContextPropertyName); foreach (var attribute in tagHelper.BoundAttributes) { - writer.WriteAutoPropertyDeclaration( - PublicModifiers, + context.WriteAutoPropertyDeclaration( + s_modifiers, attribute.TypeName, attribute.GetPropertyName()); @@ -132,17 +137,19 @@ private void WriteAttributeDeclarations(CodeWriter writer, TagHelperDescriptor t } } - private void WriteProcessMethodString(CodeWriter writer, TagHelperDescriptor tagHelper) + private void WriteProcessMethodString(CodeRenderingContext context, TagHelperDescriptor tagHelper) { + var writer = context.CodeWriter; + using (writer.BuildMethodDeclaration( - $"public override async", - $"global::{typeof(Task).FullName}", - TagHelperProcessMethodName, - new Dictionary() - { - { TagHelperContextTypeName, TagHelperContextVariableName }, - { TagHelperOutputTypeName, TagHelperOutputVariableName } - })) + $"public override async", + $"global::{typeof(Task).FullName}", + TagHelperProcessMethodName, + new Dictionary() + { + { TagHelperContextTypeName, TagHelperContextVariableName }, + { TagHelperOutputTypeName, TagHelperOutputVariableName } + })) { writer.WriteInstanceMethodInvocation( $"({ViewComponentHelperVariableName} as {IViewContextAwareTypeName})?", diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/MvcViewDocumentClassifierPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/MvcViewDocumentClassifierPass.cs index c7e4cfe6f3b..2b7fefbea2e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/MvcViewDocumentClassifierPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/MvcViewDocumentClassifierPass.cs @@ -4,7 +4,6 @@ #nullable disable using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.Language.Extensions; using Microsoft.AspNetCore.Razor.Language.Intermediate; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X; @@ -41,14 +40,10 @@ protected override void OnDocumentStructureCreated( } @class.BaseType = new BaseTypeWithModel("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", location: null); - @class.Modifiers.Clear(); - @class.Modifiers.Add("public"); + @class.UpdateModifiers("public"); method.MethodName = "ExecuteAsync"; - method.Modifiers.Clear(); - method.Modifiers.Add("public"); - method.Modifiers.Add("async"); - method.Modifiers.Add("override"); + method.UpdateModifiers("public", "async", "override"); method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}"; } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/RazorPageDocumentClassifierPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/RazorPageDocumentClassifierPass.cs index 1bc2e28cecc..d2cd5a89f9e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/RazorPageDocumentClassifierPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/RazorPageDocumentClassifierPass.cs @@ -70,14 +70,10 @@ protected override void OnDocumentStructureCreated( @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath); } - @class.Modifiers.Clear(); - @class.Modifiers.Add("public"); + @class.UpdateModifiers("public"); method.MethodName = "ExecuteAsync"; - method.Modifiers.Clear(); - method.Modifiers.Add("public"); - method.Modifiers.Add("async"); - method.Modifiers.Add("override"); + method.UpdateModifiers("public", "async", "override"); method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}"; var document = codeDocument.GetRequiredDocumentNode(); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperTargetExtension.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperTargetExtension.cs index 3d8f4117fdd..98abe9c6d2f 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperTargetExtension.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperTargetExtension.cs @@ -4,6 +4,7 @@ #nullable disable using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; @@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X; internal class ViewComponentTagHelperTargetExtension : IViewComponentTagHelperTargetExtension { - private static readonly string[] PublicModifiers = new[] { "public" }; + private static readonly ImmutableArray s_modifiers = ["public"]; public string TagHelperTypeName { get; set; } = "Microsoft.AspNetCore.Razor.TagHelpers.TagHelper"; @@ -63,13 +64,12 @@ public void WriteViewComponentTagHelper(CodeRenderingContext context, ViewCompon WriteTargetElementString(context.CodeWriter, node.TagHelper); // Initialize declaration. - using (context.CodeWriter.BuildClassDeclaration( - PublicModifiers, + using (context.BuildClassDeclaration( + s_modifiers, node.ClassName, new BaseTypeWithModel(TagHelperTypeName), - interfaces: null, - typeParameters: null, - context)) + interfaces: default, + typeParameters: default)) { // Add view component helper. context.CodeWriter.WriteVariableDeclaration( @@ -78,23 +78,26 @@ public void WriteViewComponentTagHelper(CodeRenderingContext context, ViewCompon value: null); // Add constructor. - WriteConstructorString(context.CodeWriter, node.ClassName); + WriteConstructorString(context, node.ClassName); // Add attributes. - WriteAttributeDeclarations(context.CodeWriter, node.TagHelper); + WriteAttributeDeclarations(context, node.TagHelper); // Add process method. - WriteProcessMethodString(context.CodeWriter, node.TagHelper); + WriteProcessMethodString(context, node.TagHelper); } } - private void WriteConstructorString(CodeWriter writer, string className) + private void WriteConstructorString(CodeRenderingContext context, string className) { + var writer = context.CodeWriter; + writer.Write("public ") .Write(className) .Write("(") .Write($"{ViewComponentHelperTypeName} helper") .WriteLine(")"); + using (writer.BuildScope()) { writer.WriteStartAssignment(ViewComponentHelperVariableName) @@ -103,23 +106,25 @@ private void WriteConstructorString(CodeWriter writer, string className) } } - private void WriteAttributeDeclarations(CodeWriter writer, TagHelperDescriptor tagHelper) + private void WriteAttributeDeclarations(CodeRenderingContext context, TagHelperDescriptor tagHelper) { + var writer = context.CodeWriter; + writer.Write("[") .Write(HtmlAttributeNotBoundAttributeTypeName) .WriteParameterSeparator() .Write(ViewContextAttributeTypeName) .WriteLine("]"); - writer.WriteAutoPropertyDeclaration( - PublicModifiers, + context.WriteAutoPropertyDeclaration( + s_modifiers, ViewContextTypeName, ViewContextPropertyName); foreach (var attribute in tagHelper.BoundAttributes) { - writer.WriteAutoPropertyDeclaration( - PublicModifiers, + context.WriteAutoPropertyDeclaration( + s_modifiers, attribute.TypeName, attribute.GetPropertyName()); @@ -132,17 +137,19 @@ private void WriteAttributeDeclarations(CodeWriter writer, TagHelperDescriptor t } } - private void WriteProcessMethodString(CodeWriter writer, TagHelperDescriptor tagHelper) + private void WriteProcessMethodString(CodeRenderingContext context, TagHelperDescriptor tagHelper) { + var writer = context.CodeWriter; + using (writer.BuildMethodDeclaration( - $"public override async", - $"global::{typeof(Task).FullName}", - TagHelperProcessMethodName, - new Dictionary() - { - { TagHelperContextTypeName, TagHelperContextVariableName }, - { TagHelperOutputTypeName, TagHelperOutputVariableName } - })) + $"public override async", + $"global::{typeof(Task).FullName}", + TagHelperProcessMethodName, + new Dictionary() + { + { TagHelperContextTypeName, TagHelperContextVariableName }, + { TagHelperOutputTypeName, TagHelperOutputVariableName } + })) { writer.WriteInstanceMethodInvocation( $"({ViewComponentHelperVariableName} as {IViewContextAwareTypeName})?", diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/InjectTargetExtension.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/InjectTargetExtension.cs index 0ea1c5ac9b0..2caf1cdebd6 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/InjectTargetExtension.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/InjectTargetExtension.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.CodeGeneration; using Microsoft.AspNetCore.Razor.Language.Extensions; @@ -30,13 +31,13 @@ public void WriteInjectProperty(CodeRenderingContext context, InjectIntermediate if (node.TypeName == "") { // if we don't even have a type name, just emit an empty mapped region so that intellisense still works - context.CodeWriter.BuildEnhancedLinePragma(node.TypeSource.Value, context).Dispose(); + using (context.BuildEnhancedLinePragma(node.TypeSource.Value)) { } } else { context.CodeWriter.WriteLine(RazorInjectAttribute); var memberName = node.MemberName ?? "Member_" + DefaultTagHelperTargetExtension.GetDeterministicId(context); - context.CodeWriter.WriteAutoPropertyDeclaration(["public"], node.TypeName, memberName, node.TypeSource, node.MemberSource, context, privateSetter: true, defaultValue: true); + context.WriteAutoPropertyDeclaration(["public"], node.TypeName, memberName, node.TypeSource, node.MemberSource, privateSetter: true, defaultValue: true); } } else if (!node.IsMalformed) @@ -47,9 +48,9 @@ public void WriteInjectProperty(CodeRenderingContext context, InjectIntermediate property += " = default!;"; } - if (node.Source.HasValue) + if (node.Source is SourceSpan source) { - using (context.CodeWriter.BuildLinePragma(node.Source.Value, context)) + using (context.BuildLinePragma(source)) { WriteProperty(); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/MvcViewDocumentClassifierPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/MvcViewDocumentClassifierPass.cs index 59cdcb43df4..3058bc26708 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/MvcViewDocumentClassifierPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/MvcViewDocumentClassifierPass.cs @@ -54,24 +54,20 @@ protected override void OnDocumentStructureCreated( @class.ClassName = className; } @class.BaseType = new BaseTypeWithModel("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", location: null); - @class.Modifiers.Clear(); + if (_useConsolidatedMvcViews) { - @class.Modifiers.Add("internal"); - @class.Modifiers.Add("sealed"); + @class.UpdateModifiers("internal", "sealed"); } else { - @class.Modifiers.Add("public"); + @class.UpdateModifiers("public"); } @class.NullableContext = true; method.MethodName = "ExecuteAsync"; - method.Modifiers.Clear(); - method.Modifiers.Add("public"); - method.Modifiers.Add("async"); - method.Modifiers.Add("override"); + method.UpdateModifiers("public", "async", "override"); method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}"; } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/PagesPropertyInjectionPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/PagesPropertyInjectionPass.cs index d9d8d63f6b6..eec27f59334 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/PagesPropertyInjectionPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/PagesPropertyInjectionPass.cs @@ -48,13 +48,13 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte } else { - @class.Children.Add(new PropertyDeclarationIntermediateNode() - { - Modifiers = { "public" }, - PropertyName = "Model", - PropertyType = modelType, - PropertyExpression = "ViewData.Model" - }); + var propertyNode = new PropertyDeclarationIntermediateNode( + propertyName: "Model", + propertyType: modelType, + propertyExpression: "ViewData.Model", + modifiers: ["public"]); + + @class.Children.Add(propertyNode); } static string nullableEnable(bool nullableEnabled, string code) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/RazorPageDocumentClassifierPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/RazorPageDocumentClassifierPass.cs index 3955f3ef2a7..0fe813afc27 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/RazorPageDocumentClassifierPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/RazorPageDocumentClassifierPass.cs @@ -84,24 +84,20 @@ protected override void OnDocumentStructureCreated( } @class.BaseType = new BaseTypeWithModel("global::Microsoft.AspNetCore.Mvc.RazorPages.Page"); - @class.Modifiers.Clear(); + if (_useConsolidatedMvcViews) { - @class.Modifiers.Add("internal"); - @class.Modifiers.Add("sealed"); + @class.UpdateModifiers("internal", "sealed"); } else { - @class.Modifiers.Add("public"); + @class.UpdateModifiers("public"); } @class.NullableContext = true; method.MethodName = "ExecuteAsync"; - method.Modifiers.Clear(); - method.Modifiers.Add("public"); - method.Modifiers.Add("async"); - method.Modifiers.Add("override"); + method.UpdateModifiers("public", "async", "override"); method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}"; var document = codeDocument.GetRequiredDocumentNode(); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperPass.cs index 146af9ca9d0..f878337054a 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperPass.cs @@ -123,16 +123,13 @@ private void AddField(Context context, TagHelperDescriptor tagHelper) i++; } - context.Class.Children.Insert(i, new FieldDeclarationIntermediateNode() - { - IsTagHelperField = true, - Modifiers = - { - "private", - }, - FieldName = context.GetFieldName(tagHelper), - FieldType = "global::" + context.GetFullyQualifiedName(tagHelper), - }); + var fieldNode = new FieldDeclarationIntermediateNode( + fieldName: context.GetFieldName(tagHelper), + fieldType: "global::" + context.GetFullyQualifiedName(tagHelper), + modifiers: ["private"], + isTagHelperField: true); + + context.Class.Children.Insert(i, fieldNode); } private void AddTagHelperClass(Context context, TagHelperDescriptor tagHelper) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperTargetExtension.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperTargetExtension.cs index 212bdd87437..fd8d315bfd4 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperTargetExtension.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperTargetExtension.cs @@ -4,6 +4,7 @@ #nullable disable using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; @@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions; internal class ViewComponentTagHelperTargetExtension : IViewComponentTagHelperTargetExtension { - private static readonly string[] PublicModifiers = new[] { "public" }; + private static readonly ImmutableArray s_modifiers = ["public"]; public string TagHelperTypeName { get; set; } = "Microsoft.AspNetCore.Razor.TagHelpers.TagHelper"; @@ -66,13 +67,12 @@ public void WriteViewComponentTagHelper(CodeRenderingContext context, ViewCompon WriteTargetElementString(context.CodeWriter, node.TagHelper); // Initialize declaration. - using (context.CodeWriter.BuildClassDeclaration( - PublicModifiers, + using (context.BuildClassDeclaration( + s_modifiers, node.ClassName, new BaseTypeWithModel(TagHelperTypeName), - interfaces: null, - typeParameters: null, - context)) + interfaces: default, + typeParameters: default)) { // Add view component helper. context.CodeWriter.WriteVariableDeclaration( @@ -81,13 +81,13 @@ public void WriteViewComponentTagHelper(CodeRenderingContext context, ViewCompon value: null); // Add constructor. - WriteConstructorString(context.CodeWriter, node.ClassName); + WriteConstructorString(context, node.ClassName); // Add attributes. - WriteAttributeDeclarations(context.CodeWriter, node.TagHelper); + WriteAttributeDeclarations(context, node.TagHelper); // Add process method. - WriteProcessMethodString(context.CodeWriter, node.TagHelper); + WriteProcessMethodString(context, node.TagHelper); // We pre-process the arguments passed to `InvokeAsync` to ensure that the // provided markup attributes (in kebab-case) are matched to the associated @@ -96,13 +96,16 @@ public void WriteViewComponentTagHelper(CodeRenderingContext context, ViewCompon } } - private void WriteConstructorString(CodeWriter writer, string className) + private void WriteConstructorString(CodeRenderingContext context, string className) { + var writer = context.CodeWriter; + writer.Write("public ") .Write(className) .Write("(") .Write($"{ViewComponentHelperTypeName} helper") .WriteLine(")"); + using (writer.BuildScope()) { writer.WriteStartAssignment(ViewComponentHelperVariableName) @@ -111,23 +114,25 @@ private void WriteConstructorString(CodeWriter writer, string className) } } - private void WriteAttributeDeclarations(CodeWriter writer, TagHelperDescriptor tagHelper) + private void WriteAttributeDeclarations(CodeRenderingContext context, TagHelperDescriptor tagHelper) { + var writer = context.CodeWriter; + writer.Write("[") .Write(HtmlAttributeNotBoundAttributeTypeName) .WriteParameterSeparator() .Write(ViewContextAttributeTypeName) .WriteLine("]"); - writer.WriteAutoPropertyDeclaration( - PublicModifiers, + context.WriteAutoPropertyDeclaration( + s_modifiers, ViewContextTypeName, ViewContextPropertyName); foreach (var attribute in tagHelper.BoundAttributes) { - writer.WriteAutoPropertyDeclaration( - PublicModifiers, + context.WriteAutoPropertyDeclaration( + s_modifiers, attribute.TypeName, attribute.GetPropertyName()); @@ -140,17 +145,19 @@ private void WriteAttributeDeclarations(CodeWriter writer, TagHelperDescriptor t } } - private void WriteProcessMethodString(CodeWriter writer, TagHelperDescriptor tagHelper) + private void WriteProcessMethodString(CodeRenderingContext context, TagHelperDescriptor tagHelper) { + var writer = context.CodeWriter; + using (writer.BuildMethodDeclaration( - $"public override async", - $"global::{typeof(Task).FullName}", - TagHelperProcessMethodName, - new Dictionary() - { - { TagHelperContextTypeName, TagHelperContextVariableName }, - { TagHelperOutputTypeName, TagHelperOutputVariableName } - })) + $"public override async", + $"global::{typeof(Task).FullName}", + TagHelperProcessMethodName, + new Dictionary() + { + { TagHelperContextTypeName, TagHelperContextVariableName }, + { TagHelperOutputTypeName, TagHelperOutputVariableName } + })) { writer.WriteInstanceMethodInvocation( $"({ViewComponentHelperVariableName} as {IViewContextAwareTypeName})?", diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/CodeGeneration/TestCodeRenderingContext.cs b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/CodeGeneration/TestCodeRenderingContext.cs index 87293cb7e11..8891749d572 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/CodeGeneration/TestCodeRenderingContext.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/CodeGeneration/TestCodeRenderingContext.cs @@ -1,19 +1,33 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using Microsoft.AspNetCore.Razor.Language.Intermediate; namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration; public static class TestCodeRenderingContext { + public static CodeRenderingContext Create( + RazorCodeGenerationOptions? options = null, + RazorSourceDocument? source = null, + IntermediateNodeWriter? nodeWriter = null) + { + options ??= RazorCodeGenerationOptions.Default; + source ??= TestRazorSourceDocument.Create(); + nodeWriter ??= new RuntimeNodeWriter(); + + var documentNode = new DocumentIntermediateNode(); + var context = new CodeRenderingContext(nodeWriter, source, documentNode, options); + context.SetVisitor(new RenderChildrenVisitor(context.CodeWriter)); + + return context; + } + public static CodeRenderingContext CreateDesignTime( - string newLineString = null, - string suppressUniqueIds = "test", - RazorSourceDocument source = null, - IntermediateNodeWriter nodeWriter = null) + string? newLineString = null, + string? suppressUniqueIds = "test", + RazorSourceDocument? source = null, + IntermediateNodeWriter? nodeWriter = null) { nodeWriter ??= new RuntimeNodeWriter(); source ??= TestRazorSourceDocument.Create(); @@ -28,10 +42,10 @@ public static CodeRenderingContext CreateDesignTime( } public static CodeRenderingContext CreateRuntime( - string newLineString = null, - string suppressUniqueIds = "test", - RazorSourceDocument source = null, - IntermediateNodeWriter nodeWriter = null) + string? newLineString = null, + string? suppressUniqueIds = "test", + RazorSourceDocument? source = null, + IntermediateNodeWriter? nodeWriter = null) { nodeWriter ??= new RuntimeNodeWriter(); source ??= TestRazorSourceDocument.Create(); @@ -45,7 +59,7 @@ public static CodeRenderingContext CreateRuntime( return context; } - private static RazorCodeGenerationOptions ConfigureOptions(RazorCodeGenerationOptions options, string newLine, string suppressUniqueIds) + private static RazorCodeGenerationOptions ConfigureOptions(RazorCodeGenerationOptions options, string? newLine, string? suppressUniqueIds) { if (newLine is null && suppressUniqueIds is null) { diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs index f32f3f31020..ae041474218 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs @@ -48,7 +48,7 @@ public override void VisitClassDeclaration(ClassDeclarationIntermediateNode node }; // Avoid adding the type parameters to the baseline if they aren't present. - if (node.TypeParameters != null && node.TypeParameters.Count > 0) + if (node.TypeParameters.Length > 0) { entries.Add(string.Join(", ", node.TypeParameters.Select(p => p.ParameterName))); } diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/RazorProjectEngineBuilderExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/RazorProjectEngineBuilderExtensions.cs index 7cce113f859..7bfc0c1abd5 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/RazorProjectEngineBuilderExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/RazorProjectEngineBuilderExtensions.cs @@ -7,7 +7,6 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Language.Intermediate; using Microsoft.AspNetCore.Razor.PooledObjects; namespace Microsoft.AspNetCore.Razor.Language; @@ -61,23 +60,20 @@ public static RazorProjectEngineBuilder ConfigureDocumentClassifier(this RazorPr feature.ConfigureClass.Clear(); feature.ConfigureMethod.Clear(); - feature.ConfigureNamespace.Add((RazorCodeDocument codeDocument, NamespaceDeclarationIntermediateNode node) => + feature.ConfigureNamespace.Add((codeDocument, node) => { node.Content = "Microsoft.AspNetCore.Razor.Language.IntegrationTests.TestFiles"; }); - feature.ConfigureClass.Add((RazorCodeDocument codeDocument, ClassDeclarationIntermediateNode node) => + feature.ConfigureClass.Add((codeDocument, node) => { node.ClassName = testFileName.Replace('/', '_'); - node.Modifiers.Clear(); - node.Modifiers.Add("public"); + node.UpdateModifiers("public"); }); - feature.ConfigureMethod.Add((RazorCodeDocument codeDocument, MethodDeclarationIntermediateNode node) => + feature.ConfigureMethod.Add((codeDocument, node) => { - node.Modifiers.Clear(); - node.Modifiers.Add("public"); - node.Modifiers.Add("async"); + node.UpdateModifiers("public", "async"); node.MethodName = "ExecuteAsync"; node.ReturnType = typeof(Task).FullName; });