\n\nCommit migrated from a2d1ac4cea
This commit is contained in:
Ajay Bhargav Baaskaran 2018-12-04 16:04:20 -08:00 committed by GitHub
parent ebd37599b6
commit ad0f2a5af4
15 changed files with 674 additions and 181 deletions

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Razor.Language
{ {
internal class ClassifiedSpanRewriter : SyntaxRewriter internal class ClassifiedSpanRewriter : SyntaxRewriter
{ {
public override SyntaxNode VisitMarkupTagBlock(MarkupTagBlockSyntax node) public override SyntaxNode VisitMarkupStartTag(MarkupStartTagSyntax node)
{ {
SpanContext latestSpanContext = null; SpanContext latestSpanContext = null;
var newChildren = SyntaxListBuilder<RazorSyntaxNode>.Create(); var newChildren = SyntaxListBuilder<RazorSyntaxNode>.Create();
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Razor.Language
AddLiteralIfExists(); AddLiteralIfExists();
return SyntaxFactory.MarkupTagBlock(newChildren.ToList()).Green.CreateRed(node.Parent, node.Position); return SyntaxFactory.MarkupStartTag(newChildren.ToList()).Green.CreateRed(node.Parent, node.Position);
void AddLiteralIfExists() void AddLiteralIfExists()
{ {

View File

@ -106,9 +106,14 @@ namespace Microsoft.AspNetCore.Razor.Language
base.VisitMarkupTagHelperAttributeValue(node); base.VisitMarkupTagHelperAttributeValue(node);
} }
public override void VisitMarkupTagBlock(MarkupTagBlockSyntax node) public override void VisitMarkupStartTag(MarkupStartTagSyntax node)
{ {
WriteBlock(node, BlockKindInternal.Tag, base.VisitMarkupTagBlock); WriteBlock(node, BlockKindInternal.Tag, base.VisitMarkupStartTag);
}
public override void VisitMarkupEndTag(MarkupEndTagSyntax node)
{
WriteBlock(node, BlockKindInternal.Tag, base.VisitMarkupEndTag);
} }
public override void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) public override void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node)

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using Microsoft.AspNetCore.Razor.Language.Syntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy namespace Microsoft.AspNetCore.Razor.Language.Legacy
{ {
@ -41,7 +42,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var diagnostics = context.ErrorSink.Errors; var diagnostics = context.ErrorSink.Errors;
var root = markupParser.ParseDocument().CreateRed(); var root = markupParser.ParseDocument().CreateRed();
return RazorSyntaxTree.Create(root, source, diagnostics, Options);
var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, Options);
// Group markup elements
syntaxTree = MarkupElementRewriter.AddMarkupElements(syntaxTree);
return syntaxTree;
} }
} }
} }

View File

