DefaultRazorIRLoweringPhase is too agressive
This change fixes a bug where DefaultRazorIRLoweringPhase is too
aggressive in merging HTML spans. You can hit the bug by delimiting two
html spans with a metacode character like:
<foo>@{ <bar/> }</foo>
The lowering phase will combine these HTML nodes, which is invalid as they
don't have contiguous spans.
The change here is to merge spans only when they both have an invalid
location or are contiguous.
This commit is contained in:
parent
7a1a6dd1d6
commit
e35ee53ee5
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
|
|
@ -176,16 +177,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
_builder.Pop();
|
_builder.Pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SourceSpan BuildSourceSpanFromNode(SyntaxTreeNode node)
|
protected SourceSpan? BuildSourceSpanFromNode(SyntaxTreeNode node)
|
||||||
{
|
{
|
||||||
var location = node.Start;
|
var location = node.Start;
|
||||||
var sourceRange = new SourceSpan(
|
if (location == SourceLocation.Undefined)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var span = new SourceSpan(
|
||||||
node.Start.FilePath ?? Filename,
|
node.Start.FilePath ?? Filename,
|
||||||
node.Start.AbsoluteIndex,
|
node.Start.AbsoluteIndex,
|
||||||
node.Start.LineIndex,
|
node.Start.LineIndex,
|
||||||
node.Start.CharacterIndex,
|
node.Start.CharacterIndex,
|
||||||
node.Length);
|
node.Length);
|
||||||
return sourceRange;
|
return span;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,7 +233,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MainSourceVisitor : LoweringVisitor
|
private class MainSourceVisitor : LoweringVisitor
|
||||||
{
|
{
|
||||||
private DeclareTagHelperFieldsIRNode _tagHelperFields;
|
private DeclareTagHelperFieldsIRNode _tagHelperFields;
|
||||||
|
|
@ -399,27 +405,43 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
if (currentChildren.Count > 0 && currentChildren[currentChildren.Count - 1] is HtmlContentIRNode)
|
if (currentChildren.Count > 0 && currentChildren[currentChildren.Count - 1] is HtmlContentIRNode)
|
||||||
{
|
{
|
||||||
var existingHtmlContent = (HtmlContentIRNode)currentChildren[currentChildren.Count - 1];
|
var existingHtmlContent = (HtmlContentIRNode)currentChildren[currentChildren.Count - 1];
|
||||||
existingHtmlContent.Content = string.Concat(existingHtmlContent.Content, span.Content);
|
|
||||||
|
|
||||||
if (existingHtmlContent.Source != null)
|
var source = BuildSourceSpanFromNode(span);
|
||||||
|
if (existingHtmlContent.Source == null && source == null)
|
||||||
{
|
{
|
||||||
var contentLength = existingHtmlContent.Source.Value.Length + span.Content.Length;
|
Combine(existingHtmlContent, span);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
existingHtmlContent.Source = new SourceSpan(
|
if (source != null &&
|
||||||
existingHtmlContent.Source.Value.FilePath ?? Filename,
|
existingHtmlContent.Source != null &&
|
||||||
existingHtmlContent.Source.Value.AbsoluteIndex,
|
existingHtmlContent.Source.Value.FilePath == source.Value.FilePath &&
|
||||||
existingHtmlContent.Source.Value.LineIndex,
|
existingHtmlContent.Source.Value.AbsoluteIndex + existingHtmlContent.Source.Value.Length == source.Value.AbsoluteIndex)
|
||||||
existingHtmlContent.Source.Value.CharacterIndex,
|
{
|
||||||
contentLength);
|
Combine(existingHtmlContent, span);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
_builder.Add(new HtmlContentIRNode()
|
||||||
{
|
{
|
||||||
_builder.Add(new HtmlContentIRNode()
|
Content = span.Content,
|
||||||
{
|
Source = BuildSourceSpanFromNode(span),
|
||||||
Content = span.Content,
|
});
|
||||||
Source = BuildSourceSpanFromNode(span),
|
}
|
||||||
});
|
private void Combine(HtmlContentIRNode node, Span span)
|
||||||
|
{
|
||||||
|
node.Content = node.Content + span.Content;
|
||||||
|
if (node.Source != null)
|
||||||
|
{
|
||||||
|
Debug.Assert(node.Source.Value.FilePath != null);
|
||||||
|
|
||||||
|
node.Source = new SourceSpan(
|
||||||
|
node.Source.Value.FilePath,
|
||||||
|
node.Source.Value.AbsoluteIndex,
|
||||||
|
node.Source.Value.LineIndex,
|
||||||
|
node.Source.Value.CharacterIndex,
|
||||||
|
node.Content.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
|
||||||
|
|
||||||
#line default
|
#line default
|
||||||
#line hidden
|
#line hidden
|
||||||
WriteLiteral("</p>\r\n <p>Basic Asynchronous Statement Nested: <b>");
|
WriteLiteral("</p>\r\n <p>Basic Asynchronous Statement Nested: ");
|
||||||
|
WriteLiteral(" <b>");
|
||||||
#line 13 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/Await.cshtml"
|
#line 13 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/Await.cshtml"
|
||||||
Write(await Foo());
|
Write(await Foo());
|
||||||
|
|
||||||
|
|
@ -70,7 +71,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
|
||||||
|
|
||||||
#line default
|
#line default
|
||||||
#line hidden
|
#line hidden
|
||||||
WriteLiteral("</p>\r\n <p>Advanced Asynchronous Statement Nested: <b>");
|
WriteLiteral("</p>\r\n <p>Advanced Asynchronous Statement Nested: ");
|
||||||
|
WriteLiteral(" <b>");
|
||||||
#line 24 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/Await.cshtml"
|
#line 24 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/Await.cshtml"
|
||||||
Write(await Foo(boolValue: false));
|
Write(await Foo(boolValue: false));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,18 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
|
||||||
#pragma warning disable 1998
|
#pragma warning disable 1998
|
||||||
public async System.Threading.Tasks.Task ExecuteAsync()
|
public async System.Threading.Tasks.Task ExecuteAsync()
|
||||||
{
|
{
|
||||||
WriteLiteral("\r\n<div class=\"randomNonTagHelperAttribute\">\r\n <p class=\"Hello World\" ");
|
WriteLiteral("\r\n<");
|
||||||
|
WriteLiteral("div class=\"randomNonTagHelperAttribute\">\r\n <");
|
||||||
|
WriteLiteral("p class=\"Hello World\" ");
|
||||||
#line 4 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EscapedTagHelpers.cshtml"
|
#line 4 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EscapedTagHelpers.cshtml"
|
||||||
Write(DateTime.Now);
|
Write(DateTime.Now);
|
||||||
|
|
||||||
#line default
|
#line default
|
||||||
#line hidden
|
#line hidden
|
||||||
WriteLiteral(">\r\n <input type=\"text\" />\r\n <em>Not a TagHelper: </em> ");
|
WriteLiteral(">\r\n <");
|
||||||
|
WriteLiteral("input type=\"text\" />\r\n <");
|
||||||
|
WriteLiteral("em>Not a TagHelper: </");
|
||||||
|
WriteLiteral("em> ");
|
||||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", global::Microsoft.AspNetCore.Razor.TagHelpers.TagMode.SelfClosing, "test", async() => {
|
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", global::Microsoft.AspNetCore.Razor.TagHelpers.TagMode.SelfClosing, "test", async() => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -66,7 +71,9 @@ __TestNamespace_InputTagHelper2.Checked = true;
|
||||||
}
|
}
|
||||||
Write(__tagHelperExecutionContext.Output);
|
Write(__tagHelperExecutionContext.Output);
|
||||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||||
WriteLiteral("\r\n </p>\r\n</div>");
|
WriteLiteral("\r\n </");
|
||||||
|
WriteLiteral("p>\r\n</");
|
||||||
|
WriteLiteral("div>");
|
||||||
}
|
}
|
||||||
#pragma warning restore 1998
|
#pragma warning restore 1998
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
|
||||||
|
|
||||||
#line default
|
#line default
|
||||||
#line hidden
|
#line hidden
|
||||||
WriteLiteral(" Hello, World\r\n <p>Hello, World</p>\r\n");
|
WriteLiteral(" ");
|
||||||
|
WriteLiteral("Hello, World\r\n <p>Hello, World</p>\r\n");
|
||||||
WriteLiteral("\r\n");
|
WriteLiteral("\r\n");
|
||||||
#line 8 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/Instrumented.cshtml"
|
#line 8 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/Instrumented.cshtml"
|
||||||
while(i <= 10) {
|
while(i <= 10) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
|
||||||
#pragma warning disable 1998
|
#pragma warning disable 1998
|
||||||
public async System.Threading.Tasks.Task ExecuteAsync()
|
public async System.Threading.Tasks.Task ExecuteAsync()
|
||||||
{
|
{
|
||||||
WriteLiteral("\r\n<p>This should be shown</p>\r\n\r\n");
|
WriteLiteral("\r\n<p>This should ");
|
||||||
|
WriteLiteral(" be shown</p>\r\n\r\n");
|
||||||
#line 5 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/RazorComments.cshtml"
|
#line 5 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/RazorComments.cshtml"
|
||||||
|
|
||||||
Exception foo =
|
Exception foo =
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,8 @@ WriteTo(__razor_template_writer, item);
|
||||||
|
|
||||||
#line default
|
#line default
|
||||||
#line hidden
|
#line hidden
|
||||||
WriteLiteralTo(__razor_template_writer, " <ul>\r\n <li>Child Items... ?</li>\r\n </ul>\r\n </li>");
|
WriteLiteralTo(__razor_template_writer, " <ul>\r\n <li>Child Items... ?</li>\r\n");
|
||||||
|
WriteLiteralTo(__razor_template_writer, " </ul>\r\n </li>");
|
||||||
}
|
}
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue