Skip to content

Commit 4c1ff7c

Browse files
authored
Merge pull request #8512 from davidwengier/FixComponentPropertiesWithSuffixes
2 parents 8915fc5 + db7a665 commit 4c1ff7c

File tree

143 files changed

+1268
-57
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+1268
-57
lines changed

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

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55

66
using System;
77
using System.Collections.Generic;
8+
using System.Collections.Immutable;
89
using System.Globalization;
910
using System.Linq;
1011
using Microsoft.AspNetCore.Razor.Language.Extensions;
1112
using Microsoft.AspNetCore.Razor.Language.Intermediate;
13+
using Microsoft.AspNetCore.Razor.PooledObjects;
1214

1315
namespace Microsoft.AspNetCore.Razor.Language.Components;
1416

@@ -529,11 +531,15 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE
529531
}
530532
else
531533
{
534+
using var _ = ArrayBuilderPool<IntermediateNode>.GetPooledObject(out var builder);
535+
536+
var valuePropertyName = valueAttribute?.GetPropertyName();
537+
532538
ComponentAttributeIntermediateNode valueNode = node != null ? new ComponentAttributeIntermediateNode(node) : new ComponentAttributeIntermediateNode(getNode);
533539
valueNode.Annotations[ComponentMetadata.Common.OriginalAttributeName] = bindEntry.GetOriginalAttributeName();
534540
valueNode.AttributeName = valueAttributeName;
535541
valueNode.BoundAttribute = valueAttribute; // Might be null if it doesn't match a component attribute
536-
valueNode.PropertyName = valueAttribute?.GetPropertyName();
542+
valueNode.PropertyName = valuePropertyName;
537543
valueNode.TagHelper = valueAttribute == null ? null : bindEntry.GetEffectiveNodeTagHelperDescriptor();
538544
valueNode.TypeName = valueAttribute?.IsWeaklyTyped() == false ? valueAttribute.TypeName : null;
539545

@@ -544,6 +550,8 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE
544550
valueNode.Children[0].Children.Add(valueExpressionTokens[i]);
545551
}
546552

553+
builder.Add(valueNode);
554+
547555
var changeNode = node != null ? new ComponentAttributeIntermediateNode(node) : new ComponentAttributeIntermediateNode(getNode);
548556
changeNode.Annotations[ComponentMetadata.Common.OriginalAttributeName] = bindEntry.GetOriginalAttributeName();
549557
changeNode.AttributeName = changeAttributeName;
@@ -559,12 +567,13 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE
559567
changeNode.Children[0].Children.Add(changeExpressionTokens[i]);
560568
}
561569

570+
builder.Add(changeNode);
571+
562572
// Finally, also emit a node for the "Expression" attribute, but only if the target
563573
// component is defined to accept one
564-
ComponentAttributeIntermediateNode expressionNode = null;
565574
if (expressionAttribute != null)
566575
{
567-
expressionNode = node != null ? new ComponentAttributeIntermediateNode(node) : new ComponentAttributeIntermediateNode(getNode);
576+
var expressionNode = node != null ? new ComponentAttributeIntermediateNode(node) : new ComponentAttributeIntermediateNode(getNode);
568577
expressionNode.Annotations[ComponentMetadata.Common.OriginalAttributeName] = bindEntry.GetOriginalAttributeName();
569578
expressionNode.AttributeName = expressionAttributeName;
570579
expressionNode.BoundAttribute = expressionAttribute;
@@ -579,12 +588,34 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE
579588
Content = $"() => {original.Content}",
580589
Kind = TokenKind.CSharp
581590
});
591+
592+
builder.Add(expressionNode);
582593
}
583594

584-
return expressionNode == null
585-
? new[] { valueNode, changeNode }
586-
: new[] { valueNode, changeNode, expressionNode };
595+
// We don't need to generate any runtime code for these attributes normally, as they're handled by the above nodes,
596+
// but in order for IDE scenarios around component attributes to work we need to generate a little bit of design
597+
// time code, so we create design time specific nodes with minimal information in order to do so.
598+
TryAddDesignTimePropertyAccessHelperNode(builder, bindEntry.BindSetNode, valuePropertyName);
599+
TryAddDesignTimePropertyAccessHelperNode(builder, bindEntry.BindEventNode, valuePropertyName);
600+
TryAddDesignTimePropertyAccessHelperNode(builder, bindEntry.BindAfterNode, valuePropertyName);
601+
602+
return builder.ToArray();
603+
}
604+
}
605+
606+
private static void TryAddDesignTimePropertyAccessHelperNode(ImmutableArray<IntermediateNode>.Builder builder, TagHelperDirectiveAttributeParameterIntermediateNode intermediateNode, string propertyName)
607+
{
608+
if (intermediateNode is null || propertyName is null)
609+
{
610+
return;
587611
}
612+
613+
var helperNode = new ComponentAttributeIntermediateNode(intermediateNode);
614+
helperNode.Annotations[ComponentMetadata.Common.OriginalAttributeName] = intermediateNode.OriginalAttributeName;
615+
helperNode.Annotations[ComponentMetadata.Common.IsDesignTimePropertyAccessHelper] = bool.TrueString;
616+
helperNode.PropertyName = propertyName;
617+
618+
builder.Add(helperNode);
588619
}
589620

590621
private bool TryParseBindAttribute(BindEntry bindEntry, out string valueAttributeName)

src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDesignTimeNodeWriter.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ public override void WriteComponent(CodeRenderingContext context, ComponentInter
362362
throw new ArgumentNullException(nameof(node));
363363
}
364364

365-
// We might need a scope for inferring types,
365+
// We might need a scope for inferring types,
366366
CodeWriterExtensions.CSharpCodeWritingScope? typeInferenceCaptureScope = null;
367367
string typeInferenceLocalName = null;
368368

@@ -632,6 +632,12 @@ public override void WriteComponentAttribute(CodeRenderingContext context, Compo
632632
throw new ArgumentNullException(nameof(node));
633633
}
634634

635+
// This attribute might only be here in order to allow us to generate code in WritePropertyAccess
636+
if (node.IsDesignTimePropertyAccessHelper())
637+
{
638+
return;
639+
}
640+
635641
// Looks like:
636642
// __o = 17;
637643
context.CodeWriter.Write(DesignTimeVariable);
@@ -647,7 +653,7 @@ public override void WriteComponentAttribute(CodeRenderingContext context, Compo
647653
private void WritePropertyAccess(CodeRenderingContext context, ComponentAttributeIntermediateNode node, ComponentIntermediateNode componentNode, string typeInferenceLocalName, bool shouldWriteBL0005Disable, out bool wrotePropertyAccess)
648654
{
649655
wrotePropertyAccess = false;
650-
if (node?.TagHelper?.Name is null || node.Annotations["OriginalAttributeSpan"] is null)
656+
if (node?.TagHelper?.Name is null || node.Annotations[ComponentMetadata.Common.OriginalAttributeSpan] is null)
651657
{
652658
return;
653659
}
@@ -683,7 +689,7 @@ private void WritePropertyAccess(CodeRenderingContext context, ComponentAttribut
683689
context.CodeWriter.WriteLine("#pragma warning disable BL0005");
684690
}
685691

686-
var attributeSourceSpan = (SourceSpan)node.Annotations["OriginalAttributeSpan"];
692+
var attributeSourceSpan = (SourceSpan)node.Annotations[ComponentMetadata.Common.OriginalAttributeSpan];
687693
attributeSourceSpan = new SourceSpan(attributeSourceSpan.FilePath, attributeSourceSpan.AbsoluteIndex + offset, attributeSourceSpan.LineIndex, attributeSourceSpan.CharacterIndex + offset, node.PropertyName.Length, attributeSourceSpan.LineCount, attributeSourceSpan.CharacterIndex + offset + node.PropertyName.Length);
688694

689695
if (componentNode.TypeInferenceNode == null)

src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentMetadata.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ public static class Common
4141
public const string DirectiveAttribute = "Common.DirectiveAttribute";
4242

4343
public const string AddAttributeMethodName = "Common.AddAttributeMethodName";
44+
45+
public const string OriginalAttributeSpan = "Common.OriginalAttributeSpan";
46+
47+
public const string IsDesignTimePropertyAccessHelper = "Common.IsDesignTimePropertyAccessHelper";
4448
}
4549

4650
public static class Bind

src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentNodeWriter.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,12 @@ protected List<TypeInferenceMethodParameter> GetTypeInferenceMethodParameters(Co
326326
{
327327
if (child is ComponentAttributeIntermediateNode attribute)
328328
{
329+
// Some nodes just exist to help with property access at design time, and don't need anything else written
330+
if (child.IsDesignTimePropertyAccessHelper())
331+
{
332+
continue;
333+
}
334+
329335
string typeName;
330336
if (attribute.GloballyQualifiedTypeName != null)
331337
{

src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentRuntimeNodeWriter.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,11 @@ public override void WriteComponentAttribute(CodeRenderingContext context, Compo
547547
throw new ArgumentNullException(nameof(node));
548548
}
549549

550+
if (node.IsDesignTimePropertyAccessHelper())
551+
{
552+
return;
553+
}
554+
550555
var addAttributeMethod = node.Annotations[ComponentMetadata.Common.AddAttributeMethodName] as string ?? AddComponentParameterMethodName;
551556

552557
// _builder.AddComponentParameter(1, "Foo", 42);

src/Compiler/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorIntermediateNodeLoweringPhase.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,7 +1768,7 @@ public override void VisitMarkupMinimizedTagHelperAttribute(MarkupMinimizedTagHe
17681768
IsIndexerNameMatch = indexerMatch,
17691769
};
17701770

1771-
setTagHelperProperty.Annotations.Add("OriginalAttributeSpan", BuildSourceSpanFromNode(node.Name));
1771+
setTagHelperProperty.Annotations.Add(ComponentMetadata.Common.OriginalAttributeSpan, BuildSourceSpanFromNode(node.Name));
17721772

17731773
_builder.Add(setTagHelperProperty);
17741774
}
@@ -1909,7 +1909,7 @@ public override void VisitMarkupTagHelperAttribute(MarkupTagHelperAttributeSynta
19091909
IsIndexerNameMatch = indexerMatch,
19101910
};
19111911

1912-
setTagHelperProperty.Annotations.Add("OriginalAttributeSpan", BuildSourceSpanFromNode(node.Name));
1912+
setTagHelperProperty.Annotations.Add(ComponentMetadata.Common.OriginalAttributeSpan, BuildSourceSpanFromNode(node.Name));
19131913

19141914
_builder.Push(setTagHelperProperty);
19151915
VisitAttributeValue(attributeValueNode);
@@ -1988,7 +1988,7 @@ public override void VisitMarkupTagHelperDirectiveAttribute(MarkupTagHelperDirec
19881988
};
19891989
}
19901990

1991-
attributeNode.Annotations.Add("OriginalAttributeSpan", BuildSourceSpanFromNode(node.Name));
1991+
attributeNode.Annotations.Add(ComponentMetadata.Common.OriginalAttributeSpan, BuildSourceSpanFromNode(node.Name));
19921992

19931993
_builder.Push(attributeNode);
19941994
VisitAttributeValue(attributeValueNode);

src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Intermediate/ComponentAttributeIntermediateNode.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public ComponentAttributeIntermediateNode(TagHelperHtmlAttributeIntermediateNode
2525
AttributeStructure = attributeNode.AttributeStructure;
2626
Source = attributeNode.Source;
2727

28+
foreach (var annotation in attributeNode.Annotations)
29+
{
30+
Annotations[annotation.Key] = annotation.Value;
31+
}
32+
2833
for (var i = 0; i < attributeNode.Children.Count; i++)
2934
{
3035
Children.Add(attributeNode.Children[i]);
@@ -115,6 +120,11 @@ public ComponentAttributeIntermediateNode(TagHelperDirectiveAttributeParameterIn
115120
TagHelper = directiveAttributeParameterNode.TagHelper;
116121
TypeName = directiveAttributeParameterNode.BoundAttributeParameter.TypeName;
117122

123+
foreach (var annotation in directiveAttributeParameterNode.Annotations)
124+
{
125+
Annotations[annotation.Key] = annotation.Value;
126+
}
127+
118128
for (var i = 0; i < directiveAttributeParameterNode.Children.Count; i++)
119129
{
120130
Children.Add(directiveAttributeParameterNode.Children[i]);

src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Intermediate/IntermediateNodeExtensions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System;
77
using System.Collections.Generic;
88
using System.Linq;
9+
using Microsoft.AspNetCore.Razor.Language.Components;
910

1011
namespace Microsoft.AspNetCore.Razor.Language.Intermediate;
1112

@@ -18,6 +19,13 @@ public static bool IsImported(this IntermediateNode node)
1819
return ReferenceEquals(node.Annotations[CommonAnnotations.Imported], CommonAnnotations.Imported);
1920
}
2021

22+
public static bool IsDesignTimePropertyAccessHelper(this IntermediateNode tagHelper)
23+
{
24+
return tagHelper.Annotations[ComponentMetadata.Common.IsDesignTimePropertyAccessHelper] is string text &&
25+
bool.TryParse(text, out var result) &&
26+
result;
27+
}
28+
2129
public static IReadOnlyList<RazorDiagnostic> GetAllDiagnostics(this IntermediateNode node)
2230
{
2331
if (node == null)

src/Compiler/Microsoft.AspNetCore.Razor.Language/src/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Microsoft.AspNetCore.Razor.Language.TagHelperDescriptorBuilder.PooledBuilder.Poo
1414
~static Microsoft.AspNetCore.Razor.Language.BoundAttributeDescriptorBuilderExtensions.SetBindAttributeGetSet(this Microsoft.AspNetCore.Razor.Language.BoundAttributeParameterDescriptorBuilder builder) -> void
1515
~static Microsoft.AspNetCore.Razor.Language.BoundAttributeDescriptorBuilderExtensions.SetGloballyQualifiedTypeName(this Microsoft.AspNetCore.Razor.Language.BoundAttributeDescriptorBuilder builder, string globallyQualifiedTypeName) -> void
1616
~static Microsoft.AspNetCore.Razor.Language.BoundAttributeDescriptorExtensions.GetGloballyQualifiedTypeName(this Microsoft.AspNetCore.Razor.Language.BoundAttributeDescriptor attribute) -> string
17+
~static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeExtensions.IsDesignTimePropertyAccessHelper(this Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNode tagHelper) -> bool
1718
~static Microsoft.AspNetCore.Razor.Language.RazorCodeDocumentExtensions.GetPreTagHelperSyntaxTree(this Microsoft.AspNetCore.Razor.Language.RazorCodeDocument document) -> Microsoft.AspNetCore.Razor.Language.RazorSyntaxTree
1819
~static Microsoft.AspNetCore.Razor.Language.RazorCodeDocumentExtensions.SetPreTagHelperSyntaxTree(this Microsoft.AspNetCore.Razor.Language.RazorCodeDocument document, Microsoft.AspNetCore.Razor.Language.RazorSyntaxTree syntaxTree) -> void
1920
~static Microsoft.AspNetCore.Razor.Language.RazorCSharpDocument.Create(Microsoft.AspNetCore.Razor.Language.RazorCodeDocument codeDocument, string generatedCode, Microsoft.AspNetCore.Razor.Language.RazorCodeGenerationOptions options, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Razor.Language.RazorDiagnostic> diagnostics) -> Microsoft.AspNetCore.Razor.Language.RazorCSharpDocument

src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,6 @@ public void Component_WithWriteOnlyParameter()
360360
// Arrange
361361
AdditionalSyntaxTrees.Add(Parse("""
362362
using Microsoft.AspNetCore.Components;
363-
364363
namespace Test
365364
{
366365
public class MyComponent : ComponentBase
@@ -389,14 +388,12 @@ public void Component_WithInitOnlyParameter()
389388
// Arrange
390389
AdditionalSyntaxTrees.Add(Parse("""
391390
namespace System.Runtime.CompilerServices;
392-
393391
internal static class IsExternalInit
394392
{
395393
}
396394
"""));
397395
AdditionalSyntaxTrees.Add(Parse("""
398396
using Microsoft.AspNetCore.Components;
399-
400397
namespace Test
401398
{
402399
public class MyComponent : ComponentBase

0 commit comments

Comments
 (0)