diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorRuntimeNodeWriter.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorRuntimeNodeWriter.cs index a544c10bed..f4b72488b5 100644 --- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorRuntimeNodeWriter.cs +++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorRuntimeNodeWriter.cs @@ -50,6 +50,16 @@ namespace Microsoft.AspNetCore.Blazor.Razor 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++) { @@ -63,14 +73,25 @@ namespace Microsoft.AspNetCore.Blazor.Razor if (isWhitespaceStatement) { + // The runtime and design time code differ in their handling of whitespace-only + // statements. At runtime we can discard them completely. At design time we need + // to keep them for the editor. return; } + IDisposable linePragmaScope = null; + if (node.Source != null) + { + linePragmaScope = context.CodeWriter.BuildLinePragma(node.Source.Value); + context.CodeWriter.WritePadding(0, node.Source.Value, context); + } + for (var i = 0; i < node.Children.Count; i++) { if (node.Children[i] is IntermediateToken token && token.IsCSharp) { _scopeStack.IncrementCurrentScopeChildCount(context); + context.AddSourceMappingFor(token); context.CodeWriter.Write(token.Content); } else @@ -79,6 +100,15 @@ namespace Microsoft.AspNetCore.Blazor.Razor context.RenderNode(node.Children[i]); } } + + if (linePragmaScope != null) + { + linePragmaScope.Dispose(); + } + else + { + context.CodeWriter.WriteLine(); + } } public override void WriteCSharpCodeAttributeValue(CodeRenderingContext context, CSharpCodeAttributeValueIntermediateNode node) diff --git a/test/Microsoft.AspNetCore.Blazor.Build.Test/DeclarationRazorIntegrationTest.cs b/test/Microsoft.AspNetCore.Blazor.Build.Test/DeclarationRazorIntegrationTest.cs index 555b41f670..e2d29f2e5b 100644 --- a/test/Microsoft.AspNetCore.Blazor.Build.Test/DeclarationRazorIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.Build.Test/DeclarationRazorIntegrationTest.cs @@ -100,6 +100,62 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test Assert.Empty(frames); } + [Fact] // Regression test for https://github.com/aspnet/Blazor/issues/453 + public void DeclarationConfiguration_FunctionsBlockHasLineMappings_MappingsApplyToError() + { + // Arrange & Act 1 + var generated = CompileToCSharp(@" +@functions { + public StringBuilder Builder { get; set; } +} +"); + + // Assert 1 + AssertSourceEquals(@" +// +#pragma warning disable 1591 +#pragma warning disable 0414 +#pragma warning disable 0649 +#pragma warning disable 0169 + +namespace Test +{ + #line hidden + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + public class TestComponent : Microsoft.AspNetCore.Blazor.Components.BlazorComponent + { + #pragma warning disable 1998 + protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder) + { + } + #pragma warning restore 1998 +#line 1 ""x:\dir\subdir\Test\TestComponent.cshtml"" + + public StringBuilder Builder { get; set; } + +#line default +#line hidden + } +} +#pragma warning restore 1591 +", generated); + + // Act 2 + var assembly = CompileToAssembly(generated, throwOnFailure: false); + + // Assert 2 + var diagnostic = Assert.Single(assembly.Diagnostics); + + // This error should map to line 2 of the generated file, the test + // says 1 because Roslyn's line/column data structures are 0-based. + var position = diagnostic.Location.GetMappedLineSpan(); + Assert.EndsWith(".cshtml", position.Path); + Assert.Equal(1, position.StartLinePosition.Line); + } + public class BaseClass : BlazorComponent { } diff --git a/test/Microsoft.AspNetCore.Blazor.Build.Test/RazorIntegrationTestBase.cs b/test/Microsoft.AspNetCore.Blazor.Build.Test/RazorIntegrationTestBase.cs index 97b5e3e513..975d7d97c5 100644 --- a/test/Microsoft.AspNetCore.Blazor.Build.Test/RazorIntegrationTestBase.cs +++ b/test/Microsoft.AspNetCore.Blazor.Build.Test/RazorIntegrationTestBase.cs @@ -208,7 +208,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test return CompileToAssembly(cSharpResult); } - protected CompileToAssemblyResult CompileToAssembly(CompileToCSharpResult cSharpResult) + protected CompileToAssemblyResult CompileToAssembly(CompileToCSharpResult cSharpResult, bool throwOnFailure = true) { if (cSharpResult.Diagnostics.Any()) { @@ -227,10 +227,18 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test .GetDiagnostics() .Where(d => d.Severity != DiagnosticSeverity.Hidden); - if (diagnostics.Any()) + if (diagnostics.Any() && throwOnFailure) { throw new CompilationFailedException(compilation); } + else if (diagnostics.Any()) + { + return new CompileToAssemblyResult + { + Compilation = compilation, + Diagnostics = diagnostics, + }; + } using (var peStream = new MemoryStream()) { diff --git a/test/Microsoft.AspNetCore.Blazor.Build.Test/RuntimeCodeGenerationRazorIntegrationTest.cs b/test/Microsoft.AspNetCore.Blazor.Build.Test/RuntimeCodeGenerationRazorIntegrationTest.cs index db989dac4c..b6d346e3cc 100644 --- a/test/Microsoft.AspNetCore.Blazor.Build.Test/RuntimeCodeGenerationRazorIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.Build.Test/RuntimeCodeGenerationRazorIntegrationTest.cs @@ -49,6 +49,7 @@ namespace Test protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder) { base.BuildRenderTree(builder); + builder.OpenComponent(0); builder.CloseComponent(); } @@ -110,6 +111,7 @@ namespace Test protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder) { base.BuildRenderTree(builder); + builder.OpenComponent(0); builder.AddAttribute(1, ""IntProperty"", 123); builder.AddAttribute(2, ""BoolProperty"", true); @@ -164,6 +166,7 @@ namespace Test protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder) { base.BuildRenderTree(builder); + builder.OpenComponent(0); builder.AddAttribute(1, ""StringProperty"", 42.ToString()); builder.CloseComponent(); @@ -214,6 +217,7 @@ namespace Test protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder) { base.BuildRenderTree(builder); + builder.OpenComponent(0); builder.AddAttribute(1, ""some-attribute"", ""foo""); builder.AddAttribute(2, ""another-attribute"", 43.ToString()); @@ -276,20 +280,26 @@ namespace Test protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder) { base.BuildRenderTree(builder); + builder.OpenComponent(0); builder.AddAttribute(1, ""OnClick"", new Microsoft.AspNetCore.Blazor.UIEventHandler(e => { Increment(); })); builder.CloseComponent(); builder.AddContent(2, ""\n\n""); } #pragma warning restore 1998 - +#line 4 ""x:\dir\subdir\Test\TestComponent.cshtml"" + private int counter; private void Increment() { counter++; } + +#line default +#line hidden } } #pragma warning restore 1591 + ", generated); } @@ -344,20 +354,26 @@ namespace Test protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder) { base.BuildRenderTree(builder); + builder.OpenComponent(0); builder.AddAttribute(1, ""OnClick"", new Microsoft.AspNetCore.Blazor.UIEventHandler(Increment)); builder.CloseComponent(); builder.AddContent(2, ""\n\n""); } #pragma warning restore 1998 - +#line 5 ""x:\dir\subdir\Test\TestComponent.cshtml"" + private int counter; private void Increment(UIEventArgs e) { counter++; } + +#line default +#line hidden } } #pragma warning restore 1591 + ", generated); } @@ -404,6 +420,7 @@ namespace Test protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder) { base.BuildRenderTree(builder); + builder.OpenComponent(0); builder.AddAttribute(1, ""MyAttr"", ""abc""); builder.AddAttribute(2, ""ChildContent"", (Microsoft.AspNetCore.Blazor.RenderFragment)((builder2) => { @@ -466,6 +483,7 @@ namespace Test protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder) { base.BuildRenderTree(builder); + builder.OpenComponent(0); builder.CloseComponent(); }