private static TryParseResult TryParseBlock( string tagName, Block block, IEnumerable <TagHelperDescriptor> descriptors, ErrorSink errorSink, HashSet <string> processedBoundAttributeNames) { // TODO: Accept more than just spans: https://github.com/aspnet/Razor/issues/96. // The first child will only ever NOT be a Span if a user is doing something like: // <input @checked /> var childSpan = block.Children.First() as Span; if (childSpan == null || childSpan.Kind != SpanKindInternal.Markup) { errorSink.OnError( block.Start, LegacyResources.FormatTagHelpers_CannotHaveCSharpInTagDeclaration(tagName), block.Length); return(null); } var builder = new BlockBuilder(block); // If there's only 1 child it means that it's plain text inside of the attribute. // i.e. <div class="plain text in attribute"> if (builder.Children.Count == 1) { return(TryParseSpan(childSpan, descriptors, errorSink, processedBoundAttributeNames)); } var nameSymbols = childSpan .Symbols .OfType <HtmlSymbol>() .SkipWhile(symbol => !HtmlMarkupParser.IsValidAttributeNameSymbol(symbol)) // Skip prefix .TakeWhile(nameSymbol => HtmlMarkupParser.IsValidAttributeNameSymbol(nameSymbol)) .Select(nameSymbol => nameSymbol.Content); var name = string.Concat(nameSymbols); if (string.IsNullOrEmpty(name)) { errorSink.OnError( childSpan.Start, LegacyResources.FormatTagHelpers_AttributesMustHaveAName(tagName), childSpan.Length); return(null); } // Have a name now. Able to determine correct isBoundNonStringAttribute value. var result = CreateTryParseResult(name, descriptors, processedBoundAttributeNames); var firstChild = builder.Children[0] as Span; if (firstChild != null && firstChild.Symbols[0] is HtmlSymbol) { var htmlSymbol = firstChild.Symbols[firstChild.Symbols.Count - 1] as HtmlSymbol; switch (htmlSymbol.Type) { case HtmlSymbolType.Equals: if (builder.Children.Count == 2 && builder.Children[1] is Span value && value.Kind == SpanKindInternal.Markup) { // Attribute value is a string literal. Eg: <tag my-attribute=foo />. result.AttributeStructure = AttributeStructure.NoQuotes; } else { // Could be an expression, treat NoQuotes and DoubleQuotes equivalently. We purposefully do not persist NoQuotes // ValueStyles at code generation time to protect users from rendering dynamic content with spaces // that can break attributes. // Ex: <tag my-attribute=@value /> where @value results in the test "hello world". // This way, the above code would render <tag my-attribute="hello world" />. result.AttributeStructure = AttributeStructure.DoubleQuotes; } break;