// Determines the full name of the Type of the property corresponding to an attribute with the given name.
        private static string GetPropertyType(string name, IEnumerable <TagHelperDescriptor> descriptors)
        {
            foreach (var descriptor in descriptors)
            {
                if (TagHelperMatchingConventions.TryGetFirstBoundAttributeMatch(name, descriptor, out var firstBoundAttribute, out var indexerMatch, out var _, out var _))
                {
                    if (indexerMatch)
                    {
                        return(firstBoundAttribute.IndexerTypeName);
                    }
                    else
                    {
                        return(firstBoundAttribute.TypeName);
                    }
                }
            }

            return(null);
        }
        // Create a TryParseResult for given name, filling in binding details.
        private static TryParseResult CreateTryParseResult(
            string name,
            IEnumerable <TagHelperDescriptor> descriptors,
            HashSet <string> processedBoundAttributeNames)
        {
            var isBoundAttribute          = false;
            var isBoundNonStringAttribute = false;
            var isBoundBooleanAttribute   = false;
            var isMissingDictionaryKey    = false;
            var isDirectiveAttribute      = false;

            foreach (var descriptor in descriptors)
            {
                if (TagHelperMatchingConventions.TryGetFirstBoundAttributeMatch(
                        name,
                        descriptor,
                        out var firstBoundAttribute,
                        out var indexerMatch,
                        out var parameterMatch,
                        out var boundAttributeParameter))
                {
                    isBoundAttribute = true;
                    if (parameterMatch)
                    {
                        isBoundNonStringAttribute = !boundAttributeParameter.IsStringProperty;
                        isBoundBooleanAttribute   = boundAttributeParameter.IsBooleanProperty;
                        isMissingDictionaryKey    = false;
                    }
                    else
                    {
                        isBoundNonStringAttribute = !firstBoundAttribute.ExpectsStringValue(name);
                        isBoundBooleanAttribute   = firstBoundAttribute.ExpectsBooleanValue(name);
                        isMissingDictionaryKey    = firstBoundAttribute.IndexerNamePrefix != null &&
                                                    name.Length == firstBoundAttribute.IndexerNamePrefix.Length;
                    }

                    isDirectiveAttribute = firstBoundAttribute.IsDirectiveAttribute();

                    break;
                }
            }

            var isDuplicateAttribute = false;

            if (isBoundAttribute && !processedBoundAttributeNames.Add(name))
            {
                // A bound attribute with the same name has already been processed.
                isDuplicateAttribute = true;
            }

            return(new TryParseResult
            {
                AttributeName = name,
                IsBoundAttribute = isBoundAttribute,
                IsBoundNonStringAttribute = isBoundNonStringAttribute,
                IsBoundBooleanAttribute = isBoundBooleanAttribute,
                IsMissingDictionaryKey = isMissingDictionaryKey,
                IsDuplicateAttribute = isDuplicateAttribute,
                IsDirectiveAttribute = isDirectiveAttribute
            });
        }