Skip to content

Commit f8d7f4c

Browse files
authored
Use an explicit intermediate node for directive attributes (#638)
* Use an explicit intermediate node for directive attributes * More cleanup
1 parent 4aadcc6 commit f8d7f4c

18 files changed

+249
-97
lines changed

src/Razor/src/Microsoft.AspNetCore.Razor.Language/BoundAttributeDescriptorExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ internal static bool ExpectsBooleanValue(this BoundAttributeDescriptor attribute
5151
return isIndexerNameMatch && attribute.IsIndexerBooleanProperty;
5252
}
5353

54-
internal static bool IsDirectiveAttribute(this BoundAttributeDescriptor attribute)
54+
public static bool IsDirectiveAttribute(this BoundAttributeDescriptor attribute)
5555
{
5656
if (attribute == null)
5757
{

src/Razor/src/Microsoft.AspNetCore.Razor.Language/Components/ComponentBindLoweringPass.cs

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte
2929
}
3030

3131
// For each @bind *usage* we need to rewrite the tag helper node to map to basic constructs.
32-
var references = documentNode.FindDescendantReferences<TagHelperPropertyIntermediateNode>();
33-
var parameterReferences = documentNode.FindDescendantReferences<TagHelperAttributeParameterIntermediateNode>();
32+
var references = documentNode.FindDescendantReferences<TagHelperDirectiveAttributeIntermediateNode>();
33+
var parameterReferences = documentNode.FindDescendantReferences<TagHelperDirectiveAttributeParameterIntermediateNode>();
3434

3535
var parents = new HashSet<IntermediateNode>();
3636
for (var i = 0; i < references.Count; i++)
@@ -56,15 +56,15 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte
5656
{
5757
var reference = references[i];
5858
var parent = reference.Parent;
59-
var node = (TagHelperPropertyIntermediateNode)reference.Node;
59+
var node = (TagHelperDirectiveAttributeIntermediateNode)reference.Node;
6060

6161
if (!parent.Children.Contains(node))
6262
{
6363
// This node was removed as a duplicate, skip it.
6464
continue;
6565
}
6666

67-
if (node.TagHelper.IsBindTagHelper() && node.IsDirectiveAttribute)
67+
if (node.TagHelper.IsBindTagHelper())
6868
{
6969
bindEntries[(parent, node.AttributeName)] = new BindEntry(reference);
7070
}
@@ -75,15 +75,15 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte
7575
{
7676
var parameterReference = parameterReferences[i];
7777
var parent = parameterReference.Parent;
78-
var node = (TagHelperAttributeParameterIntermediateNode)parameterReference.Node;
78+
var node = (TagHelperDirectiveAttributeParameterIntermediateNode)parameterReference.Node;
7979

8080
if (!parent.Children.Contains(node))
8181
{
8282
// This node was removed as a duplicate, skip it.
8383
continue;
8484
}
8585

86-
if (node.TagHelper.IsBindTagHelper() && node.IsDirectiveAttribute)
86+
if (node.TagHelper.IsBindTagHelper())
8787
{
8888
// Check if this tag contains a corresponding non-parameterized bind node.
8989
if (!bindEntries.TryGetValue((parent, node.AttributeNameWithoutParameter), out var entry))
@@ -140,12 +140,12 @@ private void ProcessDuplicates(IntermediateNode node)
140140
TagHelperDescriptor tagHelper = null;
141141
string attributeName = null;
142142
var attribute = node.Children[i];
143-
if (attribute is TagHelperPropertyIntermediateNode propertyAttribute)
143+
if (attribute is TagHelperDirectiveAttributeIntermediateNode directiveAttribute)
144144
{
145-
attributeName = propertyAttribute.AttributeName;
146-
tagHelper = propertyAttribute.TagHelper;
145+
attributeName = directiveAttribute.AttributeName;
146+
tagHelper = directiveAttribute.TagHelper;
147147
}
148-
else if (attribute is TagHelperAttributeParameterIntermediateNode parameterAttribute)
148+
else if (attribute is TagHelperDirectiveAttributeParameterIntermediateNode parameterAttribute)
149149
{
150150
attributeName = parameterAttribute.AttributeName;
151151
tagHelper = parameterAttribute.TagHelper;
@@ -159,12 +159,12 @@ private void ProcessDuplicates(IntermediateNode node)
159159
TagHelperDescriptor duplicateTagHelper = null;
160160
string duplicateAttributeName = null;
161161
var duplicate = node.Children[j];
162-
if (duplicate is TagHelperPropertyIntermediateNode duplicatePropertyAttribute)
162+
if (duplicate is TagHelperDirectiveAttributeIntermediateNode duplicateDirectiveAttribute)
163163
{
164-
duplicateAttributeName = duplicatePropertyAttribute.AttributeName;
165-
duplicateTagHelper = duplicatePropertyAttribute.TagHelper;
164+
duplicateAttributeName = duplicateDirectiveAttribute.AttributeName;
165+
duplicateTagHelper = duplicateDirectiveAttribute.TagHelper;
166166
}
167-
else if (duplicate is TagHelperAttributeParameterIntermediateNode duplicateParameterAttribute)
167+
else if (duplicate is TagHelperDirectiveAttributeParameterIntermediateNode duplicateParameterAttribute)
168168
{
169169
duplicateAttributeName = duplicateParameterAttribute.AttributeName;
170170
duplicateTagHelper = duplicateParameterAttribute.TagHelper;
@@ -195,12 +195,12 @@ private void ProcessDuplicates(IntermediateNode node)
195195
TagHelperDescriptor duplicateTagHelper = null;
196196
string duplicateAttributeName = null;
197197
var duplicate = node.Children[j];
198-
if (duplicate is TagHelperPropertyIntermediateNode duplicatePropertyAttribute)
198+
if (duplicate is TagHelperDirectiveAttributeIntermediateNode duplicateDirectiveAttribute)
199199
{
200-
duplicateAttributeName = duplicatePropertyAttribute.AttributeName;
201-
duplicateTagHelper = duplicatePropertyAttribute.TagHelper;
200+
duplicateAttributeName = duplicateDirectiveAttribute.AttributeName;
201+
duplicateTagHelper = duplicateDirectiveAttribute.TagHelper;
202202
}
203-
else if (duplicate is TagHelperAttributeParameterIntermediateNode duplicateParameterAttribute)
203+
else if (duplicate is TagHelperDirectiveAttributeParameterIntermediateNode duplicateParameterAttribute)
204204
{
205205
duplicateAttributeName = duplicateParameterAttribute.AttributeName;
206206
duplicateTagHelper = duplicateParameterAttribute.TagHelper;
@@ -222,15 +222,15 @@ private void ProcessDuplicates(IntermediateNode node)
222222

223223
// If we still have duplicates at this point then they are genuine conflicts.
224224
var duplicates = node.Children
225-
.OfType<TagHelperPropertyIntermediateNode>()
225+
.OfType<TagHelperDirectiveAttributeIntermediateNode>()
226226
.GroupBy(p => p.AttributeName)
227227
.Where(g => g.Count() > 1);
228228

229229
foreach (var duplicate in duplicates)
230230
{
231231
node.Diagnostics.Add(ComponentDiagnosticFactory.CreateBindAttribute_Duplicates(
232232
node.Source,
233-
duplicate.Key,
233+
duplicate.First().OriginalAttributeName,
234234
duplicate.ToArray()));
235235
foreach (var property in duplicate)
236236
{
@@ -338,7 +338,7 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE
338338
{
339339
Annotations =
340340
{
341-
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
341+
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
342342
},
343343
AttributeName = valueAttributeName,
344344
Source = node.Source,
@@ -362,7 +362,7 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE
362362
{
363363
Annotations =
364364
{
365-
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
365+
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
366366
},
367367
AttributeName = changeAttributeName,
368368
Source = node.Source,
@@ -385,7 +385,7 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE
385385
{
386386
Annotations =
387387
{
388-
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
388+
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
389389
},
390390
AttributeName = valueAttributeName,
391391
BoundAttribute = valueAttribute, // Might be null if it doesn't match a component attribute
@@ -405,7 +405,7 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE
405405
{
406406
Annotations =
407407
{
408-
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
408+
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
409409
},
410410
AttributeName = changeAttributeName,
411411
BoundAttribute = changeAttribute, // Might be null if it doesn't match a component attribute
@@ -430,7 +430,7 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE
430430
{
431431
Annotations =
432432
{
433-
[ComponentMetadata.Common.OriginalAttributeName] = node.AttributeName,
433+
[ComponentMetadata.Common.OriginalAttributeName] = node.OriginalAttributeName,
434434
},
435435
AttributeName = expressionAttributeName,
436436
BoundAttribute = expressionAttribute,
@@ -463,7 +463,7 @@ private bool TryParseBindAttribute(
463463
valueAttributeName = null;
464464
changeAttributeName = null;
465465

466-
if (!attributeName.StartsWith("@bind"))
466+
if (!attributeName.StartsWith("bind"))
467467
{
468468
return false;
469469
}
@@ -473,7 +473,7 @@ private bool TryParseBindAttribute(
473473
changeAttributeName = GetAttributeContent(bindEntry.BindEventNode)?.Content?.Trim('"');
474474
}
475475

476-
if (attributeName == "@bind")
476+
if (attributeName == "bind")
477477
{
478478
return true;
479479
}
@@ -591,7 +591,7 @@ private bool TryComputeAttributeNames(
591591

592592
private bool TryGetFormatNode(
593593
IntermediateNode node,
594-
TagHelperPropertyIntermediateNode attributeNode,
594+
TagHelperDirectiveAttributeIntermediateNode attributeNode,
595595
string valueAttributeName,
596596
out TagHelperPropertyIntermediateNode formatNode)
597597
{
@@ -807,16 +807,16 @@ private class BindEntry
807807
public BindEntry(IntermediateNodeReference bindNodeReference)
808808
{
809809
BindNodeReference = bindNodeReference;
810-
BindNode = (TagHelperPropertyIntermediateNode)bindNodeReference.Node;
810+
BindNode = (TagHelperDirectiveAttributeIntermediateNode)bindNodeReference.Node;
811811
}
812812

813813
public IntermediateNodeReference BindNodeReference { get; }
814814

815-
public TagHelperPropertyIntermediateNode BindNode { get; }
815+
public TagHelperDirectiveAttributeIntermediateNode BindNode { get; }
816816

817-
public TagHelperAttributeParameterIntermediateNode BindEventNode { get; set; }
817+
public TagHelperDirectiveAttributeParameterIntermediateNode BindEventNode { get; set; }
818818

819-
public TagHelperAttributeParameterIntermediateNode BindFormatNode { get; set; }
819+
public TagHelperDirectiveAttributeParameterIntermediateNode BindFormatNode { get; set; }
820820
}
821821
}
822822
}

src/Razor/src/Microsoft.AspNetCore.Razor.Language/Components/ComponentComplexAttributeContentPass.cs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private void ProcessAttributes(TagHelperIntermediateNode node)
3636
{
3737
if (node.Children[i] is TagHelperPropertyIntermediateNode propertyNode)
3838
{
39-
if (TrySimplifyContent(propertyNode) && node.TagHelpers.Any(t => t.IsComponentTagHelper()))
39+
if (!TrySimplifyContent(propertyNode) && node.TagHelpers.Any(t => t.IsComponentTagHelper()))
4040
{
4141
node.Diagnostics.Add(ComponentDiagnosticFactory.Create_UnsupportedComplexContent(
4242
propertyNode,
@@ -47,7 +47,7 @@ private void ProcessAttributes(TagHelperIntermediateNode node)
4747
}
4848
else if (node.Children[i] is TagHelperHtmlAttributeIntermediateNode htmlNode)
4949
{
50-
if (TrySimplifyContent(htmlNode) && node.TagHelpers.Any(t => t.IsComponentTagHelper()))
50+
if (!TrySimplifyContent(htmlNode) && node.TagHelpers.Any(t => t.IsComponentTagHelper()))
5151
{
5252
node.Diagnostics.Add(ComponentDiagnosticFactory.Create_UnsupportedComplexContent(
5353
htmlNode,
@@ -56,6 +56,17 @@ private void ProcessAttributes(TagHelperIntermediateNode node)
5656
continue;
5757
}
5858
}
59+
else if (node.Children[i] is TagHelperDirectiveAttributeIntermediateNode directiveAttributeNode)
60+
{
61+
if (!TrySimplifyContent(directiveAttributeNode))
62+
{
63+
node.Diagnostics.Add(ComponentDiagnosticFactory.Create_UnsupportedComplexContent(
64+
directiveAttributeNode,
65+
directiveAttributeNode.OriginalAttributeName));
66+
node.Children.RemoveAt(i);
67+
continue;
68+
}
69+
}
5970
}
6071
}
6172

@@ -66,7 +77,7 @@ node.Children[0] is HtmlAttributeIntermediateNode htmlNode &&
6677
htmlNode.Children.Count > 1)
6778
{
6879
// This case can be hit for a 'string' attribute
69-
return true;
80+
return false;
7081
}
7182
else if (node.Children.Count == 1 &&
7283
node.Children[0] is CSharpExpressionIntermediateNode cSharpNode &&
@@ -87,25 +98,25 @@ cSharpNode.Children[2] is IntermediateToken token2 &&
8798
cSharpNode.Children.RemoveAt(0);
8899

89100
// We were able to simplify it, all good.
90-
return false;
101+
return true;
91102
}
92103

93-
return true;
104+
return false;
94105
}
95106
else if (node.Children.Count == 1 &&
96-
node.Children[0] is CSharpCodeIntermediateNode cSharpCodeNode)
107+
node.Children[0] is CSharpCodeIntermediateNode)
97108
{
98109
// This is the case when an attribute contains a code block @{ ... }
99110
// We don't support this.
100-
return true;
111+
return false;
101112
}
102113
else if (node.Children.Count > 1)
103114
{
104115
// This is the common case for 'mixed' content
105-
return true;
116+
return false;
106117
}
107118

108-
return false;
119+
return true;
109120
}
110121
}
111122
}

src/Razor/src/Microsoft.AspNetCore.Razor.Language/Components/ComponentDiagnosticFactory.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public static RazorDiagnostic Create_MultipleComponents(SourceSpan? span, string
9595

9696
public static readonly RazorDiagnosticDescriptor UnsupportedComplexContent = new RazorDiagnosticDescriptor(
9797
$"{DiagnosticPrefix}9986",
98-
() => "Component attributes do not support complex content (mixed C# and markup). Attribute: '{0}', text '{1}'",
98+
() => "Component attributes do not support complex content (mixed C# and markup). Attribute: '{0}', text: '{1}'",
9999
RazorDiagnosticSeverity.Error);
100100

101101
public static RazorDiagnostic Create_UnsupportedComplexContent(IntermediateNode node, string attributeName)
@@ -136,7 +136,7 @@ public static RazorDiagnostic CreatePageDirective_MustSpecifyRoute(SourceSpan? s
136136
() => "The attribute '{0}' was matched by multiple bind attributes. Duplicates:{1}",
137137
RazorDiagnosticSeverity.Error);
138138

139-
public static RazorDiagnostic CreateBindAttribute_Duplicates(SourceSpan? source, string attribute, TagHelperPropertyIntermediateNode[] attributes)
139+
public static RazorDiagnostic CreateBindAttribute_Duplicates(SourceSpan? source, string attribute, TagHelperDirectiveAttributeIntermediateNode[] attributes)
140140
{
141141
var diagnostic = RazorDiagnostic.Create(
142142
BindAttribute_Duplicates,
@@ -152,7 +152,7 @@ public static RazorDiagnostic CreateBindAttribute_Duplicates(SourceSpan? source,
152152
() => "The attribute '{0}' was matched by multiple event handlers attributes. Duplicates:{1}",
153153
RazorDiagnosticSeverity.Error);
154154

155-
public static RazorDiagnostic CreateEventHandler_Duplicates(SourceSpan? source, string attribute, TagHelperPropertyIntermediateNode[] attributes)
155+
public static RazorDiagnostic CreateEventHandler_Duplicates(SourceSpan? source, string attribute, TagHelperDirectiveAttributeIntermediateNode[] attributes)
156156
{
157157
var diagnostic = RazorDiagnostic.Create(
158158
EventHandler_Duplicates,

0 commit comments

Comments
 (0)