Use an explicit intermediate node for directive attributes (dotnet/aspnetcore-tooling#638)
* Use an explicit intermediate node for directive attributes
* More cleanup
\n\nCommit migrated from f8d7f4cc3b
This commit is contained in:
parent
6be2d3dfcd
commit
c588d53a42
|
|
@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return isIndexerNameMatch && attribute.IsIndexerBooleanProperty;
|
||||
}
|
||||
|
||||
internal static bool IsDirectiveAttribute(this BoundAttributeDescriptor attribute)
|
||||
public static bool IsDirectiveAttribute(this BoundAttributeDescriptor attribute)
|
||||
{
|
||||
if (attribute == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
|
||||
// For each @bind *usage* we need to rewrite the tag helper node to map to basic constructs.
|
||||
var references = documentNode.FindDescendantReferences<TagHelperPropertyIntermediateNode>();
|
||||
var parameterReferences = documentNode.FindDescendantReferences<TagHelperAttributeParameterIntermediateNode>();
|
||||
var references = documentNode.FindDescendantReferences<TagHelperDirectiveAttributeIntermediateNode>();
|
||||
var parameterReferences = documentNode.FindDescendantReferences<TagHelperDirectiveAttributeParameterIntermediateNode>();
|
||||
|
||||
var parents = new HashSet<IntermediateNode>();
|
||||
for (var i = 0; i < references.Count; i++)
|
||||
|
|
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
var reference = references[i];
|
||||
var parent = reference.Parent;
|
||||
var node = (TagHelperPropertyIntermediateNode)reference.Node;
|
||||
var node = (TagHelperDirectiveAttributeIntermediateNode)reference.Node;
|
||||
|
||||
if (!parent.Children.Contains(node))
|
||||
{
|
||||
|
|
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
continue;
|
||||
}
|
||||
|
||||
if (node.TagHelper.IsBindTagHelper() && node.IsDirectiveAttribute)
|
||||
if (node.TagHelper.IsBindTagHelper())
|
||||
{
|
||||
bindEntries[(parent, node.AttributeName)] = new BindEntry(reference);
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
var parameterReference = parameterReferences[i];
|
||||
var parent = parameterReference.Parent;
|
||||
var node = (TagHelperAttributeParameterIntermediateNode)parameterReference.Node;
|
||||
var node = (TagHelperDirectiveAttributeParameterIntermediateNode)parameterReference.Node;
|
||||
|
||||
if (!parent.Children.Contains(node))
|
||||
{
|
||||
|
|
@ -83,7 +83,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
continue;
|
||||
}
|
||||
|
||||
if (node.TagHelper.IsBindTagHelper() && node.IsDirectiveAttribute)
|
||||
if (node.TagHelper.IsBindTagHelper())
|
||||
{
|
||||
// Check if this tag contains a corresponding non-parameterized bind node.
|
||||
if (!bindEntries.TryGetValue((parent, node.AttributeNameWithoutParameter), out var entry))
|
||||
|
|
@ -140,12 +140,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
TagHelperDescriptor tagHelper = null;
|
||||
string attributeName = null;
|
||||
var attribute = node.Children[i];
|
||||
if (attribute is TagHelperPropertyIntermediateNode propertyAttribute)
|
||||
if (attribute is TagHelperDirectiveAttributeIntermediateNode directiveAttribute)
|
||||
{
|
||||
attributeName = propertyAttribute.AttributeName;
|
||||
tagHelper = propertyAttribute.TagHelper;
|
||||
attributeName = directiveAttribute.AttributeName;
|
||||
tagHelper = directiveAttribute.TagHelper;
|
||||
}
|
||||
else if (attribute is TagHelperAttributeParameterIntermediateNode parameterAttribute)
|
||||
else if (attribute is TagHelperDirectiveAttributeParameterIntermediateNode parameterAttribute)
|
||||
{
|
||||
attributeName = parameterAttribute.AttributeName;
|
||||
tagHelper = parameterAttribute.TagHelper;
|
||||
|
|
@ -159,12 +159,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
TagHelperDescriptor duplicateTagHelper = null;
|
||||
string duplicateAttributeName = null;
|
||||
var duplicate = node.Children[j];
|
||||
if (duplicate is TagHelperPropertyIntermediateNode duplicatePropertyAttribute)
|
||||
if (duplicate is TagHelperDirectiveAttributeIntermediateNode duplicateDirectiveAttribute)
|
||||
{
|
||||
duplicateAttributeName = duplicatePropertyAttribute.AttributeName;
|
||||
duplicateTagHelper = duplicatePropertyAttribute.TagHelper;
|
||||
duplicateAttributeName = duplicateDirectiveAttribute.AttributeName;
|
||||
duplicateTagHelper = duplicateDirectiveAttribute.TagHelper;
|
||||
}
|
||||
else if (duplicate is TagHelperAttributeParameterIntermediateNode duplicateParameterAttribute)
|
||||
else if (duplicate is TagHelperDirectiveAttributeParameterIntermediateNode duplicateParameterAttribute)
|
||||
{
|
||||
duplicateAttributeName = duplicateParameterAttribute.AttributeName;
|
||||
duplicateTagHelper = duplicateParameterAttribute.TagHelper;
|
||||
|
|
@ -195,12 +195,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
TagHelperDescriptor duplicateTagHelper = null;
|
||||
string duplicateAttributeName = null;
|
||||
var duplicate = node.Children[j];
|
||||
if (duplicate is TagHelperPropertyIntermediateNode duplicatePropertyAttribute)
|
||||
if (duplicate is TagHelperDirectiveAttributeIntermediateNode duplicateDirectiveAttribute)
|
||||
{
|
||||
duplicateAttributeName = duplicatePropertyAttribute.AttributeName;
|
||||
duplicateTagHelper = duplicatePropertyAttribute.TagHelper;
|
||||
duplicateAttributeName = duplicateDirectiveAttribute.AttributeName;
|
||||
duplicateTagHelper = duplicateDirectiveAttribute.TagHelper;
|
||||
}
|
||||
else if (duplicate is TagHelperAttributeParameterIntermediateNode duplicateParameterAttribute)
|
||||
else if (duplicate is TagHelperDirectiveAttributeParameterIntermediateNode duplicateParameterAttribute)
|
||||
{
|
||||
duplicateAttributeName = duplicateParameterAttribute.AttributeName;
|
||||
duplicateTagHelper = duplicateParameterAttribute.TagHelper;
|
||||
|
|
@ -222,7 +222,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
// If we still have duplicates at this point then they are genuine conflicts.
|
||||
var duplicates = node.Children
|
||||
.OfType<TagHelperPropertyIntermediateNode>()
|
||||
.OfType<TagHelperDirectiveAttributeIntermediateNode>()
|
||||
.GroupBy(p => p.AttributeName)
|
||||
.Where(g => g.Count() > 1);
|
||||
|
||||
|
|
@ -230,7 +230,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
node.Diagnostics.Add(ComponentDiagnosticFactory.CreateBindAttribute_Duplicates(
|
||||
node.Source,
|
||||
duplicate.Key,
|
||||
duplicate.First().OriginalAttributeName,
|
||||
duplicate.ToArray()));
|
||||
foreach (var property in duplicate)
|
||||
{
|
||||
|
|
@ -338,7 +338,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
Annotations =
|
||||
{
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
|
||||
},
|
||||
AttributeName = valueAttributeName,
|
||||
Source = node.Source,
|
||||
|
|
@ -362,7 +362,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
Annotations =
|
||||
{
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
|
||||
},
|
||||
AttributeName = changeAttributeName,
|
||||
Source = node.Source,
|
||||
|
|
@ -385,7 +385,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
Annotations =
|
||||
{
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
|
||||
},
|
||||
AttributeName = valueAttributeName,
|
||||
BoundAttribute = valueAttribute, // Might be null if it doesn't match a component attribute
|
||||
|
|
@ -405,7 +405,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
Annotations =
|
||||
{
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
|
||||
},
|
||||
AttributeName = changeAttributeName,
|
||||
BoundAttribute = changeAttribute, // Might be null if it doesn't match a component attribute
|
||||
|
|
@ -430,7 +430,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
Annotations =
|
||||
{
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
|
||||
},
|
||||
AttributeName = expressionAttributeName,
|
||||
BoundAttribute = expressionAttribute,
|
||||
|
|
@ -463,7 +463,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
valueAttributeName = null;
|
||||
changeAttributeName = null;
|
||||
|
||||
if (!attributeName.StartsWith("@bind"))
|
||||
if (!attributeName.StartsWith("bind"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -473,7 +473,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
changeAttributeName = GetAttributeContent(bindEntry.BindEventNode)?.Content?.Trim('"');
|
||||
}
|
||||
|
||||
if (attributeName == "@bind")
|
||||
if (attributeName == "bind")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -591,7 +591,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
private bool TryGetFormatNode(
|
||||
IntermediateNode node,
|
||||
TagHelperPropertyIntermediateNode attributeNode,
|
||||
TagHelperDirectiveAttributeIntermediateNode attributeNode,
|
||||
string valueAttributeName,
|
||||
out TagHelperPropertyIntermediateNode formatNode)
|
||||
{
|
||||
|
|
@ -807,16 +807,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
public BindEntry(IntermediateNodeReference bindNodeReference)
|
||||
{
|
||||
BindNodeReference = bindNodeReference;
|
||||
BindNode = (TagHelperPropertyIntermediateNode)bindNodeReference.Node;
|
||||
BindNode = (TagHelperDirectiveAttributeIntermediateNode)bindNodeReference.Node;
|
||||
}
|
||||
|
||||
public IntermediateNodeReference BindNodeReference { get; }
|
||||
|
||||
public TagHelperPropertyIntermediateNode BindNode { get; }
|
||||
public TagHelperDirectiveAttributeIntermediateNode BindNode { get; }
|
||||
|
||||
public TagHelperAttributeParameterIntermediateNode BindEventNode { get; set; }
|
||||
public TagHelperDirectiveAttributeParameterIntermediateNode BindEventNode { get; set; }
|
||||
|
||||
public TagHelperAttributeParameterIntermediateNode BindFormatNode { get; set; }
|
||||
public TagHelperDirectiveAttributeParameterIntermediateNode BindFormatNode { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
if (node.Children[i] is TagHelperPropertyIntermediateNode propertyNode)
|
||||
{
|
||||
if (TrySimplifyContent(propertyNode) && node.TagHelpers.Any(t => t.IsComponentTagHelper()))
|
||||
if (!TrySimplifyContent(propertyNode) && node.TagHelpers.Any(t => t.IsComponentTagHelper()))
|
||||
{
|
||||
node.Diagnostics.Add(ComponentDiagnosticFactory.Create_UnsupportedComplexContent(
|
||||
propertyNode,
|
||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
else if (node.Children[i] is TagHelperHtmlAttributeIntermediateNode htmlNode)
|
||||
{
|
||||
if (TrySimplifyContent(htmlNode) && node.TagHelpers.Any(t => t.IsComponentTagHelper()))
|
||||
if (!TrySimplifyContent(htmlNode) && node.TagHelpers.Any(t => t.IsComponentTagHelper()))
|
||||
{
|
||||
node.Diagnostics.Add(ComponentDiagnosticFactory.Create_UnsupportedComplexContent(
|
||||
htmlNode,
|
||||
|
|
@ -56,6 +56,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
continue;
|
||||
}
|
||||
}
|
||||
else if (node.Children[i] is TagHelperDirectiveAttributeIntermediateNode directiveAttributeNode)
|
||||
{
|
||||
if (!TrySimplifyContent(directiveAttributeNode))
|
||||
{
|
||||
node.Diagnostics.Add(ComponentDiagnosticFactory.Create_UnsupportedComplexContent(
|
||||
directiveAttributeNode,
|
||||
directiveAttributeNode.OriginalAttributeName));
|
||||
node.Children.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +77,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
htmlNode.Children.Count > 1)
|
||||
{
|
||||
// This case can be hit for a 'string' attribute
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
else if (node.Children.Count == 1 &&
|
||||
node.Children[0] is CSharpExpressionIntermediateNode cSharpNode &&
|
||||
|
|
@ -87,25 +98,25 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
cSharpNode.Children.RemoveAt(0);
|
||||
|
||||
// We were able to simplify it, all good.
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
else if (node.Children.Count == 1 &&
|
||||
node.Children[0] is CSharpCodeIntermediateNode cSharpCodeNode)
|
||||
node.Children[0] is CSharpCodeIntermediateNode)
|
||||
{
|
||||
// This is the case when an attribute contains a code block @{ ... }
|
||||
// We don't support this.
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
else if (node.Children.Count > 1)
|
||||
{
|
||||
// This is the common case for 'mixed' content
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor UnsupportedComplexContent = new RazorDiagnosticDescriptor(
|
||||
$"{DiagnosticPrefix}9986",
|
||||
() => "Component attributes do not support complex content (mixed C# and markup). Attribute: '{0}', text '{1}'",
|
||||
() => "Component attributes do not support complex content (mixed C# and markup). Attribute: '{0}', text: '{1}'",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
public static RazorDiagnostic Create_UnsupportedComplexContent(IntermediateNode node, string attributeName)
|
||||
|
|
@ -136,7 +136,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
() => "The attribute '{0}' was matched by multiple bind attributes. Duplicates:{1}",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
public static RazorDiagnostic CreateBindAttribute_Duplicates(SourceSpan? source, string attribute, TagHelperPropertyIntermediateNode[] attributes)
|
||||
public static RazorDiagnostic CreateBindAttribute_Duplicates(SourceSpan? source, string attribute, TagHelperDirectiveAttributeIntermediateNode[] attributes)
|
||||
{
|
||||
var diagnostic = RazorDiagnostic.Create(
|
||||
BindAttribute_Duplicates,
|
||||
|
|
@ -152,7 +152,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
() => "The attribute '{0}' was matched by multiple event handlers attributes. Duplicates:{1}",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
public static RazorDiagnostic CreateEventHandler_Duplicates(SourceSpan? source, string attribute, TagHelperPropertyIntermediateNode[] attributes)
|
||||
public static RazorDiagnostic CreateEventHandler_Duplicates(SourceSpan? source, string attribute, TagHelperDirectiveAttributeIntermediateNode[] attributes)
|
||||
{
|
||||
var diagnostic = RazorDiagnostic.Create(
|
||||
EventHandler_Duplicates,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
// For each event handler *usage* we need to rewrite the tag helper node to map to basic constructs.
|
||||
// Each usage will be represented by a tag helper property that is a descendant of either
|
||||
// a component or element.
|
||||
var references = documentNode.FindDescendantReferences<TagHelperPropertyIntermediateNode>();
|
||||
var references = documentNode.FindDescendantReferences<TagHelperDirectiveAttributeIntermediateNode>();
|
||||
|
||||
var parents = new HashSet<IntermediateNode>();
|
||||
for (var i = 0; i < references.Count; i++)
|
||||
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
for (var i = 0; i < references.Count; i++)
|
||||
{
|
||||
var reference = references[i];
|
||||
var node = (TagHelperPropertyIntermediateNode)reference.Node;
|
||||
var node = (TagHelperDirectiveAttributeIntermediateNode)reference.Node;
|
||||
|
||||
if (!reference.Parent.Children.Contains(node))
|
||||
{
|
||||
|
|
@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
continue;
|
||||
}
|
||||
|
||||
if (node.TagHelper.IsEventHandlerTagHelper() && node.IsDirectiveAttribute)
|
||||
if (node.TagHelper.IsEventHandlerTagHelper())
|
||||
{
|
||||
reference.Replace(RewriteUsage(reference.Parent, node));
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
// If we still have duplicates at this point then they are genuine conflicts.
|
||||
var duplicates = parent.Children
|
||||
.OfType<TagHelperPropertyIntermediateNode>()
|
||||
.OfType<TagHelperDirectiveAttributeIntermediateNode>()
|
||||
.Where(p => p.TagHelper?.IsEventHandlerTagHelper() ?? false)
|
||||
.GroupBy(p => p.AttributeName)
|
||||
.Where(g => g.Count() > 1);
|
||||
|
|
@ -111,7 +111,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
}
|
||||
|
||||
private IntermediateNode RewriteUsage(IntermediateNode parent, TagHelperPropertyIntermediateNode node)
|
||||
private IntermediateNode RewriteUsage(IntermediateNode parent, TagHelperDirectiveAttributeIntermediateNode node)
|
||||
{
|
||||
var original = GetAttributeContent(node);
|
||||
if (original.Count == 0)
|
||||
|
|
@ -148,19 +148,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
|
||||
var attributeName = node.AttributeName;
|
||||
if (node.IsDirectiveAttribute && attributeName.StartsWith("@"))
|
||||
{
|
||||
// Directive attributes start with a "@" but we don't want that to be included in the output attribute name.
|
||||
// E.g, <input @onclick="..." /> should result in the creation of 'onclick' attribute.
|
||||
attributeName = attributeName.Substring(1);
|
||||
}
|
||||
|
||||
if (parent is MarkupElementIntermediateNode)
|
||||
{
|
||||
var result = new HtmlAttributeIntermediateNode()
|
||||
{
|
||||
Annotations =
|
||||
{
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
|
||||
},
|
||||
AttributeName = attributeName,
|
||||
Source = node.Source,
|
||||
|
|
@ -188,7 +183,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
Annotations =
|
||||
{
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
|
||||
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -203,7 +198,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
}
|
||||
|
||||
private static IReadOnlyList<IntermediateToken> GetAttributeContent(TagHelperPropertyIntermediateNode node)
|
||||
private static IReadOnlyList<IntermediateToken> GetAttributeContent(TagHelperDirectiveAttributeIntermediateNode node)
|
||||
{
|
||||
var template = node.FindDescendantNodes<TemplateIntermediateNode>().FirstOrDefault();
|
||||
if (template != null)
|
||||
|
|
|
|||
|
|
@ -25,20 +25,20 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
return;
|
||||
}
|
||||
|
||||
var references = documentNode.FindDescendantReferences<TagHelperPropertyIntermediateNode>();
|
||||
var references = documentNode.FindDescendantReferences<TagHelperDirectiveAttributeIntermediateNode>();
|
||||
for (var i = 0; i < references.Count; i++)
|
||||
{
|
||||
var reference = references[i];
|
||||
var node = (TagHelperPropertyIntermediateNode)reference.Node;
|
||||
var node = (TagHelperDirectiveAttributeIntermediateNode)reference.Node;
|
||||
|
||||
if (node.TagHelper.IsKeyTagHelper() && node.IsDirectiveAttribute)
|
||||
if (node.TagHelper.IsKeyTagHelper())
|
||||
{
|
||||
reference.Replace(RewriteUsage(reference.Parent, node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IntermediateNode RewriteUsage(IntermediateNode parent, TagHelperPropertyIntermediateNode node)
|
||||
private IntermediateNode RewriteUsage(IntermediateNode parent, TagHelperDirectiveAttributeIntermediateNode node)
|
||||
{
|
||||
// If we can't get a nonempty attribute value, do nothing because there will
|
||||
// already be a diagnostic for empty values
|
||||
|
|
@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
return new SetKeyIntermediateNode(keyValueToken);
|
||||
}
|
||||
|
||||
private IntermediateToken DetermineKeyValueToken(TagHelperPropertyIntermediateNode attributeNode)
|
||||
private IntermediateToken DetermineKeyValueToken(TagHelperDirectiveAttributeIntermediateNode attributeNode)
|
||||
{
|
||||
IntermediateToken foundToken = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
continue;
|
||||
}
|
||||
|
||||
// This is an unrecognized attribute, this is possible if you try to do something like put 'ref' on a child content.
|
||||
// This is an unrecognized tag helper bound attribute. This will practically never happen unless the child content descriptor was misconfigured.
|
||||
childContent.Diagnostics.Add(ComponentDiagnosticFactory.Create_ChildContentHasInvalidAttribute(property.Source, property.AttributeName, attribute.Name));
|
||||
}
|
||||
else if (child is TagHelperHtmlAttributeIntermediateNode a)
|
||||
|
|
@ -269,6 +269,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
// This is an HTML attribute on a child content.
|
||||
childContent.Diagnostics.Add(ComponentDiagnosticFactory.Create_ChildContentHasInvalidAttribute(a.Source, a.AttributeName, attribute.Name));
|
||||
}
|
||||
else if (child is TagHelperDirectiveAttributeIntermediateNode directiveAttribute)
|
||||
{
|
||||
// We don't support directive attributes inside child content, this is possible if you try to do something like put '@ref' on a child content.
|
||||
childContent.Diagnostics.Add(ComponentDiagnosticFactory.Create_ChildContentHasInvalidAttribute(directiveAttribute.Source, directiveAttribute.OriginalAttributeName, attribute.Name));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is some other kind of node (likely an implicit child content)
|
||||
|
|
@ -385,6 +390,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
_children.Add(new ComponentAttributeIntermediateNode(node));
|
||||
}
|
||||
|
||||
public override void VisitTagHelperDirectiveAttribute(TagHelperDirectiveAttributeIntermediateNode node)
|
||||
{
|
||||
// We don't want to do anything special with directive attributes here.
|
||||
// Let their corresponding lowering pass take care of processing them.
|
||||
_children.Add(node);
|
||||
}
|
||||
|
||||
public override void VisitDefault(IntermediateNode node)
|
||||
{
|
||||
_children.Add(node);
|
||||
|
|
@ -490,6 +502,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
_children.Add(node.TagHelper.IsComponentTagHelper() ? (IntermediateNode)new ComponentAttributeIntermediateNode(node) : node);
|
||||
}
|
||||
|
||||
public override void VisitTagHelperDirectiveAttribute(TagHelperDirectiveAttributeIntermediateNode node)
|
||||
{
|
||||
// We don't want to do anything special with directive attributes here.
|
||||
// Let their corresponding lowering pass take care of processing them.
|
||||
_children.Add(node);
|
||||
}
|
||||
|
||||
public override void VisitDefault(IntermediateNode node)
|
||||
{
|
||||
_children.Add(node);
|
||||
|
|
|
|||
|
|
@ -25,20 +25,20 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
return;
|
||||
}
|
||||
|
||||
var references = documentNode.FindDescendantReferences<TagHelperPropertyIntermediateNode>();
|
||||
var references = documentNode.FindDescendantReferences<TagHelperDirectiveAttributeIntermediateNode>();
|
||||
for (var i = 0; i < references.Count; i++)
|
||||
{
|
||||
var reference = references[i];
|
||||
var node = (TagHelperPropertyIntermediateNode)reference.Node;
|
||||
var node = (TagHelperDirectiveAttributeIntermediateNode)reference.Node;
|
||||
|
||||
if (node.TagHelper.IsRefTagHelper() && node.IsDirectiveAttribute)
|
||||
if (node.TagHelper.IsRefTagHelper())
|
||||
{
|
||||
reference.Replace(RewriteUsage(@class, reference.Parent, node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IntermediateNode RewriteUsage(ClassDeclarationIntermediateNode classNode, IntermediateNode parent, TagHelperPropertyIntermediateNode node)
|
||||
private IntermediateNode RewriteUsage(ClassDeclarationIntermediateNode classNode, IntermediateNode parent, TagHelperDirectiveAttributeIntermediateNode node)
|
||||
{
|
||||
// If we can't get a nonempty attribute name, do nothing because there will
|
||||
// already be a diagnostic for empty values
|
||||
|
|
@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
}
|
||||
|
||||
private IntermediateToken DetermineIdentifierToken(TagHelperPropertyIntermediateNode attributeNode)
|
||||
private IntermediateToken DetermineIdentifierToken(TagHelperDirectiveAttributeIntermediateNode attributeNode)
|
||||
{
|
||||
IntermediateToken foundToken = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
ancestor is ComponentAttributeIntermediateNode ||
|
||||
|
||||
// Inside malformed ref attribute
|
||||
ancestor is TagHelperPropertyIntermediateNode)
|
||||
ancestor is TagHelperPropertyIntermediateNode ||
|
||||
|
||||
// Inside a directive attribute
|
||||
ancestor is TagHelperDirectiveAttributeIntermediateNode)
|
||||
{
|
||||
Candidates.Add(new IntermediateNodeReference(Parent, node));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1752,14 +1752,19 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return;
|
||||
}
|
||||
|
||||
// Directive attributes should start with '@' unless the descriptors are misconfigured.
|
||||
// In that case, we would have already logged an error.
|
||||
var actualAttributeName = attributeName.StartsWith("@") ? attributeName.Substring(1) : attributeName;
|
||||
|
||||
IntermediateNode attributeNode;
|
||||
if (parameterMatch &&
|
||||
TagHelperMatchingConventions.TryGetBoundAttributeParameter(attributeName, out var attributeNameWithoutParameter, out _))
|
||||
TagHelperMatchingConventions.TryGetBoundAttributeParameter(actualAttributeName, out var attributeNameWithoutParameter, out _))
|
||||
{
|
||||
attributeNode = new TagHelperAttributeParameterIntermediateNode()
|
||||
attributeNode = new TagHelperDirectiveAttributeParameterIntermediateNode()
|
||||
{
|
||||
AttributeName = attributeName,
|
||||
AttributeName = actualAttributeName,
|
||||
AttributeNameWithoutParameter = attributeNameWithoutParameter,
|
||||
OriginalAttributeName = attributeName,
|
||||
BoundAttributeParameter = associatedAttributeParameterDescriptor,
|
||||
BoundAttribute = associatedAttributeDescriptor,
|
||||
TagHelper = associatedDescriptor,
|
||||
|
|
@ -1770,9 +1775,10 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
}
|
||||
else
|
||||
{
|
||||
attributeNode = new TagHelperPropertyIntermediateNode()
|
||||
attributeNode = new TagHelperDirectiveAttributeIntermediateNode()
|
||||
{
|
||||
AttributeName = attributeName,
|
||||
AttributeName = actualAttributeName,
|
||||
OriginalAttributeName = attributeName,
|
||||
BoundAttribute = associatedAttributeDescriptor,
|
||||
TagHelper = associatedDescriptor,
|
||||
AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure,
|
||||
|
|
@ -1870,14 +1876,19 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
out var parameterMatch,
|
||||
out var associatedAttributeParameterDescriptor))
|
||||
{
|
||||
// Directive attributes should start with '@' unless the descriptors are misconfigured.
|
||||
// In that case, we would have already logged an error.
|
||||
var actualAttributeName = attributeName.StartsWith("@") ? attributeName.Substring(1) : attributeName;
|
||||
|
||||
IntermediateNode attributeNode;
|
||||
if (parameterMatch &&
|
||||
TagHelperMatchingConventions.TryGetBoundAttributeParameter(attributeName, out var attributeNameWithoutParameter, out _))
|
||||
TagHelperMatchingConventions.TryGetBoundAttributeParameter(actualAttributeName, out var attributeNameWithoutParameter, out _))
|
||||
{
|
||||
attributeNode = new TagHelperAttributeParameterIntermediateNode()
|
||||
attributeNode = new TagHelperDirectiveAttributeParameterIntermediateNode()
|
||||
{
|
||||
AttributeName = attributeName,
|
||||
AttributeName = actualAttributeName,
|
||||
AttributeNameWithoutParameter = attributeNameWithoutParameter,
|
||||
OriginalAttributeName = attributeName,
|
||||
BoundAttributeParameter = associatedAttributeParameterDescriptor,
|
||||
BoundAttribute = associatedAttributeDescriptor,
|
||||
TagHelper = associatedDescriptor,
|
||||
|
|
@ -1888,9 +1899,10 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
}
|
||||
else
|
||||
{
|
||||
attributeNode = new TagHelperPropertyIntermediateNode()
|
||||
attributeNode = new TagHelperDirectiveAttributeIntermediateNode()
|
||||
{
|
||||
AttributeName = attributeName,
|
||||
AttributeName = actualAttributeName,
|
||||
OriginalAttributeName = attributeName,
|
||||
BoundAttribute = associatedAttributeDescriptor,
|
||||
TagHelper = associatedDescriptor,
|
||||
AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure,
|
||||
|
|
|
|||
|
|
@ -42,12 +42,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
}
|
||||
|
||||
var attributeName = propertyNode.AttributeName;
|
||||
if (propertyNode.IsDirectiveAttribute && attributeName.StartsWith("@"))
|
||||
{
|
||||
// Directive attributes start with a "@" but we don't want that to be included in the output attribute name.
|
||||
// E.g, <input @onclick="..." /> should result in the creation of 'onclick' attribute.
|
||||
attributeName = attributeName.Substring(1);
|
||||
}
|
||||
|
||||
AttributeName = attributeName;
|
||||
AttributeStructure = propertyNode.AttributeStructure;
|
||||
|
|
@ -68,6 +62,32 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
}
|
||||
}
|
||||
|
||||
public ComponentAttributeIntermediateNode(TagHelperDirectiveAttributeIntermediateNode directiveAttributeNode)
|
||||
{
|
||||
if (directiveAttributeNode == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(directiveAttributeNode));
|
||||
}
|
||||
|
||||
AttributeName = directiveAttributeNode.AttributeName;
|
||||
AttributeStructure = directiveAttributeNode.AttributeStructure;
|
||||
BoundAttribute = directiveAttributeNode.BoundAttribute;
|
||||
PropertyName = directiveAttributeNode.BoundAttribute.GetPropertyName();
|
||||
Source = directiveAttributeNode.Source;
|
||||
TagHelper = directiveAttributeNode.TagHelper;
|
||||
TypeName = directiveAttributeNode.BoundAttribute.IsWeaklyTyped() ? null : directiveAttributeNode.BoundAttribute.TypeName;
|
||||
|
||||
for (var i = 0; i < directiveAttributeNode.Children.Count; i++)
|
||||
{
|
||||
Children.Add(directiveAttributeNode.Children[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < directiveAttributeNode.Diagnostics.Count; i++)
|
||||
{
|
||||
Diagnostics.Add(directiveAttributeNode.Diagnostics[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection();
|
||||
|
||||
public string AttributeName { get; set; }
|
||||
|
|
|
|||
|
|
@ -124,12 +124,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
VisitDefault(node);
|
||||
}
|
||||
|
||||
public virtual void VisitTagHelperDirectiveAttribute(TagHelperDirectiveAttributeIntermediateNode node)
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
|
||||
public virtual void VisitTagHelperHtmlAttribute(TagHelperHtmlAttributeIntermediateNode node)
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
|
||||
public virtual void VisitTagHelperAttributeParameter(TagHelperAttributeParameterIntermediateNode node)
|
||||
public virtual void VisitTagHelperDirectiveAttributeParameter(TagHelperDirectiveAttributeParameterIntermediateNode node)
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||
{
|
||||
public sealed class TagHelperDirectiveAttributeIntermediateNode : IntermediateNode
|
||||
{
|
||||
public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection();
|
||||
|
||||
public string AttributeName { get; set; }
|
||||
|
||||
public string OriginalAttributeName { get; set; }
|
||||
|
||||
public AttributeStructure AttributeStructure { get; set; }
|
||||
|
||||
public BoundAttributeDescriptor BoundAttribute { get; set; }
|
||||
|
||||
public TagHelperDescriptor TagHelper { get; set; }
|
||||
|
||||
public bool IsIndexerNameMatch { get; set; }
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
{
|
||||
if (visitor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(visitor));
|
||||
}
|
||||
|
||||
visitor.VisitTagHelperDirectiveAttribute(this);
|
||||
}
|
||||
|
||||
public override void FormatNode(IntermediateNodeFormatter formatter)
|
||||
{
|
||||
formatter.WriteContent(AttributeName);
|
||||
|
||||
formatter.WriteProperty(nameof(AttributeName), AttributeName);
|
||||
formatter.WriteProperty(nameof(OriginalAttributeName), OriginalAttributeName);
|
||||
formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString());
|
||||
formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute?.DisplayName);
|
||||
formatter.WriteProperty(nameof(IsIndexerNameMatch), IsIndexerNameMatch.ToString());
|
||||
formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ using System;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||
{
|
||||
public sealed class TagHelperAttributeParameterIntermediateNode : IntermediateNode
|
||||
public sealed class TagHelperDirectiveAttributeParameterIntermediateNode : IntermediateNode
|
||||
{
|
||||
public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection();
|
||||
|
||||
|
|
@ -13,6 +13,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
|
||||
public string AttributeNameWithoutParameter { get; set; }
|
||||
|
||||
public string OriginalAttributeName { get; set; }
|
||||
|
||||
public AttributeStructure AttributeStructure { get; set; }
|
||||
|
||||
public BoundAttributeParameterDescriptor BoundAttributeParameter { get; set; }
|
||||
|
|
@ -23,8 +25,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
|
||||
public bool IsIndexerNameMatch { get; set; }
|
||||
|
||||
public bool IsDirectiveAttribute => BoundAttribute?.IsDirectiveAttribute() ?? false;
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
{
|
||||
if (visitor == null)
|
||||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
throw new ArgumentNullException(nameof(visitor));
|
||||
}
|
||||
|
||||
visitor.VisitTagHelperAttributeParameter(this);
|
||||
visitor.VisitTagHelperDirectiveAttributeParameter(this);
|
||||
}
|
||||
|
||||
public override void FormatNode(IntermediateNodeFormatter formatter)
|
||||
|
|
@ -40,10 +40,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
formatter.WriteContent(AttributeName);
|
||||
|
||||
formatter.WriteProperty(nameof(AttributeName), AttributeName);
|
||||
formatter.WriteProperty(nameof(OriginalAttributeName), OriginalAttributeName);
|
||||
formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString());
|
||||
formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute?.DisplayName);
|
||||
formatter.WriteProperty(nameof(BoundAttributeParameter), BoundAttributeParameter?.DisplayName);
|
||||
formatter.WriteProperty(nameof(IsDirectiveAttribute), IsDirectiveAttribute.ToString());
|
||||
formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,8 +19,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
|
||||
public bool IsIndexerNameMatch { get; set; }
|
||||
|
||||
public bool IsDirectiveAttribute => BoundAttribute?.IsDirectiveAttribute() ?? false;
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
{
|
||||
if (visitor == null)
|
||||
|
|
@ -38,7 +36,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
formatter.WriteProperty(nameof(AttributeName), AttributeName);
|
||||
formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString());
|
||||
formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute?.DisplayName);
|
||||
formatter.WriteProperty(nameof(IsDirectiveAttribute), IsDirectiveAttribute.ToString());
|
||||
formatter.WriteProperty(nameof(IsIndexerNameMatch), IsIndexerNameMatch.ToString());
|
||||
formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,5 +245,26 @@ Some Content
|
|||
"Invalid parameter name. The parameter name attribute 'Context' on component 'RenderChildContentString' can only include literal text.",
|
||||
diagnostic.GetMessage());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChildContent_ExplicitChildContent_ContainsDirectiveAttribute_ProducesDiagnostic()
|
||||
{
|
||||
// Arrange
|
||||
AdditionalSyntaxTrees.Add(RenderChildContentStringComponent);
|
||||
|
||||
// Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<RenderChildContentString>
|
||||
<ChildContent Context=""items"" @key=""Hello"">
|
||||
</ChildContent>
|
||||
</RenderChildContentString>");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Same(ComponentDiagnosticFactory.ChildContentHasInvalidAttribute.Id, diagnostic.Id);
|
||||
Assert.Equal(
|
||||
"Unrecognized attribute '@key' on child content element 'ChildContent'.",
|
||||
diagnostic.GetMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,5 +107,23 @@ namespace Test
|
|||
Assert.Equal(0, item.Span.CharacterIndex);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectiveAttribute_ComplexContent_ReportsError()
|
||||
{
|
||||
// Arrange & Act
|
||||
var generated = CompileToCSharp(@"
|
||||
<input type=""text"" @key=""Foo @Text"" />
|
||||
@functions {
|
||||
public string Text { get; set; } = ""text"";
|
||||
}");
|
||||
|
||||
// Assert
|
||||
var diagnostic = Assert.Single(generated.Diagnostics);
|
||||
Assert.Equal("RZ9986", diagnostic.Id);
|
||||
Assert.Equal(
|
||||
"Component attributes do not support complex content (mixed C# and markup). Attribute: '@key', text: 'Foo @Text'",
|
||||
diagnostic.GetMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,12 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
|||
WriteContentNode(node, node.AttributeName, string.Format("HtmlAttributeValueStyle.{0}", node.AttributeStructure));
|
||||
}
|
||||
|
||||
public override void VisitTagHelperAttributeParameter(TagHelperAttributeParameterIntermediateNode node)
|
||||
public override void VisitTagHelperDirectiveAttribute(TagHelperDirectiveAttributeIntermediateNode node)
|
||||
{
|
||||
WriteContentNode(node, node.AttributeName, node.BoundAttribute.DisplayName, string.Format("HtmlAttributeValueStyle.{0}", node.AttributeStructure));
|
||||
}
|
||||
|
||||
public override void VisitTagHelperDirectiveAttributeParameter(TagHelperDirectiveAttributeParameterIntermediateNode node)
|
||||
{
|
||||
WriteContentNode(node, node.AttributeName, string.Format("HtmlAttributeValueStyle.{0}", node.AttributeStructure));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue