Fix: Nested tag helpers do not work when tag helper prefix is set

This commit is contained in:
Ajay Bhargav Baaskaran 2017-08-10 12:18:39 -07:00
parent db805eb3e3
commit 45732a5dd3
13 changed files with 218 additions and 38 deletions

View File

@ -44,7 +44,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private readonly Stack<BlockBuilder> _blockStack;
private TagHelperBlockTracker _currentTagHelperTracker;
private BlockBuilder _currentBlock;
private string _currentParentTagName;
public TagHelperParseTreeRewriter(string tagHelperPrefix, IEnumerable<TagHelperDescriptor> descriptors)
{
@ -56,6 +55,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
_htmlAttributeTracker = new List<KeyValuePair<string, string>>();
}
private TagBlockTracker CurrentTracker => _trackerStack.Count > 0 ? _trackerStack.Peek() : null;
private string CurrentParentTagName => CurrentTracker?.TagName;
private bool CurrentParentIsTagHelper => CurrentTracker?.IsTagHelper ?? false;
public Block Rewrite(Block syntaxTree, ErrorSink errorSink)
{
RewriteTags(syntaxTree, errorSink, depth: 0);
@ -189,7 +194,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// 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);
tagHelperBinding = _tagHelperBinder.GetBinding(tagName, elementAttributes, _currentParentTagName);
tagHelperBinding = _tagHelperBinder.GetBinding(
tagName,
elementAttributes,
CurrentParentTagName,
CurrentParentIsTagHelper);
// If there aren't any TagHelperDescriptors registered then we aren't a TagHelper
if (tagHelperBinding == null)
@ -257,7 +266,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
tagHelperBinding = _tagHelperBinder.GetBinding(
tagName,
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: _currentParentTagName);
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.
@ -508,10 +518,20 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return;
}
var currentTracker = _trackerStack.Count > 0 ? _trackerStack.Peek() : null;
if (!HasAllowedChildren())
{
return;
}
if (HasAllowedChildren() &&
!_currentTagHelperTracker.AllowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase))
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))
{
OnAllowedChildrenTagError(_currentTagHelperTracker, tagName, tagBlock, errorSink);
}
@ -812,14 +832,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void PushTrackerStack(TagBlockTracker tracker)
{
_currentParentTagName = tracker.TagName;
_trackerStack.Push(tracker);
}
private TagBlockTracker PopTrackerStack()
{
var poppedTracker = _trackerStack.Pop();
_currentParentTagName = _trackerStack.Count > 0 ? _trackerStack.Peek().TagName : null;
return poppedTracker;
}

View File

@ -39,12 +39,14 @@ namespace Microsoft.AspNetCore.Razor.Language
/// retrieves catch-all <see cref="TagHelperDescriptor"/>s (descriptors that target every tag).</param>
/// <param name="attributes">Attributes on the HTML tag.</param>
/// <param name="parentTagName">The parent tag name of the given <paramref name="tagName"/> tag.</param>
/// <param name="parentIsTagHelper">Is the parent tag of the given <paramref name="tagName"/> tag a tag helper.</param>
/// <returns><see cref="TagHelperDescriptor"/>s that apply to the given HTML tag criteria.
/// Will return <c>null</c> if no <see cref="TagHelperDescriptor"/>s are a match.</returns>
public TagHelperBinding GetBinding(
string tagName,
IReadOnlyList<KeyValuePair<string, string>> attributes,
string parentTagName)
string parentTagName,
bool parentIsTagHelper)
{
if (!string.IsNullOrEmpty(_tagHelperPrefix) &&
(tagName.Length <= _tagHelperPrefix.Length ||
@ -74,11 +76,17 @@ namespace Microsoft.AspNetCore.Razor.Language
}
var tagNameWithoutPrefix = _tagHelperPrefix != null ? tagName.Substring(_tagHelperPrefix.Length) : tagName;
var parentTagNameWithoutPrefix = parentTagName;
if (_tagHelperPrefix != null && parentIsTagHelper)
{
parentTagNameWithoutPrefix = parentTagName.Substring(_tagHelperPrefix.Length);
}
Dictionary<TagHelperDescriptor, IReadOnlyList<TagMatchingRuleDescriptor>> applicableDescriptorMappings = null;
foreach (var descriptor in descriptors)
{
var applicableRules = descriptor.TagMatchingRules.Where(
rule => TagHelperMatchingConventions.SatisfiesRule(tagNameWithoutPrefix, parentTagName, attributes, rule));
rule => TagHelperMatchingConventions.SatisfiesRule(tagNameWithoutPrefix, parentTagNameWithoutPrefix, attributes, rule));
if (applicableRules.Any())
{

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Razor.Language
public static bool SatisfiesRule(
string tagNameWithoutPrefix,
string parentTagName,
string parentTagNameWithoutPrefix,
IEnumerable<KeyValuePair<string, string>> tagAttributes,
TagMatchingRuleDescriptor rule)
{
@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Razor.Language
return false;
}
var satisfiesParentTag = SatisfiesParentTag(parentTagName, rule);
var satisfiesParentTag = SatisfiesParentTag(parentTagNameWithoutPrefix, rule);
if (!satisfiesParentTag)
{
return false;
@ -89,14 +89,14 @@ namespace Microsoft.AspNetCore.Razor.Language
return true;
}
public static bool SatisfiesParentTag(string parentTagName, TagMatchingRuleDescriptor rule)
public static bool SatisfiesParentTag(string parentTagNameWithoutPrefix, TagMatchingRuleDescriptor rule)
{
if (rule == null)
{
throw new ArgumentNullException(nameof(rule));
}
if (rule.ParentTag != null && !string.Equals(parentTagName, rule.ParentTag, StringComparison.OrdinalIgnoreCase))
if (rule.ParentTag != null && !string.Equals(parentTagNameWithoutPrefix, rule.ParentTag, StringComparison.OrdinalIgnoreCase))
{
return false;
}

View File

@ -55,7 +55,13 @@ namespace Microsoft.CodeAnalysis.Razor
var prefix = documentContext.Prefix ?? string.Empty;
Debug.Assert(completionContext.CurrentTagName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
var applicableTagHelperBinding = _tagHelperFactsService.GetTagHelperBinding(documentContext, completionContext.CurrentTagName, completionContext.Attributes, completionContext.CurrentParentTagName);
var applicableTagHelperBinding = _tagHelperFactsService.GetTagHelperBinding(
documentContext,
completionContext.CurrentTagName,
completionContext.Attributes,
completionContext.CurrentParentTagName,
parentIsTagHelper: false);
var applicableDescriptors = applicableTagHelperBinding?.Descriptors ?? Enumerable.Empty<TagHelperDescriptor>();
var unprefixedTagName = completionContext.CurrentTagName.Substring(prefix.Length);
@ -227,11 +233,13 @@ namespace Microsoft.CodeAnalysis.Razor
}
var prefix = completionContext.DocumentContext.Prefix ?? string.Empty;
var binding = _tagHelperFactsService.GetTagHelperBinding(
completionContext.DocumentContext,
completionContext.ContainingTagName,
completionContext.Attributes,
completionContext.ContainingParentTagName);
completionContext.ContainingParentTagName,
completionContext.ContainingParentIsTagHelper);
if (binding == null)
{

View File

@ -14,7 +14,8 @@ namespace Microsoft.CodeAnalysis.Razor
TagHelperDocumentContext documentContext,
string tagName,
IEnumerable<KeyValuePair<string, string>> attributes,
string parentTag)
string parentTag,
bool parentIsTagHelper)
{
if (documentContext == null)
{
@ -39,7 +40,7 @@ namespace Microsoft.CodeAnalysis.Razor
var prefix = documentContext.Prefix;
var tagHelperBinder = new TagHelperBinder(prefix, descriptors);
var binding = tagHelperBinder.GetBinding(tagName, attributes.ToList(), parentTag);
var binding = tagHelperBinder.GetBinding(tagName, attributes.ToList(), parentTag, parentIsTagHelper);
return binding;
}

View File

@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.Razor
string containingTagName,
IEnumerable<KeyValuePair<string, string>> attributes,
string containingParentTagName,
bool containingParentIsTagHelper,
Func<string, bool> inHTMLSchema)
{
if (documentContext == null)
@ -37,6 +38,7 @@ namespace Microsoft.CodeAnalysis.Razor
ContainingTagName = containingTagName;
Attributes = attributes;
ContainingParentTagName = containingParentTagName;
ContainingParentIsTagHelper = containingParentIsTagHelper;
InHTMLSchema = inHTMLSchema;
}
@ -50,6 +52,8 @@ namespace Microsoft.CodeAnalysis.Razor
public string ContainingParentTagName { get; }
public bool ContainingParentIsTagHelper { get; }
public Func<string, bool> InHTMLSchema { get; }
}
}

View File

@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
public abstract class TagHelperFactsService : ILanguageService
{
public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable<KeyValuePair<string, string>> attributes, string parentTag);
public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable<KeyValuePair<string, string>> attributes, string parentTag, bool parentIsTagHelper);
public abstract IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding);

View File

@ -62,7 +62,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
var prefix = documentContext.Prefix ?? string.Empty;
Debug.Assert(completionContext.CurrentTagName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
var applicableTagHelperBinding = _tagHelperFactsService.GetTagHelperBinding(documentContext, completionContext.CurrentTagName, completionContext.Attributes, completionContext.CurrentParentTagName);
var applicableTagHelperBinding = _tagHelperFactsService.GetTagHelperBinding(
documentContext,
completionContext.CurrentTagName,
completionContext.Attributes,
completionContext.CurrentParentTagName);
var applicableDescriptors = applicableTagHelperBinding?.Descriptors ?? Enumerable.Empty<TagHelperDescriptor>();
var unprefixedTagName = completionContext.CurrentTagName.Substring(prefix.Length);

View File

@ -45,7 +45,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
var prefix = documentContext.Prefix;
var tagHelperBinder = new TagHelperBinder(prefix, descriptors);
var binding = tagHelperBinder.GetBinding(tagName, attributes.ToList(), parentTag);
var binding = tagHelperBinder.GetBinding(tagName, attributes.ToList(), parentTag, parentIsTagHelper: false);
return binding;
}

View File

@ -385,6 +385,99 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
tagHelperPrefix: "th:");
}
[Fact]
public void Rewrite_UnderstandsTagHelperPrefixAndAllowedChildrenAndRequireParent()
{
// Arrange
var documentContent = "<th:p><th:strong></th:strong></th:p>";
var expectedOutput = new MarkupBlock(
new MarkupTagHelperBlock("th:p",
new MarkupTagHelperBlock("th:strong")));
var descriptors = new TagHelperDescriptor[]
{
TagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
.AllowChildTag("strong")
.Build(),
TagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong").RequireParentTag("p"))
.Build(),
};
// Act & Assert
EvaluateData(
descriptors,
documentContent,
expectedOutput,
expectedErrors: Enumerable.Empty<RazorError>(),
tagHelperPrefix: "th:");
}
[Fact]
public void Rewrite_InvalidStructure_UnderstandsTagHelperPrefixAndAllowedChildrenAndRequireParent()
{
// Arrange
var documentContent = "<th:p></th:strong></th:p>";
var expectedOutput = new MarkupBlock(
new MarkupTagHelperBlock("th:p",
new MarkupTagBlock(
Factory.Markup("</th:strong>"))));
var descriptors = new TagHelperDescriptor[]
{
TagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
.AllowChildTag("strong")
.Build(),
TagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong").RequireParentTag("p"))
.Build(),
};
var expectedErrors = new[] {
new RazorError(
LegacyResources.FormatTagHelpersParseTreeRewriter_FoundMalformedTagHelper("th:strong"),
absoluteIndex: 8,
lineIndex: 0,
columnIndex: 8,
length: 9),
};
// Act & Assert
EvaluateData(
descriptors,
documentContent,
expectedOutput,
expectedErrors: expectedErrors,
tagHelperPrefix: "th:");
}
[Fact]
public void Rewrite_NonTagHelperChild_UnderstandsTagHelperPrefixAndAllowedChildren()
{
// Arrange
var documentContent = "<th:p><strong></strong></th:p>";
var expectedOutput = new MarkupBlock(
new MarkupTagHelperBlock("th:p",
new MarkupTagBlock(
Factory.Markup("<strong>")),
new MarkupTagBlock(
Factory.Markup("</strong>"))));
var descriptors = new TagHelperDescriptor[]
{
TagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
.AllowChildTag("strong")
.Build(),
};
// Act & Assert
EvaluateData(
descriptors,
documentContent,
expectedOutput,
expectedErrors: Enumerable.Empty<RazorError>(),
tagHelperPrefix: "th:");
}
public static TheoryData InvalidHtmlScriptBlockData
{
get

View File

@ -28,7 +28,8 @@ namespace Microsoft.AspNetCore.Razor.Language
var bindingResult = tagHelperBinder.GetBinding(
tagName: "th:div",
attributes: expectedAttributes,
parentTagName: "body");
parentTagName: "body",
parentIsTagHelper: false);
// Assert
Assert.Equal(expectedDescriptors, bindingResult.Descriptors, TagHelperDescriptorComparer.CaseSensitive);
@ -109,7 +110,8 @@ namespace Microsoft.AspNetCore.Razor.Language
var bindingResult = tagHelperBinder.GetBinding(
tagName,
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: parentTagName);
parentTagName: parentTagName,
parentIsTagHelper: false);
// Assert
Assert.Equal((IEnumerable<TagHelperDescriptor>)expectedDescriptors, bindingResult.Descriptors, TagHelperDescriptorComparer.CaseSensitive);
@ -272,7 +274,7 @@ namespace Microsoft.AspNetCore.Razor.Language
var tagHelperBinder = new TagHelperBinder(null, (IReadOnlyList<TagHelperDescriptor>)availableDescriptors);
// Act
var bindingResult = tagHelperBinder.GetBinding(tagName, providedAttributes, parentTagName: "p");
var bindingResult = tagHelperBinder.GetBinding(tagName, providedAttributes, parentTagName: "p", parentIsTagHelper: false);
// Assert
Assert.Equal((IEnumerable<TagHelperDescriptor>)expectedDescriptors, bindingResult?.Descriptors, TagHelperDescriptorComparer.CaseSensitive);
@ -292,7 +294,8 @@ namespace Microsoft.AspNetCore.Razor.Language
var bindingResult = tagHelperBinder.GetBinding(
tagName: "th",
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: "p");
parentTagName: "p",
parentIsTagHelper: false);
// Assert
Assert.Null(bindingResult);
@ -312,11 +315,13 @@ namespace Microsoft.AspNetCore.Razor.Language
var bindingResultDiv = tagHelperBinder.GetBinding(
tagName: "th:div",
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: "p");
parentTagName: "p",
parentIsTagHelper: false);
var bindingResultSpan = tagHelperBinder.GetBinding(
tagName: "th:span",
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: "p");
parentTagName: "p",
parentIsTagHelper: false);
// Assert
var descriptor = Assert.Single(bindingResultDiv.Descriptors);
@ -339,7 +344,8 @@ namespace Microsoft.AspNetCore.Razor.Language
var bindingResult = tagHelperBinder.GetBinding(
tagName: "th:div",
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: "p");
parentTagName: "p",
parentIsTagHelper: false);
// Assert
var descriptor = Assert.Single(bindingResult.Descriptors);
@ -362,7 +368,8 @@ namespace Microsoft.AspNetCore.Razor.Language
var bindingResult = tagHelperBinder.GetBinding(
tagName: "div",
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: "p");
parentTagName: "p",
parentIsTagHelper: false);
// Assert
Assert.Null(bindingResult);
@ -385,7 +392,8 @@ namespace Microsoft.AspNetCore.Razor.Language
var tagHelperBinding = tagHelperBinder.GetBinding(
tagName: "foo",
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: "p");
parentTagName: "p",
parentIsTagHelper: false);
// Assert
Assert.Null(tagHelperBinding);
@ -411,11 +419,13 @@ namespace Microsoft.AspNetCore.Razor.Language
var divBinding = tagHelperBinder.GetBinding(
tagName: "div",
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: "p");
parentTagName: "p",
parentIsTagHelper: false);
var spanBinding = tagHelperBinder.GetBinding(
tagName: "span",
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: "p");
parentTagName: "p",
parentIsTagHelper: false);
// Assert
// For divs
@ -443,7 +453,8 @@ namespace Microsoft.AspNetCore.Razor.Language
var bindingResult = tagHelperBinder.GetBinding(
tagName: "div",
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: "p");
parentTagName: "p",
parentIsTagHelper: false);
// Assert
var descriptor = Assert.Single(bindingResult.Descriptors);
@ -470,7 +481,8 @@ namespace Microsoft.AspNetCore.Razor.Language
var binding = tagHelperBinder.GetBinding(
tagName: "div",
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: "p");
parentTagName: "p",
parentIsTagHelper: false);
// Assert
var boundDescriptor = Assert.Single(binding.Descriptors);
@ -479,5 +491,34 @@ namespace Microsoft.AspNetCore.Razor.Language
var boundRule = Assert.Single(boundRules);
Assert.Equal("div", boundRule.TagName);
}
[Fact]
public void GetBinding_PrefixedParent_ReturnsBinding()
{
// Arrange
var divDescriptor = TagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("div").RequireParentTag("p"))
.Build();
var pDescriptor = TagHelperDescriptorBuilder.Create("foo2", "SomeAssembly")
.TagMatchingRuleDescriptor(rule => rule.RequireTagName("p"))
.Build();
var descriptors = new[] { divDescriptor, pDescriptor };
var tagHelperBinder = new TagHelperBinder("th:", descriptors);
// Act
var bindingResult = tagHelperBinder.GetBinding(
tagName: "th:div",
attributes: Array.Empty<KeyValuePair<string, string>>(),
parentTagName: "th:p",
parentIsTagHelper: true);
// Assert
var boundDescriptor = Assert.Single(bindingResult.Descriptors);
Assert.Same(divDescriptor, boundDescriptor);
var boundRules = bindingResult.GetBoundRules(boundDescriptor);
var boundRule = Assert.Single(boundRules);
Assert.Equal("div", boundRule.TagName);
Assert.Equal("p", boundRule.ParentTag);
}
}
}

View File

@ -927,6 +927,7 @@ namespace Microsoft.CodeAnalysis.Razor
IEnumerable<string> existingCompletions,
string containingTagName,
string containingParentTagName = "body",
bool containingParentIsTagHelper = false,
string tagHelperPrefix = "")
{
var documentContext = TagHelperDocumentContext.Create(tagHelperPrefix, descriptors);
@ -936,6 +937,7 @@ namespace Microsoft.CodeAnalysis.Razor
containingTagName,
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
containingParentTagName: containingParentTagName,
containingParentIsTagHelper: containingParentIsTagHelper,
inHTMLSchema: (tag) => tag == "strong" || tag == "b" || tag == "bold" || tag == "li" || tag == "div");
return completionContext;

View File

@ -27,7 +27,7 @@ namespace Microsoft.CodeAnalysis.Razor
var service = new DefaultTagHelperFactsService();
// Act
var binding = service.GetTagHelperBinding(documentContext, "!a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null);
var binding = service.GetTagHelperBinding(documentContext, "!a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
// Assert
Assert.Null(binding);
@ -73,7 +73,7 @@ namespace Microsoft.CodeAnalysis.Razor
};
// Act
var binding = service.GetTagHelperBinding(documentContext, "a", attributes, parentTag: "p");
var binding = service.GetTagHelperBinding(documentContext, "a", attributes, parentTag: "p", parentIsTagHelper: false);
// Assert
var descriptor = Assert.Single(binding.Descriptors);
@ -109,7 +109,7 @@ namespace Microsoft.CodeAnalysis.Razor
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsService();
var binding = service.GetTagHelperBinding(documentContext, "a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null);
var binding = service.GetTagHelperBinding(documentContext, "a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
// Act
var descriptors = service.GetBoundTagHelperAttributes(documentContext, "asp-route-something", binding);
@ -144,7 +144,7 @@ namespace Microsoft.CodeAnalysis.Razor
};
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
var service = new DefaultTagHelperFactsService();
var binding = service.GetTagHelperBinding(documentContext, "input", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null);
var binding = service.GetTagHelperBinding(documentContext, "input", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null, parentIsTagHelper: false);
// Act
var descriptors = service.GetBoundTagHelperAttributes(documentContext, "asp-for", binding);