private void ProcessDuplicates(IntermediateNode node) { // Reverse order because we will remove nodes. // // Each 'property' node could be duplicated if there are multiple tag helpers that match that // particular attribute. This is common in our approach, which relies on 'fallback' tag helpers // that overlap with more specific ones. for (var i = node.Children.Count - 1; i >= 0; i--) { // For each usage of the general 'fallback' bind tag helper, it could duplicate // the usage of a more specific one. Look for duplicates and remove the fallback. var attribute = node.Children[i] as TagHelperPropertyIntermediateNode; if (attribute != null && attribute.TagHelper != null && attribute.TagHelper.IsFallbackBindTagHelper()) { for (var j = 0; j < node.Children.Count; j++) { var duplicate = node.Children[j] as TagHelperPropertyIntermediateNode; if (duplicate != null && duplicate.TagHelper != null && duplicate.TagHelper.IsBindTagHelper() && duplicate.AttributeName == attribute.AttributeName && !object.ReferenceEquals(attribute, duplicate)) { // Found a duplicate - remove the 'fallback' in favor of the // more specific tag helper. node.Children.RemoveAt(i); break; } } } // Also treat the general <input bind="..." /> as a 'fallback' for that case and remove it. // This is a workaround for a limitation where you can't write a tag helper that binds only // when a specific attribute is **not** present. if (attribute != null && attribute.TagHelper != null && attribute.TagHelper.IsInputElementFallbackBindTagHelper()) { for (var j = 0; j < node.Children.Count; j++) { var duplicate = node.Children[j] as TagHelperPropertyIntermediateNode; if (duplicate != null && duplicate.TagHelper != null && duplicate.TagHelper.IsInputElementBindTagHelper() && duplicate.AttributeName == attribute.AttributeName && !object.ReferenceEquals(attribute, duplicate)) { // Found a duplicate - remove the 'fallback' input tag helper in favor of the // more specific tag helper. node.Children.RemoveAt(i); break; } } } } // If we still have duplicates at this point then they are genuine conflicts. var duplicates = node.Children .OfType <TagHelperPropertyIntermediateNode>() .GroupBy(p => p.AttributeName) .Where(g => g.Count() > 1); foreach (var duplicate in duplicates) { node.Diagnostics.Add(ComponentDiagnosticFactory.CreateBindAttribute_Duplicates( node.Source, duplicate.Key, duplicate.ToArray())); foreach (var property in duplicate) { node.Children.Remove(property); } } }