private static void EvaluateTagHelperAttribute( ErrorCollector collector, TagHelperAttributeNode actual, TagHelperAttributeNode expected) { if (actual.Name != expected.Name) { collector.AddError("{0} - FAILED :: Attribute names do not match", expected.Name); } else { collector.AddMessage("{0} - PASSED :: Attribute names match", expected.Name); } if (actual.AttributeStructure != expected.AttributeStructure) { collector.AddError("{0} - FAILED :: Attribute value styles do not match", expected.AttributeStructure.ToString()); } else { collector.AddMessage("{0} - PASSED :: Attribute value style match", expected.AttributeStructure); } if (actual.AttributeStructure != AttributeStructure.Minimized) { EvaluateSyntaxTreeNode(collector, actual.Value, expected.Value); } }
private static IList <TagHelperAttributeNode> GetTagAttributes( string tagName, bool validStructure, Block tagBlock, TagHelperBinding bindingResult, ErrorSink errorSink) { var attributes = new List <TagHelperAttributeNode>(); // We skip the first child "<tagname" and take everything up to the ending portion of the tag ">" or "/>". // The -2 accounts for both the start and end tags. If the tag does not have a valid structure then there's // no end tag to ignore. var symbolOffset = validStructure ? 2 : 1; var attributeChildren = tagBlock.Children.Skip(1).Take(tagBlock.Children.Count() - symbolOffset); var processedBoundAttributeNames = new HashSet <string>(StringComparer.OrdinalIgnoreCase); foreach (var child in attributeChildren) { TryParseResult result; if (child.IsBlock) { result = TryParseBlock(tagName, (Block)child, bindingResult.Descriptors, errorSink, processedBoundAttributeNames); } else { result = TryParseSpan((Span)child, bindingResult.Descriptors, errorSink, processedBoundAttributeNames); } // Only want to track the attribute if we succeeded in parsing its corresponding Block/Span. if (result != null) { SourceLocation?errorLocation = null; // Check if it's a bound attribute that is minimized or if it's a bound non-string attribute that // is null or whitespace. if ((result.IsBoundAttribute && result.AttributeValueNode == null) || (result.IsBoundNonStringAttribute && IsNullOrWhitespaceAttributeValue(result.AttributeValueNode))) { errorLocation = GetAttributeNameStartLocation(child); errorSink.OnError( errorLocation.Value, LegacyResources.FormatRewriterError_EmptyTagHelperBoundAttribute( result.AttributeName, tagName, GetPropertyType(result.AttributeName, bindingResult.Descriptors)), result.AttributeName.Length); } // Check if the attribute was a prefix match for a tag helper dictionary property but the // dictionary key would be the empty string. if (result.IsMissingDictionaryKey) { if (!errorLocation.HasValue) { errorLocation = GetAttributeNameStartLocation(child); } errorSink.OnError( errorLocation.Value, LegacyResources.FormatTagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey( result.AttributeName, tagName), result.AttributeName.Length); } var attributeNode = new TagHelperAttributeNode( result.AttributeName, result.AttributeValueNode, result.AttributeStructure); attributes.Add(attributeNode); } else { // Error occured while parsing the attribute. Don't try parsing the rest to avoid misleading errors. break; } } return(attributes); }
private static IList <TagHelperAttributeNode> GetTagAttributes( string tagName, bool validStructure, Block tagBlock, TagHelperBinding bindingResult, ErrorSink errorSink, RazorParserFeatureFlags featureFlags) { var attributes = new List <TagHelperAttributeNode>(); // We skip the first child "<tagname" and take everything up to the ending portion of the tag ">" or "/>". // The -2 accounts for both the start and end tags. If the tag does not have a valid structure then there's // no end tag to ignore. var symbolOffset = validStructure ? 2 : 1; var attributeChildren = tagBlock.Children.Skip(1).Take(tagBlock.Children.Count() - symbolOffset); var processedBoundAttributeNames = new HashSet <string>(StringComparer.OrdinalIgnoreCase); foreach (var child in attributeChildren) { TryParseResult result; if (child.IsBlock) { result = TryParseBlock(tagName, (Block)child, bindingResult.Descriptors, errorSink, processedBoundAttributeNames); } else { result = TryParseSpan((Span)child, bindingResult.Descriptors, errorSink, processedBoundAttributeNames); } // Only want to track the attribute if we succeeded in parsing its corresponding Block/Span. if (result != null) { // Check if it's a non-boolean bound attribute that is minimized or if it's a bound // non-string attribute that has null or whitespace content. var isMinimized = result.AttributeValueNode == null; var isValidMinimizedAttribute = featureFlags.AllowMinimizedBooleanTagHelperAttributes && result.IsBoundBooleanAttribute; if ((isMinimized && result.IsBoundAttribute && !isValidMinimizedAttribute) || (!isMinimized && result.IsBoundNonStringAttribute && IsNullOrWhitespaceAttributeValue(result.AttributeValueNode))) { var errorLocation = GetAttributeNameLocation(child, result.AttributeName); var propertyTypeName = GetPropertyType(result.AttributeName, bindingResult.Descriptors); var diagnostic = RazorDiagnosticFactory.CreateTagHelper_EmptyBoundAttribute(errorLocation, result.AttributeName, tagName, propertyTypeName); errorSink.OnError(diagnostic); } // Check if the attribute was a prefix match for a tag helper dictionary property but the // dictionary key would be the empty string. if (result.IsMissingDictionaryKey) { var errorLocation = GetAttributeNameLocation(child, result.AttributeName); var diagnostic = RazorDiagnosticFactory.CreateParsing_TagHelperIndexerAttributeNameMustIncludeKey(errorLocation, result.AttributeName, tagName); errorSink.OnError(diagnostic); } var attributeNode = new TagHelperAttributeNode( result.AttributeName, result.AttributeValueNode, result.AttributeStructure); attributes.Add(attributeNode); } else { // Error occured while parsing the attribute. Don't try parsing the rest to avoid misleading errors. break; } } return(attributes); }