@ -10,13 +10,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{ {
internal static class TagHelperBlockRewriter internal static class TagHelperBlockRewriter
{ {
private static readonly string StringTypeName = typeof(string).FullName;
public static MarkupTagHelperStartTagSyntax Rewrite( public static MarkupTagHelperStartTagSyntax Rewrite(
string tagName, string tagName,
bool validStructure, bool validStructure,
RazorParserFeatureFlags featureFlags, RazorParserFeatureFlags featureFlags,
MarkupTagBlockSyntax tag, MarkupStartTagSyntax tag,
TagHelperBinding bindingResult, TagHelperBinding bindingResult,
ErrorSink errorSink, ErrorSink errorSink,
RazorSourceDocument source) RazorSourceDocument source)
@ -28,7 +26,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
} }
public static TagMode GetTagMode( public static TagMode GetTagMode(
MarkupTagBlockSyntax tagBlock, MarkupStartTagSyntax tagBlock,
TagHelperBinding bindingResult, TagHelperBinding bindingResult,
ErrorSink errorSink) ErrorSink errorSink)
{ {
@ -57,7 +55,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private static SyntaxList<RazorSyntaxNode> GetRewrittenChildren( private static SyntaxList<RazorSyntaxNode> GetRewrittenChildren(
string tagName, string tagName,
bool validStructure, bool validStructure,
MarkupTagBlockSyntax tagBlock, MarkupStartTagSyntax tagBlock,
TagHelperBinding bindingResult, TagHelperBinding bindingResult,
RazorParserFeatureFlags featureFlags, RazorParserFeatureFlags featureFlags,
ErrorSink errorSink, ErrorSink errorSink,

View File

@ -15,7 +15,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public static RazorSyntaxTree Rewrite(RazorSyntaxTree syntaxTree, string tagHelperPrefix, IEnumerable<TagHelperDescriptor> descriptors) public static RazorSyntaxTree Rewrite(RazorSyntaxTree syntaxTree, string tagHelperPrefix, IEnumerable<TagHelperDescriptor> descriptors)
{ {
var errorSink = new ErrorSink(); var errorSink = new ErrorSink();
syntaxTree = MarkupElementRewriter.AddMarkupElements(syntaxTree);
var rewriter = new Rewriter( var rewriter = new Rewriter(
syntaxTree.Source, syntaxTree.Source,
@ -33,8 +32,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var diagnostics = CombineErrors(syntaxTree.Diagnostics, errorList).OrderBy(error => error.Span.AbsoluteIndex); var diagnostics = CombineErrors(syntaxTree.Diagnostics, errorList).OrderBy(error => error.Span.AbsoluteIndex);
var newSyntaxTree = RazorSyntaxTree.Create(rewritten, syntaxTree.Source, diagnostics, syntaxTree.Options); var newSyntaxTree = RazorSyntaxTree.Create(rewritten, syntaxTree.Source, diagnostics, syntaxTree.Options);
newSyntaxTree = MarkupElementRewriter.RemoveMarkupElements(newSyntaxTree);
return newSyntaxTree; return newSyntaxTree;
} }
@ -103,7 +100,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
TagHelperInfo tagHelperInfo = null; TagHelperInfo tagHelperInfo = null;
// Visit the start tag. // Visit the start tag.
var startTag = (MarkupTagBlockSyntax)Visit(node.StartTag); var startTag = (MarkupStartTagSyntax)Visit(node.StartTag);
if (startTag != null) if (startTag != null)
{ {
var tagName = startTag.GetTagName(); var tagName = startTag.GetTagName();
@ -145,7 +142,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
else else
{ {
// Non-TagHelper tag. // Non-TagHelper tag.
ValidateParentAllowsPlainTag(startTag); ValidateParentAllowsPlainStartTag(startTag);
if (!startTag.IsSelfClosing() && !startTag.IsVoidElement()) if (!startTag.IsSelfClosing() && !startTag.IsVoidElement())
{ {
@ -159,7 +156,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var body = VisitList(node.Body); var body = VisitList(node.Body);
// Visit end tag. // Visit end tag.
var endTag = (MarkupTagBlockSyntax)Visit(node.EndTag); var endTag = (MarkupEndTagSyntax)Visit(node.EndTag);
if (endTag != null) if (endTag != null)
{ {
var tagName = endTag.GetTagName(); var tagName = endTag.GetTagName();
@ -181,9 +178,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{ {
// Standalone end tag. We may need to error if it is not supposed to be here. // Standalone end tag. We may need to error if it is not supposed to be here.
// If there was a corresponding start tag, we would have already added this error. // If there was a corresponding start tag, we would have already added this error.
ValidateParentAllowsPlainTag(endTag); ValidateParentAllowsPlainEndTag(endTag);
} }
else if (!endTag.IsVoidElement()) else
{ {
// Since a start tag exists, we must already be tracking it. // Since a start tag exists, we must already be tracking it.
// Pop the stack as we're done with the end tag. // Pop the stack as we're done with the end tag.
@ -205,7 +202,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public override SyntaxNode VisitMarkupTextLiteral(MarkupTextLiteralSyntax node) public override SyntaxNode VisitMarkupTextLiteral(MarkupTextLiteralSyntax node)
{ {
var tagParent = node.FirstAncestorOrSelf<SyntaxNode>(n => n is MarkupTagBlockSyntax); var tagParent = node.FirstAncestorOrSelf<SyntaxNode>(n => n is MarkupStartTagSyntax || n is MarkupEndTagSyntax);
var isPartofTagBlock = tagParent != null; var isPartofTagBlock = tagParent != null;
if (!isPartofTagBlock) if (!isPartofTagBlock)
{ {
@ -215,13 +212,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return base.VisitMarkupTextLiteral(node); return base.VisitMarkupTextLiteral(node);
} }
private bool TryRewriteTagHelperStart(MarkupTagBlockSyntax tagBlock, out MarkupTagHelperStartTagSyntax rewritten, out TagHelperInfo tagHelperInfo) private bool TryRewriteTagHelperStart(MarkupStartTagSyntax startTag, out MarkupTagHelperStartTagSyntax rewritten, out TagHelperInfo tagHelperInfo)
{ {
rewritten = null; rewritten = null;
tagHelperInfo = null; tagHelperInfo = null;
// Get tag name of the current block // Get tag name of the current block
var tagName = tagBlock.GetTagName(); var tagName = startTag.GetTagName();
// Could not determine tag name, it can't be a TagHelper, continue on and track the element. // Could not determine tag name, it can't be a TagHelper, continue on and track the element.
if (tagName == null) if (tagName == null)
@ -231,7 +228,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
TagHelperBinding tagHelperBinding; TagHelperBinding tagHelperBinding;
if (!IsPotentialTagHelper(tagName, tagBlock)) if (!IsPotentialTagHelperStart(tagName, startTag))
{ {
return false; return false;
} }
@ -240,7 +237,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var tagNameScope = tracker?.TagName ?? string.Empty; var tagNameScope = tracker?.TagName ?? string.Empty;
// We're now in a start tag block, we first need to see if the tag block is a tag helper. // We're now in a start tag block, we first need to see if the tag block is a tag helper.
var elementAttributes = GetAttributeNameValuePairs(tagBlock); var elementAttributes = GetAttributeNameValuePairs(startTag);
tagHelperBinding = _tagHelperBinder.GetBinding( tagHelperBinding = _tagHelperBinder.GetBinding(
tagName, tagName,
@ -264,29 +261,29 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return false; return false;
} }
ValidateParentAllowsTagHelper(tagName, tagBlock); ValidateParentAllowsTagHelper(tagName, startTag);
ValidateBinding(tagHelperBinding, tagName, tagBlock); ValidateBinding(tagHelperBinding, tagName, startTag);
// We're in a start TagHelper block. // We're in a start TagHelper block.
var validTagStructure = ValidateTagSyntax(tagName, tagBlock); var validTagStructure = ValidateStartTagSyntax(tagName, startTag);
var startTag = TagHelperBlockRewriter.Rewrite( var rewrittenStartTag = TagHelperBlockRewriter.Rewrite(
tagName, tagName,
validTagStructure, validTagStructure,
_featureFlags, _featureFlags,
tagBlock, startTag,
tagHelperBinding, tagHelperBinding,
_errorSink, _errorSink,
_source); _source);
var tagMode = TagHelperBlockRewriter.GetTagMode(tagBlock, tagHelperBinding, _errorSink); var tagMode = TagHelperBlockRewriter.GetTagMode(startTag, tagHelperBinding, _errorSink);
tagHelperInfo = new TagHelperInfo(tagName, tagMode, tagHelperBinding); tagHelperInfo = new TagHelperInfo(tagName, tagMode, tagHelperBinding);
rewritten = startTag; rewritten = rewrittenStartTag;
return true; return true;
} }
private bool TryRewriteTagHelperEnd(MarkupTagBlockSyntax tagBlock, out MarkupTagHelperEndTagSyntax rewritten) private bool TryRewriteTagHelperEnd(MarkupEndTagSyntax tagBlock, out MarkupTagHelperEndTagSyntax rewritten)
{ {
rewritten = null; rewritten = null;
var tagName = tagBlock.GetTagName(); var tagName = tagBlock.GetTagName();
@ -298,7 +295,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var tracker = CurrentTagHelperTracker; var tracker = CurrentTagHelperTracker;
var tagNameScope = tracker?.TagName ?? string.Empty; var tagNameScope = tracker?.TagName ?? string.Empty;
if (!IsPotentialTagHelper(tagName, tagBlock)) if (!IsPotentialTagHelperEnd(tagName, tagBlock))
{ {
return false; return false;
} }
@ -315,7 +312,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return false; return false;
} }
ValidateTagSyntax(tagName, tagBlock); ValidateEndTagSyntax(tagName, tagBlock);
_trackerStack.Pop(); _trackerStack.Pop();
} }
@ -360,10 +357,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
} }
// Internal for testing // Internal for testing
internal IReadOnlyList<KeyValuePair<string, string>> GetAttributeNameValuePairs(MarkupTagBlockSyntax tagBlock) internal IReadOnlyList<KeyValuePair<string, string>> GetAttributeNameValuePairs(MarkupStartTagSyntax tagBlock)
{ {
// Need to calculate how many children we should take that represent the attributes. // Need to calculate how many children we should take that represent the attributes.
var childrenOffset = IsPartialTag(tagBlock) ? 0 : 1; var childrenOffset = IsPartialStartTag(tagBlock) ? 0 : 1;
var childCount = tagBlock.Children.Count - childrenOffset; var childCount = tagBlock.Children.Count - childrenOffset;
if (childCount <= 1) if (childCount <= 1)
@ -436,19 +433,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return attributes; return attributes;
} }
private void ValidateParentAllowsTagHelper(string tagName, MarkupTagBlockSyntax tagBlock) private void ValidateParentAllowsTagHelper(string tagName, MarkupStartTagSyntax tagBlock)
{ {
if (HasAllowedChildren() && if (HasAllowedChildren() &&
!CurrentTagHelperTracker.PrefixedAllowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase)) !CurrentTagHelperTracker.PrefixedAllowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase))
{ {
OnAllowedChildrenTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source); OnAllowedChildrenStartTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source);
} }
} }
private void ValidateBinding( private void ValidateBinding(
TagHelperBinding bindingResult, TagHelperBinding bindingResult,
string tagName, string tagName,
MarkupTagBlockSyntax tagBlock) MarkupStartTagSyntax tagBlock)
{ {
// Ensure that all descriptors associated with this tag have appropriate TagStructures. Cannot have // Ensure that all descriptors associated with this tag have appropriate TagStructures. Cannot have
// multiple descriptors that expect different TagStructures (other than TagStructure.Unspecified). // multiple descriptors that expect different TagStructures (other than TagStructure.Unspecified).
@ -479,12 +476,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
} }
} }
private bool ValidateTagSyntax(string tagName, MarkupTagBlockSyntax tag) private bool ValidateStartTagSyntax(string tagName, MarkupStartTagSyntax tag)
{ {
// We assume an invalid syntax until we verify that the tag meets all of our "valid syntax" criteria. // We assume an invalid syntax until we verify that the tag meets all of our "valid syntax" criteria.
if (IsPartialTag(tag)) if (IsPartialStartTag(tag))
{ {
var errorStart = GetTagDeclarationErrorStart(tag); var errorStart = GetStartTagDeclarationErrorStart(tag, _source);
_errorSink.OnError( _errorSink.OnError(
RazorDiagnosticFactory.CreateParsing_TagHelperMissingCloseAngle( RazorDiagnosticFactory.CreateParsing_TagHelperMissingCloseAngle(
@ -496,7 +493,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return true; return true;
} }
private bool IsPotentialTagHelper(string tagName, MarkupTagBlockSyntax childBlock) private bool ValidateEndTagSyntax(string tagName, MarkupEndTagSyntax tag)
{
// We assume an invalid syntax until we verify that the tag meets all of our "valid syntax" criteria.
if (IsPartialEndTag(tag))
{
var errorStart = GetEndTagDeclarationErrorStart(tag, _source);
_errorSink.OnError(
RazorDiagnosticFactory.CreateParsing_TagHelperMissingCloseAngle(
new SourceSpan(errorStart, tagName.Length), tagName));
return false;
}
return true;
}
private bool IsPotentialTagHelperStart(string tagName, MarkupStartTagSyntax childBlock)
{ {
Debug.Assert(childBlock.Children.Count > 0); Debug.Assert(childBlock.Children.Count > 0);
var child = childBlock.Children[0]; var child = childBlock.Children[0];
@ -505,14 +519,37 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
child.Kind != SyntaxKind.MarkupTransition; child.Kind != SyntaxKind.MarkupTransition;
} }
private SourceLocation GetTagDeclarationErrorStart(MarkupTagBlockSyntax tagBlock) private bool IsPotentialTagHelperEnd(string tagName, MarkupEndTagSyntax childBlock)
{ {
var advanceBy = IsEndTag(tagBlock) ? "</" : "<"; Debug.Assert(childBlock.Children.Count > 0);
var child = childBlock.Children[0];
return SourceLocationTracker.Advance(tagBlock.GetSourceLocation(_source), advanceBy); return !string.Equals(tagName, SyntaxConstants.TextTagName, StringComparison.OrdinalIgnoreCase) ||
child.Kind != SyntaxKind.MarkupTransition;
} }
private static bool IsPartialTag(MarkupTagBlockSyntax tagBlock) private static bool IsPartialStartTag(MarkupStartTagSyntax tagBlock)
{
// No need to validate the tag end because in order to be a tag block it must start with '<'.
var tagEnd = tagBlock.Children[tagBlock.Children.Count - 1];
// If our tag end is not a markup span it means it's some sort of code SyntaxTreeNode (not a valid format)
if (tagEnd != null && tagEnd is MarkupTextLiteralSyntax tagEndLiteral)
{
var endToken = tagEndLiteral.LiteralTokens.Count > 0 ?
tagEndLiteral.LiteralTokens[tagEndLiteral.LiteralTokens.Count - 1] :
null;
if (endToken != null && endToken.Kind == SyntaxKind.CloseAngle)
{
return false;
}
}
return true;
}
private static bool IsPartialEndTag(MarkupEndTagSyntax tagBlock)
{ {
// No need to validate the tag end because in order to be a tag block it must start with '<'. // No need to validate the tag end because in order to be a tag block it must start with '<'.
var tagEnd = tagBlock.Children[tagBlock.Children.Count - 1]; var tagEnd = tagBlock.Children[tagBlock.Children.Count - 1];
@ -566,7 +603,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
} }
} }
private void ValidateParentAllowsPlainTag(MarkupTagBlockSyntax tagBlock) private void ValidateParentAllowsPlainStartTag(MarkupStartTagSyntax tagBlock)
{ {
var tagName = tagBlock.GetTagName(); var tagName = tagBlock.GetTagName();
@ -595,7 +632,40 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var allowedChildren = tagHelperBinding != null ? CurrentTagHelperTracker.PrefixedAllowedChildren : CurrentTagHelperTracker.AllowedChildren; var allowedChildren = tagHelperBinding != null ? CurrentTagHelperTracker.PrefixedAllowedChildren : CurrentTagHelperTracker.AllowedChildren;
if (!allowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase)) if (!allowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase))
{ {
OnAllowedChildrenTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source); OnAllowedChildrenStartTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source);
}
}
private void ValidateParentAllowsPlainEndTag(MarkupEndTagSyntax tagBlock)
{
var tagName = tagBlock.GetTagName();
// Treat partial tags such as '</' which have no tag names as content.
if (string.IsNullOrEmpty(tagName))
{
var firstChild = tagBlock.Children.First();
Debug.Assert(firstChild is MarkupTextLiteralSyntax || firstChild is MarkupTransitionSyntax);
ValidateParentAllowsContent(tagBlock.Children.First());
return;
}
if (!HasAllowedChildren())
{
return;
}
var tagHelperBinding = _tagHelperBinder.GetBinding(
tagName,
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: CurrentParentTagName,
parentIsTagHelper: CurrentParentIsTagHelper);
// If we found a binding for the current tag, then it is a tag helper. Use the prefixed allowed children to compare.
var allowedChildren = tagHelperBinding != null ? CurrentTagHelperTracker.PrefixedAllowedChildren : CurrentTagHelperTracker.AllowedChildren;
if (!allowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase))
{
OnAllowedChildrenEndTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source);
} }
} }
@ -632,15 +702,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return commentParent != null; return commentParent != null;
} }
private static void OnAllowedChildrenTagError( private static void OnAllowedChildrenStartTagError(
TagHelperTracker tracker, TagHelperTracker tracker,
string tagName, string tagName,
MarkupTagBlockSyntax tagBlock, MarkupStartTagSyntax tagBlock,
ErrorSink errorSink, ErrorSink errorSink,
RazorSourceDocument source) RazorSourceDocument source)
{ {
var allowedChildrenString = string.Join(", ", tracker.AllowedChildren); var allowedChildrenString = string.Join(", ", tracker.AllowedChildren);
var errorStart = GetTagDeclarationErrorStart(tagBlock, source); var errorStart = GetStartTagDeclarationErrorStart(tagBlock, source);
errorSink.OnError( errorSink.OnError(
RazorDiagnosticFactory.CreateTagHelper_InvalidNestedTag( RazorDiagnosticFactory.CreateTagHelper_InvalidNestedTag(
@ -650,21 +720,32 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
allowedChildrenString)); allowedChildrenString));
} }
private static SourceLocation GetTagDeclarationErrorStart(MarkupTagBlockSyntax tagBlock, RazorSourceDocument source) private static void OnAllowedChildrenEndTagError(
TagHelperTracker tracker,
string tagName,
MarkupEndTagSyntax tagBlock,
ErrorSink errorSink,
RazorSourceDocument source)
{ {
var advanceBy = IsEndTag(tagBlock) ? "</" : "<"; var allowedChildrenString = string.Join(", ", tracker.AllowedChildren);
var errorStart = GetEndTagDeclarationErrorStart(tagBlock, source);
return SourceLocationTracker.Advance(tagBlock.GetSourceLocation(source), advanceBy); errorSink.OnError(
RazorDiagnosticFactory.CreateTagHelper_InvalidNestedTag(
new SourceSpan(errorStart, tagName.Length),
tagName,
tracker.TagName,
allowedChildrenString));
} }
private static bool IsEndTag(MarkupTagBlockSyntax tagBlock) private static SourceLocation GetStartTagDeclarationErrorStart(MarkupStartTagSyntax tagBlock, RazorSourceDocument source)
{ {
var childSpan = (MarkupTextLiteralSyntax)tagBlock.Children.First(); return SourceLocationTracker.Advance(tagBlock.GetSourceLocation(source), "<");
}
// We grab the token that could be forward slash private static SourceLocation GetEndTagDeclarationErrorStart(MarkupEndTagSyntax tagBlock, RazorSourceDocument source)
var relevantToken = childSpan.LiteralTokens[childSpan.LiteralTokens.Count == 1 ? 0 : 1]; {
return SourceLocationTracker.Advance(tagBlock.GetSourceLocation(source), "</");
return relevantToken.Kind == SyntaxKind.ForwardSlash;
} }
private class TagTracker private class TagTracker

View File

@ -1441,11 +1441,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
internal sealed partial class MarkupElementSyntax : MarkupSyntaxNode internal sealed partial class MarkupElementSyntax : MarkupSyntaxNode
{ {
private readonly MarkupTagBlockSyntax _startTag; private readonly MarkupStartTagSyntax _startTag;
private readonly GreenNode _body; private readonly GreenNode _body;
private readonly MarkupTagBlockSyntax _endTag; private readonly MarkupEndTagSyntax _endTag;
internal MarkupElementSyntax(SyntaxKind kind, MarkupTagBlockSyntax startTag, GreenNode body, MarkupTagBlockSyntax endTag, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations) internal MarkupElementSyntax(SyntaxKind kind, MarkupStartTagSyntax startTag, GreenNode body, MarkupEndTagSyntax endTag, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
: base(kind, diagnostics, annotations) : base(kind, diagnostics, annotations)
{ {
SlotCount = 3; SlotCount = 3;
@ -1467,7 +1467,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
} }
internal MarkupElementSyntax(SyntaxKind kind, MarkupTagBlockSyntax startTag, GreenNode body, MarkupTagBlockSyntax endTag) internal MarkupElementSyntax(SyntaxKind kind, MarkupStartTagSyntax startTag, GreenNode body, MarkupEndTagSyntax endTag)
: base(kind) : base(kind)
{ {
SlotCount = 3; SlotCount = 3;
@ -1488,9 +1488,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
} }
} }
public MarkupTagBlockSyntax StartTag { get { return _startTag; } } public MarkupStartTagSyntax StartTag { get { return _startTag; } }
public SyntaxList<RazorSyntaxNode> Body { get { return new SyntaxList<RazorSyntaxNode>(_body); } } public SyntaxList<RazorSyntaxNode> Body { get { return new SyntaxList<RazorSyntaxNode>(_body); } }
public MarkupTagBlockSyntax EndTag { get { return _endTag; } } public MarkupEndTagSyntax EndTag { get { return _endTag; } }
internal override GreenNode GetSlot(int index) internal override GreenNode GetSlot(int index)
{ {
@ -1518,7 +1518,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
visitor.VisitMarkupElement(this); visitor.VisitMarkupElement(this);
} }
public MarkupElementSyntax Update(MarkupTagBlockSyntax startTag, Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList<RazorSyntaxNode> body, MarkupTagBlockSyntax endTag) public MarkupElementSyntax Update(MarkupStartTagSyntax startTag, Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList<RazorSyntaxNode> body, MarkupEndTagSyntax endTag)
{ {
if (startTag != StartTag || body != Body || endTag != EndTag) if (startTag != StartTag || body != Body || endTag != EndTag)
{ {
@ -1546,6 +1546,168 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
} }
} }
internal sealed partial class MarkupStartTagSyntax : RazorBlockSyntax
{
private readonly GreenNode _children;
internal MarkupStartTagSyntax(SyntaxKind kind, GreenNode children, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
: base(kind, diagnostics, annotations)
{
SlotCount = 1;
if (children != null)
{
AdjustFlagsAndWidth(children);
_children = children;
}
}
internal MarkupStartTagSyntax(SyntaxKind kind, GreenNode children)
: base(kind)
{
SlotCount = 1;
if (children != null)
{
AdjustFlagsAndWidth(children);
_children = children;
}
}
public override SyntaxList<RazorSyntaxNode> Children { get { return new SyntaxList<RazorSyntaxNode>(_children); } }
internal override GreenNode GetSlot(int index)
{
switch (index)
{
case 0: return _children;
default: return null;
}
}
internal override SyntaxNode CreateRed(SyntaxNode parent, int position)
{
return new Syntax.MarkupStartTagSyntax(this, parent, position);
}
public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor)
{
return visitor.VisitMarkupStartTag(this);
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.VisitMarkupStartTag(this);
}
public MarkupStartTagSyntax Update(Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList<RazorSyntaxNode> children)
{
if (children != Children)
{
var newNode = SyntaxFactory.MarkupStartTag(children);
var diags = GetDiagnostics();
if (diags != null && diags.Length > 0)
newNode = newNode.WithDiagnosticsGreen(diags);
var annotations = GetAnnotations();
if (annotations != null && annotations.Length > 0)
newNode = newNode.WithAnnotationsGreen(annotations);
return newNode;
}
return this;
}
internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics)
{
return new MarkupStartTagSyntax(Kind, _children, diagnostics, GetAnnotations());
}
internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
{
return new MarkupStartTagSyntax(Kind, _children, GetDiagnostics(), annotations);
}
}
internal sealed partial class MarkupEndTagSyntax : RazorBlockSyntax
{
private readonly GreenNode _children;
internal MarkupEndTagSyntax(SyntaxKind kind, GreenNode children, RazorDiagnostic[] diagnostics, SyntaxAnnotation[] annotations)
: base(kind, diagnostics, annotations)
{
SlotCount = 1;
if (children != null)
{
AdjustFlagsAndWidth(children);
_children = children;
}
}
internal MarkupEndTagSyntax(SyntaxKind kind, GreenNode children)
: base(kind)
{
SlotCount = 1;
if (children != null)
{
AdjustFlagsAndWidth(children);
_children = children;
}
}
public override SyntaxList<RazorSyntaxNode> Children { get { return new SyntaxList<RazorSyntaxNode>(_children); } }
internal override GreenNode GetSlot(int index)
{
switch (index)
{
case 0: return _children;
default: return null;
}
}
internal override SyntaxNode CreateRed(SyntaxNode parent, int position)
{
return new Syntax.MarkupEndTagSyntax(this, parent, position);
}
public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor)
{
return visitor.VisitMarkupEndTag(this);
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.VisitMarkupEndTag(this);
}
public MarkupEndTagSyntax Update(Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList<RazorSyntaxNode> children)
{
if (children != Children)
{
var newNode = SyntaxFactory.MarkupEndTag(children);
var diags = GetDiagnostics();
if (diags != null && diags.Length > 0)
newNode = newNode.WithDiagnosticsGreen(diags);
var annotations = GetAnnotations();
if (annotations != null && annotations.Length > 0)
newNode = newNode.WithAnnotationsGreen(annotations);
return newNode;
}
return this;
}
internal override GreenNode SetDiagnostics(RazorDiagnostic[] diagnostics)
{
return new MarkupEndTagSyntax(Kind, _children, diagnostics, GetAnnotations());
}
internal override GreenNode SetAnnotations(SyntaxAnnotation[] annotations)
{
return new MarkupEndTagSyntax(Kind, _children, GetDiagnostics(), annotations);
}
}
internal sealed partial class MarkupTagHelperElementSyntax : MarkupSyntaxNode internal sealed partial class MarkupTagHelperElementSyntax : MarkupSyntaxNode
{ {
private readonly MarkupTagHelperStartTagSyntax _startTag; private readonly MarkupTagHelperStartTagSyntax _startTag;
@ -3383,6 +3545,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
return DefaultVisit(node); return DefaultVisit(node);
} }
public virtual TResult VisitMarkupStartTag(MarkupStartTagSyntax node)
{
return DefaultVisit(node);
}
public virtual TResult VisitMarkupEndTag(MarkupEndTagSyntax node)
{
return DefaultVisit(node);
}
public virtual TResult VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) public virtual TResult VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node)
{ {
return DefaultVisit(node); return DefaultVisit(node);
@ -3572,6 +3744,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
DefaultVisit(node); DefaultVisit(node);
} }
public virtual void VisitMarkupStartTag(MarkupStartTagSyntax node)
{
DefaultVisit(node);
}
public virtual void VisitMarkupEndTag(MarkupEndTagSyntax node)
{
DefaultVisit(node);
}
public virtual void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) public virtual void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node)
{ {
DefaultVisit(node); DefaultVisit(node);
@ -3786,12 +3968,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
public override GreenNode VisitMarkupElement(MarkupElementSyntax node) public override GreenNode VisitMarkupElement(MarkupElementSyntax node)
{ {
var startTag = (MarkupTagBlockSyntax)Visit(node.StartTag); var startTag = (MarkupStartTagSyntax)Visit(node.StartTag);
var body = VisitList(node.Body); var body = VisitList(node.Body);
var endTag = (MarkupTagBlockSyntax)Visit(node.EndTag); var endTag = (MarkupEndTagSyntax)Visit(node.EndTag);
return node.Update(startTag, body, endTag); return node.Update(startTag, body, endTag);
} }
public override GreenNode VisitMarkupStartTag(MarkupStartTagSyntax node)
{
var children = VisitList(node.Children);
return node.Update(children);
}
public override GreenNode VisitMarkupEndTag(MarkupEndTagSyntax node)
{
var children = VisitList(node.Children);
return node.Update(children);
}
public override GreenNode VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) public override GreenNode VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node)
{ {
var startTag = (MarkupTagHelperStartTagSyntax)Visit(node.StartTag); var startTag = (MarkupTagHelperStartTagSyntax)Visit(node.StartTag);
@ -4108,13 +4302,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
return result; return result;
} }
public static MarkupElementSyntax MarkupElement(MarkupTagBlockSyntax startTag, Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList<RazorSyntaxNode> body, MarkupTagBlockSyntax endTag) public static MarkupElementSyntax MarkupElement(MarkupStartTagSyntax startTag, Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList<RazorSyntaxNode> body, MarkupEndTagSyntax endTag)
{ {
var result = new MarkupElementSyntax(SyntaxKind.MarkupElement, startTag, body.Node, endTag); var result = new MarkupElementSyntax(SyntaxKind.MarkupElement, startTag, body.Node, endTag);
return result; return result;
} }
public static MarkupStartTagSyntax MarkupStartTag(Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList<RazorSyntaxNode> children)
{
var result = new MarkupStartTagSyntax(SyntaxKind.MarkupStartTag, children.Node);
return result;
}
public static MarkupEndTagSyntax MarkupEndTag(Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList<RazorSyntaxNode> children)
{
var result = new MarkupEndTagSyntax(SyntaxKind.MarkupEndTag, children.Node);
return result;
}
public static MarkupTagHelperElementSyntax MarkupTagHelperElement(MarkupTagHelperStartTagSyntax startTag, Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList<RazorSyntaxNode> body, MarkupTagHelperEndTagSyntax endTag) public static MarkupTagHelperElementSyntax MarkupTagHelperElement(MarkupTagHelperStartTagSyntax startTag, Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax.SyntaxList<RazorSyntaxNode> body, MarkupTagHelperEndTagSyntax endTag)
{ {
if (startTag == null) if (startTag == null)
@ -4343,6 +4551,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax
typeof(MarkupLiteralAttributeValueSyntax), typeof(MarkupLiteralAttributeValueSyntax),
typeof(MarkupDynamicAttributeValueSyntax), typeof(MarkupDynamicAttributeValueSyntax),
typeof(MarkupElementSyntax), typeof(MarkupElementSyntax),
typeof(MarkupStartTagSyntax),
typeof(MarkupEndTagSyntax),
typeof(MarkupTagHelperElementSyntax), typeof(MarkupTagHelperElementSyntax),
typeof(MarkupTagHelperStartTagSyntax), typeof(MarkupTagHelperStartTagSyntax),
typeof(MarkupTagHelperEndTagSyntax), typeof(MarkupTagHelperEndTagSyntax),

View File

@ -113,6 +113,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
return DefaultVisit(node); return DefaultVisit(node);
} }
/// <summary>Called when the visitor visits a MarkupStartTagSyntax node.</summary>
public virtual TResult VisitMarkupStartTag(MarkupStartTagSyntax node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a MarkupEndTagSyntax node.</summary>
public virtual TResult VisitMarkupEndTag(MarkupEndTagSyntax node)
{
return DefaultVisit(node);
}
/// <summary>Called when the visitor visits a MarkupTagHelperElementSyntax node.</summary> /// <summary>Called when the visitor visits a MarkupTagHelperElementSyntax node.</summary>
public virtual TResult VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) public virtual TResult VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node)
{ {
@ -338,6 +350,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
DefaultVisit(node); DefaultVisit(node);
} }
/// <summary>Called when the visitor visits a MarkupStartTagSyntax node.</summary>
public virtual void VisitMarkupStartTag(MarkupStartTagSyntax node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a MarkupEndTagSyntax node.</summary>
public virtual void VisitMarkupEndTag(MarkupEndTagSyntax node)
{
DefaultVisit(node);
}
/// <summary>Called when the visitor visits a MarkupTagHelperElementSyntax node.</summary> /// <summary>Called when the visitor visits a MarkupTagHelperElementSyntax node.</summary>
public virtual void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) public virtual void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node)
{ {
@ -572,12 +596,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
public override SyntaxNode VisitMarkupElement(MarkupElementSyntax node) public override SyntaxNode VisitMarkupElement(MarkupElementSyntax node)
{ {
var startTag = (MarkupTagBlockSyntax)Visit(node.StartTag); var startTag = (MarkupStartTagSyntax)Visit(node.StartTag);
var body = VisitList(node.Body); var body = VisitList(node.Body);
var endTag = (MarkupTagBlockSyntax)Visit(node.EndTag); var endTag = (MarkupEndTagSyntax)Visit(node.EndTag);
return node.Update(startTag, body, endTag); return node.Update(startTag, body, endTag);
} }
public override SyntaxNode VisitMarkupStartTag(MarkupStartTagSyntax node)
{
var children = VisitList(node.Children);
return node.Update(children);
}
public override SyntaxNode VisitMarkupEndTag(MarkupEndTagSyntax node)
{
var children = VisitList(node.Children);
return node.Update(children);
}
public override SyntaxNode VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node) public override SyntaxNode VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node)
{ {
var startTag = (MarkupTagHelperStartTagSyntax)Visit(node.StartTag); var startTag = (MarkupTagHelperStartTagSyntax)Visit(node.StartTag);
@ -962,15 +998,39 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
} }
/// <summary>Creates a new MarkupElementSyntax instance.</summary> /// <summary>Creates a new MarkupElementSyntax instance.</summary>
public static MarkupElementSyntax MarkupElement(MarkupTagBlockSyntax startTag, SyntaxList<RazorSyntaxNode> body, MarkupTagBlockSyntax endTag) public static MarkupElementSyntax MarkupElement(MarkupStartTagSyntax startTag, SyntaxList<RazorSyntaxNode> body, MarkupEndTagSyntax endTag)
{ {
return (MarkupElementSyntax)InternalSyntax.SyntaxFactory.MarkupElement(startTag == null ? null : (InternalSyntax.MarkupTagBlockSyntax)startTag.Green, body.Node.ToGreenList<InternalSyntax.RazorSyntaxNode>(), endTag == null ? null : (InternalSyntax.MarkupTagBlockSyntax)endTag.Green).CreateRed(); return (MarkupElementSyntax)InternalSyntax.SyntaxFactory.MarkupElement(startTag == null ? null : (InternalSyntax.MarkupStartTagSyntax)startTag.Green, body.Node.ToGreenList<InternalSyntax.RazorSyntaxNode>(), endTag == null ? null : (InternalSyntax.MarkupEndTagSyntax)endTag.Green).CreateRed();
} }
/// <summary>Creates a new MarkupElementSyntax instance.</summary> /// <summary>Creates a new MarkupElementSyntax instance.</summary>
public static MarkupElementSyntax MarkupElement(SyntaxList<RazorSyntaxNode> body = default(SyntaxList<RazorSyntaxNode>)) public static MarkupElementSyntax MarkupElement(SyntaxList<RazorSyntaxNode> body = default(SyntaxList<RazorSyntaxNode>))
{ {
return SyntaxFactory.MarkupElement(default(MarkupTagBlockSyntax), body, default(MarkupTagBlockSyntax)); return SyntaxFactory.MarkupElement(default(MarkupStartTagSyntax), body, default(MarkupEndTagSyntax));
}
/// <summary>Creates a new MarkupStartTagSyntax instance.</summary>
public static MarkupStartTagSyntax MarkupStartTag(SyntaxList<RazorSyntaxNode> children)
{
return (MarkupStartTagSyntax)InternalSyntax.SyntaxFactory.MarkupStartTag(children.Node.ToGreenList<InternalSyntax.RazorSyntaxNode>()).CreateRed();
}
/// <summary>Creates a new MarkupStartTagSyntax instance.</summary>
public static MarkupStartTagSyntax MarkupStartTag()
{
return SyntaxFactory.MarkupStartTag(default(SyntaxList<RazorSyntaxNode>));
}
/// <summary>Creates a new MarkupEndTagSyntax instance.</summary>
public static MarkupEndTagSyntax MarkupEndTag(SyntaxList<RazorSyntaxNode> children)
{
return (MarkupEndTagSyntax)InternalSyntax.SyntaxFactory.MarkupEndTag(children.Node.ToGreenList<InternalSyntax.RazorSyntaxNode>()).CreateRed();
}
/// <summary>Creates a new MarkupEndTagSyntax instance.</summary>
public static MarkupEndTagSyntax MarkupEndTag()
{
return SyntaxFactory.MarkupEndTag(default(SyntaxList<RazorSyntaxNode>));
} }
/// <summary>Creates a new MarkupTagHelperElementSyntax instance.</summary> /// <summary>Creates a new MarkupTagHelperElementSyntax instance.</summary>

