Example #1
0
            private void ValidateParentAllowsPlainEndTag(MarkupEndTagSyntax tagBlock)
            {
                var tagName = tagBlock.GetTagNameWithOptionalBang();

                // 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);

                    ValidateParentAllowsContent(firstChild);
                    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);
                }
            }
Example #2
0
            private bool IsPotentialTagHelperEnd(string tagName, MarkupEndTagSyntax childBlock)
            {
                Debug.Assert(childBlock.Children.Count > 0);
                var child = childBlock.Children[0];

                return(!string.Equals(tagName, SyntaxConstants.TextTagName, StringComparison.OrdinalIgnoreCase) ||
                       child.Kind != SyntaxKind.MarkupTransition);
            }
Example #3
0
 public override void VisitMarkupEndTag(MarkupEndTagSyntax node)
 {
     WriteBlock(node, FormattingBlockKind.Tag, n =>
     {
         var children = GetRewrittenMarkupEndTagChildren(node);
         foreach (var child in children)
         {
             Visit(child);
         }
     });
 }
Example #4
0
 public override void VisitMarkupEndTag(MarkupEndTagSyntax node)
 {
     AddSemanticRange(node.OpenAngle);
     if (node.Bang != null)
     {
         AddSemanticRange(node.Bang);
     }
     if (node.ForwardSlash != null)
     {
         AddSemanticRange(node.ForwardSlash);
     }
     AddSemanticRange(node.Name, SyntaxKind.MarkupElement);
     AddSemanticRange(node.CloseAngle);
     base.VisitMarkupEndTag(node);
 }
Example #5
0
        public override void VisitMarkupEndTag(MarkupEndTagSyntax node)
        {
            AddSemanticRange(node.OpenAngle, RazorSemanticTokensLegend.MarkupTagDelimiter);
            if (node.Bang != null)
            {
                AddSemanticRange(node.Bang, RazorSemanticTokensLegend.MarkupElement);
            }

            if (node.ForwardSlash != null)
            {
                AddSemanticRange(node.ForwardSlash, RazorSemanticTokensLegend.MarkupTagDelimiter);
            }
            AddSemanticRange(node.Name, RazorSemanticTokensLegend.MarkupElement);
            AddSemanticRange(node.CloseAngle, RazorSemanticTokensLegend.MarkupTagDelimiter);
        }
Example #6
0
            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);
            }
Example #7
0
            private static void OnAllowedChildrenEndTagError(
                TagHelperTracker tracker,
                string tagName,
                MarkupEndTagSyntax tagBlock,
                ErrorSink errorSink,
                RazorSourceDocument source)
            {
                var allowedChildrenString = string.Join(", ", tracker.AllowedChildren);
                var errorStart            = GetEndTagDeclarationErrorStart(tagBlock, source);

                errorSink.OnError(
                    RazorDiagnosticFactory.CreateTagHelper_InvalidNestedTag(
                        new SourceSpan(errorStart, tagName.Length),
                        tagName,
                        tracker.TagName,
                        allowedChildrenString));
            }
        public static TagMode GetTagMode(
            MarkupStartTagSyntax startTag,
            MarkupEndTagSyntax endTag,
            TagHelperBinding bindingResult)
        {
            var childSpan = startTag.GetLastToken()?.Parent;

            // Self-closing tags are always valid despite descriptors[X].TagStructure.
            if (childSpan?.GetContent().EndsWith("/>", StringComparison.Ordinal) ?? false)
            {
                return(TagMode.SelfClosing);
            }

            var hasDirectiveAttribute = false;

            foreach (var descriptor in bindingResult.Descriptors)
            {
                var boundRules     = bindingResult.Mappings[descriptor];
                var nonDefaultRule = boundRules.FirstOrDefault(rule => rule.TagStructure != TagStructure.Unspecified);

                if (nonDefaultRule?.TagStructure == TagStructure.WithoutEndTag)
                {
                    return(TagMode.StartTagOnly);
                }

                // Directive attribute will tolerate forms that don't work for tag helpers. For instance:
                //
                // <input @onclick="..."> vs <input onclick="..." />
                //
                // We don't want this to become an error just because you added a directive attribute.
                if (descriptor.IsAnyComponentDocumentTagHelper() && !descriptor.IsComponentOrChildContentTagHelper())
                {
                    hasDirectiveAttribute = true;
                }
            }

            if (hasDirectiveAttribute && startTag.IsVoidElement() && endTag == null)
            {
                return(TagMode.StartTagOnly);
            }

            return(TagMode.StartTagAndEndTag);
        }
            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 '<'.
                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);
            }
Example #10
0
        private static SyntaxList <RazorSyntaxNode> GetRewrittenMarkupEndTagChildren(MarkupEndTagSyntax node)
        {
            // Rewrites the children of the end tag to look like the legacy syntax tree.
            if (node.IsMarkupTransition)
            {
                var tokens       = node.DescendantNodes().Where(n => n is SyntaxToken token && !token.IsMissing).Cast <SyntaxToken>().ToArray();
                var tokenBuilder = SyntaxListBuilder <SyntaxToken> .Create();

                tokenBuilder.AddRange(tokens, 0, tokens.Length);
                var markupTransition = SyntaxFactory.MarkupTransition(tokenBuilder.ToList()).Green.CreateRed(node, node.Position);
                var spanContext      = node.GetSpanContext();
                if (spanContext != null)
                {
                    markupTransition = markupTransition.WithSpanContext(spanContext);
                }

                var builder = new SyntaxListBuilder(1);
                builder.Add(markupTransition);
                return(new SyntaxList <RazorSyntaxNode>(builder.ToListNode().CreateRed(node, node.Position)));
            }

            return(node.Children);
        }
Example #11
0
 private static bool IsPartialEndTag(MarkupEndTagSyntax endTag)
 {
     return(endTag.CloseAngle.IsMissing);
 }
Example #12
0
 private bool IsPotentialTagHelperEnd(string tagName, MarkupEndTagSyntax endTag)
 {
     return(!string.Equals(tagName, SyntaxConstants.TextTagName, StringComparison.OrdinalIgnoreCase) ||
            !endTag.IsMarkupTransition);
 }
Example #13
0
            private bool TryRewriteTagHelperEnd(MarkupStartTagSyntax startTag, MarkupEndTagSyntax endTag, out MarkupTagHelperEndTagSyntax rewritten)
            {
                rewritten = null;
                var tagName = endTag.GetTagNameWithOptionalBang();

                // Could not determine tag name, it can't be a TagHelper, continue on and track the element.
                if (string.IsNullOrEmpty(tagName) || tagName.StartsWith("!", StringComparison.Ordinal))
                {
                    return(false);
                }

                var tracker      = CurrentTagHelperTracker;
                var tagNameScope = tracker?.TagName ?? string.Empty;

                if (!IsPotentialTagHelperEnd(tagName, endTag))
                {
                    return(false);
                }

                // Validate that our end tag matches the currently scoped tag, if not we may need to error.
                if (startTag != null && tagNameScope.Equals(tagName, StringComparison.OrdinalIgnoreCase))
                {
                    // If there are additional end tags required before we can build our block it means we're in a
                    // situation like this: <myth req="..."><myth></myth></myth> where we're at the inside </myth>.
                    if (tracker.OpenMatchingTags > 0)
                    {
                        tracker.OpenMatchingTags--;

                        return(false);
                    }

                    ValidateEndTagSyntax(tagName, endTag);

                    _trackerStack.Pop();
                }
                else
                {
                    var tagHelperBinding = _tagHelperBinder.GetBinding(
                        tagName,
                        attributes: Array.Empty <KeyValuePair <string, string> >(),
                        parentTagName: CurrentParentTagName,
                        parentIsTagHelper: CurrentParentIsTagHelper);

                    // If there are not TagHelperDescriptors associated with the end tag block that also have no
                    // required attributes then it means we can't be a TagHelper, bail out.
                    if (tagHelperBinding == null)
                    {
                        return(false);
                    }

                    foreach (var descriptor in tagHelperBinding.Descriptors)
                    {
                        var boundRules  = tagHelperBinding.Mappings[descriptor];
                        var invalidRule = boundRules.FirstOrDefault(rule => rule.TagStructure == TagStructure.WithoutEndTag);

                        if (invalidRule != null)
                        {
                            // End tag TagHelper that states it shouldn't have an end tag.
                            _errorSink.OnError(
                                RazorDiagnosticFactory.CreateParsing_TagHelperMustNotHaveAnEndTag(
                                    new SourceSpan(SourceLocationTracker.Advance(endTag.GetSourceLocation(_source), "</"), tagName.Length),
                                    tagName,
                                    descriptor.DisplayName,
                                    invalidRule.TagStructure));

                            return(false);
                        }
                    }
                }

                rewritten = SyntaxFactory.MarkupTagHelperEndTag(
                    endTag.OpenAngle, endTag.ForwardSlash, endTag.Bang, endTag.Name, endTag.MiscAttributeContent, endTag.CloseAngle);

                return(true);
            }
Example #14
0
            private bool TryRewriteTagHelperStart(
                MarkupStartTagSyntax startTag,
                MarkupEndTagSyntax endTag,
                out MarkupTagHelperStartTagSyntax rewritten,
                out TagHelperInfo tagHelperInfo)
            {
                rewritten     = null;
                tagHelperInfo = null;

                // Get tag name of the current block
                var tagName = startTag.GetTagNameWithOptionalBang();

                // Could not determine tag name, it can't be a TagHelper, continue on and track the element.
                if (string.IsNullOrEmpty(tagName) || tagName.StartsWith("!", StringComparison.Ordinal))
                {
                    return(false);
                }

                TagHelperBinding tagHelperBinding;

                if (!IsPotentialTagHelperStart(tagName, startTag))
                {
                    return(false);
                }

                var tracker      = CurrentTagHelperTracker;
                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.
                var elementAttributes = GetAttributeNameValuePairs(startTag);

                tagHelperBinding = _tagHelperBinder.GetBinding(
                    tagName,
                    elementAttributes,
                    CurrentParentTagName,
                    CurrentParentIsTagHelper);

                // If there aren't any TagHelperDescriptors registered then we aren't a TagHelper
                if (tagHelperBinding == null)
                {
                    // If the current tag matches the current TagHelper scope it means the parent TagHelper matched
                    // all the required attributes but the current one did not; therefore, we need to increment the
                    // OpenMatchingTags counter for current the TagHelperBlock so we don't end it too early.
                    // ex: <myth req="..."><myth></myth></myth> We don't want the first myth to close on the inside
                    // tag.
                    if (string.Equals(tagNameScope, tagName, StringComparison.OrdinalIgnoreCase))
                    {
                        tracker.OpenMatchingTags++;
                    }

                    return(false);
                }

                ValidateParentAllowsTagHelper(tagName, startTag);
                ValidateBinding(tagHelperBinding, tagName, startTag);

                // We're in a start TagHelper block.
                ValidateStartTagSyntax(tagName, startTag);

                var rewrittenStartTag = TagHelperBlockRewriter.Rewrite(
                    tagName,
                    _featureFlags,
                    startTag,
                    tagHelperBinding,
                    _errorSink,
                    _source);

                var tagMode = TagHelperBlockRewriter.GetTagMode(startTag, endTag, tagHelperBinding);

                tagHelperInfo = new TagHelperInfo(tagName, tagMode, tagHelperBinding);
                rewritten     = rewrittenStartTag;

                return(true);
            }
Example #15
0
 public override void VisitMarkupEndTag(MarkupEndTagSyntax node)
 {
     WriteNode(node, isHtml: true, base.VisitMarkupEndTag);
 }
Example #16
0
 private static SourceLocation GetEndTagDeclarationErrorStart(MarkupEndTagSyntax tagBlock, RazorSourceDocument source)
 {
     return(SourceLocationTracker.Advance(tagBlock.GetSourceLocation(source), "</"));
 }
 public override void VisitMarkupEndTag(MarkupEndTagSyntax node)
 {
     WriteBlock(node, BlockKindInternal.Tag, base.VisitMarkupEndTag);
 }