This fix adds missing line mappings for the Blazor runtime code
generation. We had the correct line mappings for design time code, but
they were missing in this case for runtime code - where they are used
for error messages and debugging (not yet supported).

Once this fix is is in the error window and output log will report the
file/line/column of the original source in .cshtml.

It looks like jumping to the code from the error window is currently not
working correctly in VS. It works from the output window.

I'm going to follow up on the VS issue in the Razor repo, since the fix
won't come from the Blazor side.
This commit is contained in:
Ryan Nowak 2018-04-03 15:14:18 -07:00
parent d097190824
commit 0c942ccc76
4 changed files with 116 additions and 4 deletions

View File

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

View File

@ -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(@"
// <auto-generated/>
#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
{
}

View File

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

View File

@ -49,6 +49,7 @@ namespace Test
protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder)
{
base.BuildRenderTree(builder);
builder.OpenComponent<Test.MyComponent>(0);
builder.CloseComponent();
}
@ -110,6 +111,7 @@ namespace Test
protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder)
{
base.BuildRenderTree(builder);
builder.OpenComponent<Test.MyComponent>(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<Test.MyComponent>(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<Test.MyComponent>(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<Test.MyComponent>(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<Test.MyComponent>(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<Test.MyComponent>(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<Test.MyComponent>(0);
builder.CloseComponent();
}