View File

@ -1382,16 +1382,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
internal sealed partial class MarkupElementSyntax : MarkupSyntaxNode internal sealed partial class MarkupElementSyntax : MarkupSyntaxNode
{ {
private MarkupTagBlockSyntax _startTag; private MarkupStartTagSyntax _startTag;
private SyntaxNode _body; private SyntaxNode _body;
private MarkupTagBlockSyntax _endTag; private MarkupEndTagSyntax _endTag;
internal MarkupElementSyntax(GreenNode green, SyntaxNode parent, int position) internal MarkupElementSyntax(GreenNode green, SyntaxNode parent, int position)
: base(green, parent, position) : base(green, parent, position)
{ {
} }
public MarkupTagBlockSyntax StartTag public MarkupStartTagSyntax StartTag
{ {
get get
{ {
@ -1407,7 +1407,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
} }
} }
public MarkupTagBlockSyntax EndTag public MarkupEndTagSyntax EndTag
{ {
get get
{ {
@ -1446,7 +1446,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
visitor.VisitMarkupElement(this); visitor.VisitMarkupElement(this);
} }
public MarkupElementSyntax Update(MarkupTagBlockSyntax startTag, SyntaxList<RazorSyntaxNode> body, MarkupTagBlockSyntax endTag) public MarkupElementSyntax Update(MarkupStartTagSyntax startTag, SyntaxList<RazorSyntaxNode> body, MarkupEndTagSyntax endTag)
{ {
if (startTag != StartTag || body != Body || endTag != EndTag) if (startTag != StartTag || body != Body || endTag != EndTag)
{ {
@ -1460,7 +1460,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
return this; return this;
} }
public MarkupElementSyntax WithStartTag(MarkupTagBlockSyntax startTag) public MarkupElementSyntax WithStartTag(MarkupStartTagSyntax startTag)
{ {
return Update(startTag, Body, EndTag); return Update(startTag, Body, EndTag);
} }
@ -1470,14 +1470,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
return Update(StartTag, body, EndTag); return Update(StartTag, body, EndTag);
} }
public MarkupElementSyntax WithEndTag(MarkupTagBlockSyntax endTag) public MarkupElementSyntax WithEndTag(MarkupEndTagSyntax endTag)
{ {
return Update(StartTag, Body, endTag); return Update(StartTag, Body, endTag);
} }
public MarkupElementSyntax AddStartTagChildren(params RazorSyntaxNode[] items) public MarkupElementSyntax AddStartTagChildren(params RazorSyntaxNode[] items)
{ {
var _startTag = this.StartTag ?? SyntaxFactory.MarkupTagBlock(); var _startTag = this.StartTag ?? SyntaxFactory.MarkupStartTag();
return this.WithStartTag(_startTag.WithChildren(_startTag.Children.AddRange(items))); return this.WithStartTag(_startTag.WithChildren(_startTag.Children.AddRange(items)));
} }
@ -1488,11 +1488,153 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
public MarkupElementSyntax AddEndTagChildren(params RazorSyntaxNode[] items) public MarkupElementSyntax AddEndTagChildren(params RazorSyntaxNode[] items)
{ {
var _endTag = this.EndTag ?? SyntaxFactory.MarkupTagBlock(); var _endTag = this.EndTag ?? SyntaxFactory.MarkupEndTag();
return this.WithEndTag(_endTag.WithChildren(_endTag.Children.AddRange(items))); return this.WithEndTag(_endTag.WithChildren(_endTag.Children.AddRange(items)));
} }
} }
internal sealed partial class MarkupStartTagSyntax : RazorBlockSyntax
{
private SyntaxNode _children;
internal MarkupStartTagSyntax(GreenNode green, SyntaxNode parent, int position)
: base(green, parent, position)
{
}
public override SyntaxList<RazorSyntaxNode> Children
{
get
{
return new SyntaxList<RazorSyntaxNode>(GetRed(ref _children, 0));
}
}
internal override SyntaxNode GetNodeSlot(int index)
{
switch (index)
{
case 0: return GetRedAtZero(ref _children);
default: return null;
}
}
internal override SyntaxNode GetCachedSlot(int index)
{
switch (index)
{
case 0: return _children;
default: return null;
}
}
public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor)
{
return visitor.VisitMarkupStartTag(this);
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.VisitMarkupStartTag(this);
}
public MarkupStartTagSyntax Update(SyntaxList<RazorSyntaxNode> children)
{
if (children != Children)
{
var newNode = SyntaxFactory.MarkupStartTag(children);
var annotations = GetAnnotations();
if (annotations != null && annotations.Length > 0)
return newNode.WithAnnotations(annotations);
return newNode;
}
return this;
}
internal override RazorBlockSyntax WithChildrenCore(SyntaxList<RazorSyntaxNode> children) => WithChildren(children);
public new MarkupStartTagSyntax WithChildren(SyntaxList<RazorSyntaxNode> children)
{
return Update(children);
}
internal override RazorBlockSyntax AddChildrenCore(params RazorSyntaxNode[] items) => AddChildren(items);
public new MarkupStartTagSyntax AddChildren(params RazorSyntaxNode[] items)
{
return WithChildren(this.Children.AddRange(items));
}
}
internal sealed partial class MarkupEndTagSyntax : RazorBlockSyntax
{
private SyntaxNode _children;
internal MarkupEndTagSyntax(GreenNode green, SyntaxNode parent, int position)
: base(green, parent, position)
{
}
public override SyntaxList<RazorSyntaxNode> Children
{
get
{
return new SyntaxList<RazorSyntaxNode>(GetRed(ref _children, 0));
}
}
internal override SyntaxNode GetNodeSlot(int index)
{
switch (index)
{
case 0: return GetRedAtZero(ref _children);
default: return null;
}
}
internal override SyntaxNode GetCachedSlot(int index)
{
switch (index)
{
case 0: return _children;
default: return null;
}
}
public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor)
{
return visitor.VisitMarkupEndTag(this);
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.VisitMarkupEndTag(this);
}
public MarkupEndTagSyntax Update(SyntaxList<RazorSyntaxNode> children)
{
if (children != Children)
{
var newNode = SyntaxFactory.MarkupEndTag(children);
var annotations = GetAnnotations();
if (annotations != null && annotations.Length > 0)
return newNode.WithAnnotations(annotations);
return newNode;
}
return this;
}
internal override RazorBlockSyntax WithChildrenCore(SyntaxList<RazorSyntaxNode> children) => WithChildren(children);
public new MarkupEndTagSyntax WithChildren(SyntaxList<RazorSyntaxNode> children)
{
return Update(children);
}
internal override RazorBlockSyntax AddChildrenCore(params RazorSyntaxNode[] items) => AddChildren(items);
public new MarkupEndTagSyntax AddChildren(params RazorSyntaxNode[] items)
{
return WithChildren(this.Children.AddRange(items));
}
}
internal sealed partial class MarkupTagHelperElementSyntax : MarkupSyntaxNode internal sealed partial class MarkupTagHelperElementSyntax : MarkupSyntaxNode
{ {
private MarkupTagHelperStartTagSyntax _startTag; private MarkupTagHelperStartTagSyntax _startTag;

View File

@ -19,15 +19,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
return newSyntaxTree; return newSyntaxTree;
} }
public static RazorSyntaxTree RemoveMarkupElements(RazorSyntaxTree syntaxTree)
{
var rewriter = new RemoveMarkupElementRewriter();
var rewrittenRoot = rewriter.Visit(syntaxTree.Root);
var newSyntaxTree = RazorSyntaxTree.Create(rewrittenRoot, syntaxTree.Source, syntaxTree.Diagnostics, syntaxTree.Options);
return newSyntaxTree;
}
private class AddMarkupElementRewriter : SyntaxRewriter private class AddMarkupElementRewriter : SyntaxRewriter
{ {
private readonly Stack<TagBlockTracker> _startTagTracker = new Stack<TagBlockTracker>(); private readonly Stack<TagBlockTracker> _startTagTracker = new Stack<TagBlockTracker>();
@ -38,10 +29,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
public override SyntaxNode Visit(SyntaxNode node) public override SyntaxNode Visit(SyntaxNode node)
{ {
node = base.Visit(node);
if (node != null) if (node != null)
{ {
var diagnostics = node.GetDiagnostics();
node = base.Visit(node);
if (diagnostics.Length > 0)
{
// Persist node diagnostics.
node = node.WithDiagnostics(diagnostics);
}
node = RewriteNode(node); node = RewriteNode(node);
} }
@ -71,7 +68,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
var tagName = tagBlock.GetTagName(); var tagName = tagBlock.GetTagName();
if (string.IsNullOrWhiteSpace(tagName) || tagBlock.IsSelfClosing()) if (string.IsNullOrWhiteSpace(tagName) || tagBlock.IsSelfClosing())
{ {
// Don't want to track incomplete, invalid (Eg. </>, < >), void or self-closing tags. // Don't want to track incomplete, invalid (Eg. </>, < >) or self-closing tags.
// Simply wrap it in a block with no body or start/end tag. // Simply wrap it in a block with no body or start/end tag.
if (IsEndTag(tagBlock)) if (IsEndTag(tagBlock))
{ {
@ -102,7 +99,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
if (!TryRecoverStartTag(rewrittenChildren, tagName, tagBlock)) if (!TryRecoverStartTag(rewrittenChildren, tagName, tagBlock))
{ {
// Could not recover. The end tag doesn't have a corresponding start tag. Wrap it in a block and move on. // Could not recover. The end tag doesn't have a corresponding start tag. Wrap it in a block and move on.
var rewritten = SyntaxFactory.MarkupElement(startTag: null, body: new SyntaxList<RazorSyntaxNode>(), endTag: tagBlock); var rewritten = SyntaxFactory.MarkupElement(startTag: null, body: new SyntaxList<RazorSyntaxNode>(), endTag: GetEndTagSyntax(tagBlock));
TrackChild(rewritten, rewrittenChildren); TrackChild(rewritten, rewrittenChildren);
} }
} }
@ -149,7 +146,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
// The call to SyntaxNode.ReplaceNodes() later will take care removing the nodes whose replacement is null. // The call to SyntaxNode.ReplaceNodes() later will take care removing the nodes whose replacement is null.
var body = tagChildren.Where(t => t != null).ToList(); var body = tagChildren.Where(t => t != null).ToList();
var rewritten = SyntaxFactory.MarkupElement(startTag, new SyntaxList<RazorSyntaxNode>(body), endTag); var rewritten = SyntaxFactory.MarkupElement(
GetStartTagSyntax(startTag),
new SyntaxList<RazorSyntaxNode>(body),
GetEndTagSyntax(endTag));
if (startTag != null) if (startTag != null)
{ {
// If there was a start tag, that is where we want to put our new element. // If there was a start tag, that is where we want to put our new element.
@ -232,6 +233,26 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
return childContent.StartsWith("</") || childContent.StartsWith("/"); return childContent.StartsWith("</") || childContent.StartsWith("/");
} }
private static MarkupStartTagSyntax GetStartTagSyntax(MarkupTagBlockSyntax tagBlock)
{
if (tagBlock == null)
{
return null;
}
return SyntaxFactory.MarkupStartTag(tagBlock.Children);
}
private static MarkupEndTagSyntax GetEndTagSyntax(MarkupTagBlockSyntax tagBlock)
{
if (tagBlock == null)
{
return null;
}
return SyntaxFactory.MarkupEndTag(tagBlock.Children);
}
private class TagBlockTracker private class TagBlockTracker
{ {
public TagBlockTracker(MarkupTagBlockSyntax tagBlock) public TagBlockTracker(MarkupTagBlockSyntax tagBlock)
@ -248,43 +269,5 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
public string TagName { get; } public string TagName { get; }
} }
} }
private class RemoveMarkupElementRewriter : SyntaxRewriter
{
public override SyntaxNode Visit(SyntaxNode node)
{
if (node != null)
{
node = RewriteNode(node);
}
return base.Visit(node);
}
private SyntaxNode RewriteNode(SyntaxNode node)
{
if (node.IsToken)
{
return node;
}
var children = node.ChildNodes();
for (var i = 0; i < children.Count; i++)
{
var child = children[i];
if (!(child is MarkupElementSyntax tagElement))
{
continue;
}
node = node.ReplaceNode(tagElement, tagElement.ChildNodes());
// Since we rewrote 'node', it's children are different. Update our collection.
children = node.ChildNodes();
}
return node;
}
}
} }
} }

View File

@ -102,9 +102,17 @@
</Node> </Node>
<Node Name="MarkupElementSyntax" Base="MarkupSyntaxNode"> <Node Name="MarkupElementSyntax" Base="MarkupSyntaxNode">
<Kind Name="MarkupElement" /> <Kind Name="MarkupElement" />
<Field Name="StartTag" Type="MarkupTagBlockSyntax" Optional="true" /> <Field Name="StartTag" Type="MarkupStartTagSyntax" Optional="true" />
<Field Name="Body" Type="SyntaxList&lt;RazorSyntaxNode&gt;" /> <Field Name="Body" Type="SyntaxList&lt;RazorSyntaxNode&gt;" />
<Field Name="EndTag" Type="MarkupTagBlockSyntax" Optional="true" /> <Field Name="EndTag" Type="MarkupEndTagSyntax" Optional="true" />
</Node>
<Node Name="MarkupStartTagSyntax" Base="RazorBlockSyntax">
<Kind Name="MarkupStartTag" />
<Field Name="Children" Type="SyntaxList&lt;RazorSyntaxNode&gt;" Override="true" />
</Node>
<Node Name="MarkupEndTagSyntax" Base="RazorBlockSyntax">
<Kind Name="MarkupEndTag" />
<Field Name="Children" Type="SyntaxList&lt;RazorSyntaxNode&gt;" Override="true" />
</Node> </Node>
<Node Name="MarkupTagHelperElementSyntax" Base="MarkupSyntaxNode"> <Node Name="MarkupTagHelperElementSyntax" Base="MarkupSyntaxNode">
<Kind Name="MarkupTagHelperElement" /> <Kind Name="MarkupTagHelperElement" />

View File

@ -19,6 +19,8 @@ namespace Microsoft.AspNetCore.Razor.Language
MarkupBlock, MarkupBlock,
MarkupTransition, MarkupTransition,
MarkupElement, MarkupElement,
MarkupStartTag,
MarkupEndTag,
MarkupTagBlock, MarkupTagBlock,
MarkupTextLiteral, MarkupTextLiteral,
MarkupEphemeralTextLiteral, MarkupEphemeralTextLiteral,

View File

@ -223,55 +223,31 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
public static string GetTagName(this MarkupTagBlockSyntax tagBlock) public static string GetTagName(this MarkupTagBlockSyntax tagBlock)
{ {
if (tagBlock == null) return GetTagNameCore(tagBlock);
{
throw new ArgumentNullException(nameof(tagBlock));
}
var child = tagBlock.Children[0];
if (tagBlock.Children.Count == 0 || !(child is MarkupTextLiteralSyntax))
{
return null;
}
var childLiteral = (MarkupTextLiteralSyntax)child;
SyntaxToken textToken = null;
for (var i = 0; i < childLiteral.LiteralTokens.Count; i++)
{
var token = childLiteral.LiteralTokens[i];
if (token != null &&
(token.Kind == SyntaxKind.Whitespace || token.Kind == SyntaxKind.Text))
{
textToken = token;
break;
}
}
if (textToken == null)
{
return null;
}
return textToken.Kind == SyntaxKind.Whitespace ? null : textToken.Content;
} }
public static string GetTagName(this MarkupTagHelperStartTagSyntax tagBlock) public static string GetTagName(this MarkupStartTagSyntax startTag)
{
return GetTagNameCore(startTag);
}
public static string GetTagName(this MarkupEndTagSyntax endTag)
{
return GetTagNameCore(endTag);
}
private static string GetTagNameCore(RazorBlockSyntax tagBlock)
{ {
if (tagBlock == null) if (tagBlock == null)
{ {
throw new ArgumentNullException(nameof(tagBlock)); throw new ArgumentNullException(nameof(tagBlock));
} }
var child = tagBlock.Children[0]; if (tagBlock.Children.Count == 0 || !(tagBlock.Children[0] is MarkupTextLiteralSyntax childLiteral))
if (tagBlock.Children.Count == 0 || !(child is MarkupTextLiteralSyntax))
{ {
return null; return null;
} }
var childLiteral = (MarkupTextLiteralSyntax)child;
SyntaxToken textToken = null; SyntaxToken textToken = null;
for (var i = 0; i < childLiteral.LiteralTokens.Count; i++) for (var i = 0; i < childLiteral.LiteralTokens.Count; i++)
{ {
@ -294,6 +270,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
} }
public static bool IsSelfClosing(this MarkupTagBlockSyntax tagBlock) public static bool IsSelfClosing(this MarkupTagBlockSyntax tagBlock)
{
return IsSelfClosingCore(tagBlock);
}
public static bool IsSelfClosing(this MarkupStartTagSyntax startTag)
{
return IsSelfClosingCore(startTag);
}
private static bool IsSelfClosingCore(RazorBlockSyntax tagBlock)
{ {
if (tagBlock == null) if (tagBlock == null)
{ {
@ -305,14 +291,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
return lastChild?.GetContent().EndsWith("/>", StringComparison.Ordinal) ?? false; return lastChild?.GetContent().EndsWith("/>", StringComparison.Ordinal) ?? false;
} }
public static bool IsVoidElement(this MarkupTagBlockSyntax tagBlock) public static bool IsVoidElement(this MarkupStartTagSyntax startTag)
{ {
if (tagBlock == null) if (startTag == null)
{ {
throw new ArgumentNullException(nameof(tagBlock)); throw new ArgumentNullException(nameof(startTag));
} }
return ParserHelpers.VoidElements.Contains(tagBlock.GetTagName()); return ParserHelpers.VoidElements.Contains(startTag.GetTagName());
} }
} }
} }

View File

@ -287,7 +287,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
{ {
return node.Kind == SyntaxKind.MarkupTextLiteral || return node.Kind == SyntaxKind.MarkupTextLiteral ||
node.Kind == SyntaxKind.MarkupEphemeralTextLiteral || node.Kind == SyntaxKind.MarkupEphemeralTextLiteral ||
node.Kind == SyntaxKind.MarkupTagBlock || node.Kind == SyntaxKind.MarkupStartTag ||
node.Kind == SyntaxKind.MarkupEndTag ||
node.Kind == SyntaxKind.MarkupAttributeBlock || node.Kind == SyntaxKind.MarkupAttributeBlock ||
node.Kind == SyntaxKind.MarkupMinimizedAttributeBlock || node.Kind == SyntaxKind.MarkupMinimizedAttributeBlock ||
node.Kind == SyntaxKind.MarkupTagHelperAttribute || node.Kind == SyntaxKind.MarkupTagHelperAttribute ||

View File

@ -13,6 +13,7 @@ using System.Text;
using Xunit; using Xunit;
using Xunit.Sdk; using Xunit.Sdk;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Razor.Language.Syntax;
namespace Microsoft.AspNetCore.Razor.Language.Legacy namespace Microsoft.AspNetCore.Razor.Language.Legacy
{ {
@ -240,6 +241,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options); var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options);
codeDocument.SetSyntaxTree(syntaxTree); codeDocument.SetSyntaxTree(syntaxTree);
// Group markup elements
syntaxTree = MarkupElementRewriter.AddMarkupElements(syntaxTree);
var defaultDirectivePass = new DefaultDirectiveSyntaxTreePass(); var defaultDirectivePass = new DefaultDirectiveSyntaxTreePass();
syntaxTree = defaultDirectivePass.Execute(codeDocument, syntaxTree); syntaxTree = defaultDirectivePass.Execute(codeDocument, syntaxTree);
@ -267,6 +271,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options); var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options);
// Group markup elements
syntaxTree = MarkupElementRewriter.AddMarkupElements(syntaxTree);
return syntaxTree; return syntaxTree;
} }
@ -300,6 +307,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options); var syntaxTree = RazorSyntaxTree.Create(root, source, diagnostics, options);
// Group markup elements
syntaxTree = MarkupElementRewriter.AddMarkupElements(syntaxTree);
return syntaxTree; return syntaxTree;
} }

View File

@ -203,7 +203,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax
{ {
return node.Kind == SyntaxKind.MarkupTextLiteral || return node.Kind == SyntaxKind.MarkupTextLiteral ||
node.Kind == SyntaxKind.MarkupEphemeralTextLiteral || node.Kind == SyntaxKind.MarkupEphemeralTextLiteral ||
node.Kind == SyntaxKind.MarkupTagBlock || node.Kind == SyntaxKind.MarkupStartTag ||
node.Kind == SyntaxKind.MarkupEndTag ||
node.Kind == SyntaxKind.MarkupAttributeBlock || node.Kind == SyntaxKind.MarkupAttributeBlock ||
node.Kind == SyntaxKind.MarkupMinimizedAttributeBlock || node.Kind == SyntaxKind.MarkupMinimizedAttributeBlock ||
node.Kind == SyntaxKind.MarkupTagHelperAttribute || node.Kind == SyntaxKind.MarkupTagHelperAttribute ||