diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/InstrumentationPassTest.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/InstrumentationPassTest.cs index 098b5972534..f59bfe096bc 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/InstrumentationPassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/InstrumentationPassTest.cs @@ -147,9 +147,17 @@ public void InstrumentationPass_SkipsCSharpExpression_InsideTagHelperAttribute() var builder = IntermediateNodeBuilder.Create(documentNode); - builder.Push(new TagHelperIntermediateNode()); + builder.Push(new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }); - builder.Push(new TagHelperHtmlAttributeIntermediateNode()); + builder.Push(new TagHelperHtmlAttributeIntermediateNode() + { + AttributeName = "Test", + AttributeStructure = 0 + }); builder.Push(new CSharpExpressionIntermediateNode() { @@ -188,9 +196,17 @@ public void InstrumentationPass_SkipsCSharpExpression_InsideTagHelperProperty() var builder = IntermediateNodeBuilder.Create(documentNode); - builder.Push(new TagHelperIntermediateNode()); + builder.Push(new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }); - builder.Push(new TagHelperPropertyIntermediateNode()); + builder.Push(new TagHelperPropertyIntermediateNode(match: default) + { + AttributeName = "Test", + AttributeStructure = 0 + }); builder.Push(new CSharpExpressionIntermediateNode() { @@ -231,6 +247,8 @@ public void InstrumentationPass_InstrumentsTagHelper() builder.Add(new TagHelperIntermediateNode() { + TagMode = 0, + TagName = "Test", Source = CreateSource(3) }); @@ -254,7 +272,11 @@ public void InstrumentationPass_SkipsTagHelper_WithoutLocation() var builder = IntermediateNodeBuilder.Create(documentNode); - builder.Push(new TagHelperIntermediateNode()); + builder.Push(new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }); // Act ProjectEngine.ExecutePass(codeDocument, documentNode); diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/DefaultTagHelperTargetExtensionTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/DefaultTagHelperTargetExtensionTest.cs index aa2a5757908..286b781fe76 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/DefaultTagHelperTargetExtensionTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/DefaultTagHelperTargetExtensionTest.cs @@ -61,7 +61,12 @@ public void WriteTagHelperBody_DesignTime_WritesChildren() var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateDesignTime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperBodyIntermediateNode() { Children = @@ -91,7 +96,12 @@ public void WriteTagHelperBody_Runtime_RendersCorrectly_UsesTagNameAndModeFromCo var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperBodyIntermediateNode() { Children = @@ -126,7 +136,12 @@ public void WriteTagHelperCreate_DesignTime_RendersCorrectly_UsesSpecifiedTagHel var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateDesignTime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperCreateIntermediateNode() { FieldName = "__TestNamespace_MyTagHelper", @@ -154,7 +169,12 @@ public void WriteTagHelperCreate_Runtime_RendersCorrectly_UsesSpecifiedTagHelper var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperCreateIntermediateNode() { FieldName = "__TestNamespace_MyTagHelper", @@ -183,7 +203,12 @@ public void WriteTagHelperExecute_DesignTime_RendersAsyncCode() var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateDesignTime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperExecuteIntermediateNode(); tagHelperNode.Children.Add(node); Push(context, tagHelperNode); @@ -207,7 +232,12 @@ public void WriteTagHelperExecute_Runtime_RendersCorrectly() var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperExecuteIntermediateNode(); tagHelperNode.Children.Add(node); Push(context, tagHelperNode); @@ -237,7 +267,12 @@ public void WriteTagHelperHtmlAttribute_DesignTime_WritesNothing() var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateDesignTime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperHtmlAttributeIntermediateNode() { AttributeName = "name", @@ -277,7 +312,12 @@ public void WriteTagHelperHtmlAttribute_Runtime_SimpleAttribute_RendersCorrectly var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperHtmlAttributeIntermediateNode() { AttributeName = "name", @@ -315,7 +355,12 @@ public void WriteTagHelperHtmlAttribute_Runtime_DynamicAttribute_RendersCorrectl var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperHtmlAttributeIntermediateNode() { AttributeName = "name", @@ -425,7 +470,12 @@ public void WriteTagHelperProperty_DesignTime_StringProperty_HtmlContent_Renders var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateDesignTime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "bound", @@ -434,7 +484,6 @@ public void WriteTagHelperProperty_DesignTime_StringProperty_HtmlContent_Renders FieldName = "__InputTagHelper", IsIndexerNameMatch = false, PropertyName = "StringProp", - TagHelper = StringPropertyTagHelper, Children = { new HtmlContentIntermediateNode() @@ -466,7 +515,12 @@ public void WriteTagHelperProperty_DesignTime_StringProperty_NonHtmlContent_Rend var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateDesignTime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "bound", @@ -475,7 +529,6 @@ public void WriteTagHelperProperty_DesignTime_StringProperty_NonHtmlContent_Rend FieldName = "__InputTagHelper", IsIndexerNameMatch = false, PropertyName = "StringProp", - TagHelper = StringPropertyTagHelper, Children = { new CSharpExpressionIntermediateNode() @@ -507,7 +560,12 @@ public void WriteTagHelperProperty_DesignTime_NonStringProperty_RendersCorrectly var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateDesignTime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "bound", @@ -516,7 +574,6 @@ public void WriteTagHelperProperty_DesignTime_NonStringProperty_RendersCorrectly FieldName = "__InputTagHelper", IsIndexerNameMatch = false, PropertyName = "IntProp", - TagHelper = IntPropertyTagHelper, Source = Span, Children = { @@ -557,7 +614,12 @@ public void WriteTagHelperProperty_DesignTime_NonStringProperty_SecondUseOfAttri var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateDesignTime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node1 = new DefaultTagHelperPropertyIntermediateNode() { // We only look at the attribute name here. @@ -573,7 +635,6 @@ public void WriteTagHelperProperty_DesignTime_NonStringProperty_SecondUseOfAttri FieldName = "__InputTagHelper", IsIndexerNameMatch = false, PropertyName = "IntProp", - TagHelper = IntPropertyTagHelper, Source = Span, }; tagHelperNode.Children.Add(node1); @@ -599,7 +660,12 @@ public void WriteTagHelperProperty_DesignTime_NonStringProperty_RendersCorrectly var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateDesignTime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "bound", @@ -608,7 +674,6 @@ public void WriteTagHelperProperty_DesignTime_NonStringProperty_RendersCorrectly FieldName = "__InputTagHelper", IsIndexerNameMatch = false, PropertyName = "IntProp", - TagHelper = IntPropertyTagHelper, Children = { new CSharpExpressionIntermediateNode() @@ -639,7 +704,12 @@ public void WriteTagHelperProperty_DesignTime_NonStringIndexer_RendersCorrectly( var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateDesignTime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "foo-bound", @@ -648,7 +718,6 @@ public void WriteTagHelperProperty_DesignTime_NonStringIndexer_RendersCorrectly( FieldName = "__InputTagHelper", IsIndexerNameMatch = true, PropertyName = "IntIndexer", - TagHelper = IntIndexerTagHelper, Source = Span, Children = { @@ -687,7 +756,12 @@ public void WriteTagHelperProperty_DesignTime_NonStringIndexer_RendersCorrectly_ var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateDesignTime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "foo-bound", @@ -696,7 +770,6 @@ public void WriteTagHelperProperty_DesignTime_NonStringIndexer_RendersCorrectly_ FieldName = "__InputTagHelper", IsIndexerNameMatch = true, PropertyName = "IntIndexer", - TagHelper = IntIndexerTagHelper, Children = { new CSharpExpressionIntermediateNode() @@ -727,7 +800,12 @@ public void WriteTagHelperProperty_Runtime_StringProperty_HtmlContent_RendersCor var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "bound", @@ -736,7 +814,6 @@ public void WriteTagHelperProperty_Runtime_StringProperty_HtmlContent_RendersCor FieldName = "__InputTagHelper", IsIndexerNameMatch = false, PropertyName = "StringProp", - TagHelper = StringPropertyTagHelper, Children = { new HtmlContentIntermediateNode() @@ -773,7 +850,12 @@ public void WriteTagHelperProperty_Runtime_NonStringProperty_RendersCorrectly() var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "bound", @@ -782,7 +864,6 @@ public void WriteTagHelperProperty_Runtime_NonStringProperty_RendersCorrectly() FieldName = "__InputTagHelper", IsIndexerNameMatch = false, PropertyName = "IntProp", - TagHelper = IntPropertyTagHelper, Children = { new CSharpExpressionIntermediateNode() @@ -824,7 +905,12 @@ public void WriteTagHelperProperty_Runtime_NonStringProperty_SecondUseOfAttribut var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node1 = new DefaultTagHelperPropertyIntermediateNode() { // We only look at the attribute name here. @@ -840,7 +926,6 @@ public void WriteTagHelperProperty_Runtime_NonStringProperty_SecondUseOfAttribut FieldName = "__InputTagHelper", IsIndexerNameMatch = false, PropertyName = "IntProp", - TagHelper = IntPropertyTagHelper, Source = Span, }; tagHelperNode.Children.Add(node1); @@ -866,7 +951,12 @@ public void WriteTagHelperProperty_Runtime_NonStringProperty_RendersCorrectly_Wi var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "bound", @@ -875,7 +965,6 @@ public void WriteTagHelperProperty_Runtime_NonStringProperty_RendersCorrectly_Wi FieldName = "__InputTagHelper", IsIndexerNameMatch = false, PropertyName = "IntProp", - TagHelper = IntPropertyTagHelper, Children = { new CSharpExpressionIntermediateNode() @@ -907,7 +996,12 @@ public void WriteTagHelperProperty_Runtime_NonStringIndexer_RendersCorrectly() var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "foo-bound", @@ -916,7 +1010,6 @@ public void WriteTagHelperProperty_Runtime_NonStringIndexer_RendersCorrectly() FieldName = "__InputTagHelper", IsIndexerNameMatch = true, PropertyName = "IntIndexer", - TagHelper = IntIndexerTagHelper, Children = { new CSharpExpressionIntermediateNode() @@ -960,7 +1053,12 @@ public void WriteTagHelperProperty_Runtime_NonStringIndexer_MultipleValues() var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node1 = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "foo-first", @@ -969,7 +1067,6 @@ public void WriteTagHelperProperty_Runtime_NonStringIndexer_MultipleValues() FieldName = "__InputTagHelper", IsIndexerNameMatch = true, PropertyName = "IntIndexer", - TagHelper = IntIndexerTagHelper, Children = { new CSharpExpressionIntermediateNode() @@ -986,7 +1083,6 @@ public void WriteTagHelperProperty_Runtime_NonStringIndexer_MultipleValues() FieldName = "__InputTagHelper", IsIndexerNameMatch = true, PropertyName = "IntIndexer", - TagHelper = IntIndexerTagHelper, Children = { new CSharpExpressionIntermediateNode() @@ -1027,7 +1123,12 @@ public void WriteTagHelperProperty_Runtime_NonStringIndexer_RendersCorrectly_Wit var extension = new DefaultTagHelperTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new DefaultTagHelperPropertyIntermediateNode() { AttributeName = "foo-bound", @@ -1036,7 +1137,6 @@ public void WriteTagHelperProperty_Runtime_NonStringIndexer_RendersCorrectly_Wit FieldName = "__InputTagHelper", IsIndexerNameMatch = true, PropertyName = "IntIndexer", - TagHelper = IntIndexerTagHelper, Children = { new CSharpExpressionIntermediateNode() diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/PreallocatedAttributeTargetExtensionTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/PreallocatedAttributeTargetExtensionTest.cs index 962fba9f110..e709ea0a939 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/PreallocatedAttributeTargetExtensionTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/PreallocatedAttributeTargetExtensionTest.cs @@ -73,7 +73,12 @@ public void WriteTagHelperHtmlAttribute_RendersCorrectly() var extension = new PreallocatedAttributeTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperNode = new TagHelperIntermediateNode(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; + var node = new PreallocatedTagHelperHtmlAttributeIntermediateNode() { VariableName = "_tagHelper1" @@ -127,26 +132,31 @@ public void WriteTagHelperProperty_RendersCorrectly() var extension = new PreallocatedAttributeTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperBuilder = new TagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "FooTagHelper", "Test"); - tagHelperBuilder.Metadata(TypeName("FooTagHelper")); + var tagHelper = TagHelperDescriptorBuilder.Create("FooTagHelper", "Test") + .Metadata(TypeName("FooTagHelper")) + .BoundAttributeDescriptor(builder => builder + .Name("Foo") + .TypeName("System.String") + .PropertyName("FooProp")) + .Build(); - var builder = new BoundAttributeDescriptorBuilder(tagHelperBuilder); - builder - .Name("Foo") - .TypeName("System.String") - .PropertyName("FooProp"); + var attribute = tagHelper.BoundAttributes[0]; - var descriptor = builder.Build(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; - var tagHelperNode = new TagHelperIntermediateNode(); var node = new PreallocatedTagHelperPropertyIntermediateNode() { - AttributeName = descriptor.Name, - BoundAttribute = descriptor, + AttributeName = attribute.Name, + BoundAttribute = attribute, FieldName = "__FooTagHelper", PropertyName = "FooProp", VariableName = "_tagHelper1", }; + tagHelperNode.Children.Add(node); Push(context, tagHelperNode); @@ -170,29 +180,33 @@ public void WriteSetPreallocatedTagHelperProperty_IndexerAttribute_RendersCorrec var extension = new PreallocatedAttributeTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperBuilder = new TagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "FooTagHelper", "Test"); - tagHelperBuilder.Metadata(TypeName("FooTagHelper")); + var tagHelper = TagHelperDescriptorBuilder.Create("FooTagHelper", "Test") + .Metadata(TypeName("FooTagHelper")) + .BoundAttributeDescriptor(builder => builder + .Name("Foo") + .TypeName("System.Collections.Generic.Dictionary") + .AsDictionaryAttribute("pre-", "System.String") + .PropertyName("FooProp")) + .Build(); - var builder = new BoundAttributeDescriptorBuilder(tagHelperBuilder); - builder - .Name("Foo") - .TypeName("System.Collections.Generic.Dictionary") - .AsDictionaryAttribute("pre-", "System.String") - .PropertyName("FooProp"); + var attribute = tagHelper.BoundAttributes[0]; - var descriptor = builder.Build(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; - var tagHelperNode = new TagHelperIntermediateNode(); var node = new PreallocatedTagHelperPropertyIntermediateNode() { AttributeName = "pre-Foo", FieldName = "__FooTagHelper", VariableName = "_tagHelper1", - BoundAttribute = descriptor, + BoundAttribute = attribute, IsIndexerNameMatch = true, PropertyName = "FooProp", - TagHelper = tagHelperBuilder.Build(), }; + tagHelperNode.Children.Add(node); Push(context, tagHelperNode); @@ -220,40 +234,43 @@ public void WriteSetPreallocatedTagHelperProperty_IndexerAttribute_MultipleValue var extension = new PreallocatedAttributeTargetExtension(); using var context = TestCodeRenderingContext.CreateRuntime(); - var tagHelperBuilder = new TagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "FooTagHelper", "Test"); - tagHelperBuilder.Metadata(TypeName("FooTagHelper")); + var tagHelper = TagHelperDescriptorBuilder.Create("FooTagHelper", "Test") + .Metadata(TypeName("FooTagHelper")) + .BoundAttributeDescriptor(builder => builder + .Name("Foo") + .TypeName("System.Collections.Generic.Dictionary") + .AsDictionaryAttribute("pre-", "System.String") + .PropertyName("FooProp")) + .Build(); - var builder = new BoundAttributeDescriptorBuilder(tagHelperBuilder); - builder - .Name("Foo") - .TypeName("System.Collections.Generic.Dictionary") - .AsDictionaryAttribute("pre-", "System.String") - .PropertyName("FooProp"); + var attribute = tagHelper.BoundAttributes[0]; - var boundAttribute = builder.Build(); - var tagHelper = tagHelperBuilder.Build(); + var tagHelperNode = new TagHelperIntermediateNode() + { + TagMode = 0, + TagName = "Test" + }; - var tagHelperNode = new TagHelperIntermediateNode(); var node1 = new PreallocatedTagHelperPropertyIntermediateNode() { AttributeName = "pre-Bar", FieldName = "__FooTagHelper", VariableName = "_tagHelper0s", - BoundAttribute = boundAttribute, + BoundAttribute = attribute, IsIndexerNameMatch = true, - PropertyName = "FooProp", - TagHelper = tagHelper, + PropertyName = "FooProp" }; + var node2 = new PreallocatedTagHelperPropertyIntermediateNode() { AttributeName = "pre-Foo", FieldName = "__FooTagHelper", VariableName = "_tagHelper1", - BoundAttribute = boundAttribute, + BoundAttribute = attribute, IsIndexerNameMatch = true, - PropertyName = "FooProp", - TagHelper = tagHelper, + PropertyName = "FooProp" }; + tagHelperNode.Children.Add(node1); tagHelperNode.Children.Add(node2); Push(context, tagHelperNode); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentBindLoweringPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentBindLoweringPass.cs index 70af0af9fb7..aa390a8e044 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentBindLoweringPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentBindLoweringPass.cs @@ -523,7 +523,6 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE valueNode.AttributeName = valueAttributeName; valueNode.BoundAttribute = valueAttribute; // Might be null if it doesn't match a component attribute valueNode.PropertyName = valuePropertyName; - valueNode.TagHelper = valueAttribute == null ? null : bindEntry.GetEffectiveNodeTagHelperDescriptor(); valueNode.TypeName = valueAttribute?.IsWeaklyTyped == false ? valueAttribute.TypeName : null; valueNode.Children.Clear(); @@ -542,7 +541,6 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE changeNode.AttributeName = changeAttributeName; changeNode.BoundAttribute = changeAttribute; // Might be null if it doesn't match a component attribute changeNode.PropertyName = changeAttribute?.PropertyName; - changeNode.TagHelper = changeAttribute == null ? null : bindEntry.GetEffectiveNodeTagHelperDescriptor(); changeNode.TypeName = changeAttribute?.IsWeaklyTyped == false ? changeAttribute.TypeName : null; changeNode.Children.Clear(); @@ -565,7 +563,6 @@ private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindE expressionNode.AttributeName = expressionAttributeName; expressionNode.BoundAttribute = expressionAttribute; expressionNode.PropertyName = expressionAttribute.PropertyName; - expressionNode.TagHelper = bindEntry.GetEffectiveNodeTagHelperDescriptor(); expressionNode.TypeName = expressionAttribute.IsWeaklyTyped ? null : expressionAttribute.TypeName; expressionNode.Children.Clear(); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentEventHandlerLoweringPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentEventHandlerLoweringPass.cs index 28dbff3e4f4..59ac54adf8d 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentEventHandlerLoweringPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentEventHandlerLoweringPass.cs @@ -136,7 +136,7 @@ private static void ProcessDuplicates(IntermediateNode parent) var parameterDuplicates = parent.Children .OfType() - .Where(p => p.TagHelper?.IsEventHandlerTagHelper() ?? false) + .Where(p => p.TagHelper.IsEventHandlerTagHelper()) .GroupBy(p => p.AttributeName) .Where(g => g.Count() > 1); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs index 7ecfeb47f1b..ab161e20175 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs @@ -89,9 +89,8 @@ static TagHelperDescriptor GetTagHelperOrAddDiagnostic(TagHelperIntermediateNode { TagHelperDescriptor candidate = null; List matched = null; - for (var i = 0; i < node.TagHelpers.Count; i++) + foreach (var tagHelper in node.TagHelpers) { - var tagHelper = node.TagHelpers[i]; if (!tagHelper.IsComponentTagHelper) { continue; diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorIntermediateNodeLoweringPhase.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorIntermediateNodeLoweringPhase.cs index a22fe867146..02829ac8d2c 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorIntermediateNodeLoweringPhase.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorIntermediateNodeLoweringPhase.cs @@ -1032,14 +1032,10 @@ public override void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax no { TagName = tagName, TagMode = info.TagMode, - Source = BuildSourceSpanFromNode(node) + Source = BuildSourceSpanFromNode(node), + TagHelpers = info.BindingResult.Descriptors }; - foreach (var tagHelper in info.BindingResult.Descriptors) - { - tagHelperNode.TagHelpers.Add(tagHelper); - } - _builder.Push(tagHelperNode); _builder.Push(new TagHelperBodyIntermediateNode()); @@ -1084,34 +1080,25 @@ public override void VisitMarkupMinimizedTagHelperAttribute(MarkupMinimizedTagHe var element = node.FirstAncestorOrSelf(); var descriptors = element.TagHelperInfo.BindingResult.Descriptors; var attributeName = node.Name.GetContent(); - var associatedDescriptors = descriptors.Where(descriptor => - descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor))); - if (associatedDescriptors.Any() && _renderedBoundAttributeNames.Add(attributeName)) + using var matches = new PooledArrayBuilder(); + TagHelperMatchingConventions.GetAttributeMatches(descriptors, attributeName, ref matches.AsRef()); + + if (matches.Any() && _renderedBoundAttributeNames.Add(attributeName)) { - foreach (var associatedDescriptor in associatedDescriptors) + foreach (var match in matches) { - var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(a => - { - return TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, a); - }); - - var expectsBooleanValue = associatedAttributeDescriptor.ExpectsBooleanValue(attributeName); - - if (!expectsBooleanValue) + if (!match.ExpectsBooleanValue) { // We do not allow minimized non-boolean bound attributes. return; } - var setTagHelperProperty = new TagHelperPropertyIntermediateNode() + var setTagHelperProperty = new TagHelperPropertyIntermediateNode(match) { AttributeName = attributeName, - BoundAttribute = associatedAttributeDescriptor, - TagHelper = associatedDescriptor, AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, Source = null, - IsIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(associatedAttributeDescriptor, attributeName.AsSpan()), }; _builder.Add(setTagHelperProperty); @@ -1136,31 +1123,18 @@ public override void VisitMarkupTagHelperAttribute(MarkupTagHelperAttributeSynta var attributeName = node.Name.GetContent(); var attributeValueNode = node.Value; - using var associatedDescriptorsWithSatisfyingAttribute = new PooledArrayBuilder<(TagHelperDescriptor, BoundAttributeDescriptor)>(); - foreach (var descriptor in descriptors) - { - foreach (var attributeDescriptor in descriptor.BoundAttributes) - { - if (TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor)) - { - associatedDescriptorsWithSatisfyingAttribute.Add((descriptor, attributeDescriptor)); - break; - } - } - } + using var matches = new PooledArrayBuilder(); + TagHelperMatchingConventions.GetAttributeMatches(descriptors, attributeName, ref matches.AsRef()); - if (associatedDescriptorsWithSatisfyingAttribute.Any() && _renderedBoundAttributeNames.Add(attributeName)) + if (matches.Any() && _renderedBoundAttributeNames.Add(attributeName)) { - foreach (var (associatedDescriptor, associatedAttributeDescriptor) in associatedDescriptorsWithSatisfyingAttribute) + foreach (var match in matches) { - var setTagHelperProperty = new TagHelperPropertyIntermediateNode() + var setTagHelperProperty = new TagHelperPropertyIntermediateNode(match) { AttributeName = attributeName, - BoundAttribute = associatedAttributeDescriptor, - TagHelper = associatedDescriptor, AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, Source = BuildSourceSpanFromNode(attributeValueNode), - IsIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(associatedAttributeDescriptor, attributeName.AsSpan()), }; _builder.Push(setTagHelperProperty); @@ -1775,14 +1749,10 @@ public override void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax no { TagName = tagName, TagMode = info.TagMode, - Source = BuildSourceSpanFromNode(node) + Source = BuildSourceSpanFromNode(node), + TagHelpers = info.BindingResult.Descriptors }; - foreach (var tagHelper in info.BindingResult.Descriptors) - { - tagHelperNode.TagHelpers.Add(tagHelper); - } - if (node.StartTag != null && // We only want this error during the second phase of the two phase compilation. !_document.Options.SuppressPrimaryMethodBody && @@ -1856,43 +1826,29 @@ public override void VisitMarkupMinimizedTagHelperAttribute(MarkupMinimizedTagHe var element = node.FirstAncestorOrSelf(); var descriptors = element.TagHelperInfo.BindingResult.Descriptors; var attributeName = node.Name.GetContent(); - var associatedDescriptors = descriptors.Where(descriptor => - descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor))); - if (associatedDescriptors.Any() && _renderedBoundAttributeNames.Add(attributeName)) + using var matches = new PooledArrayBuilder(); + TagHelperMatchingConventions.GetAttributeMatches(descriptors, attributeName, ref matches.AsRef()); + + if (matches.Any() && _renderedBoundAttributeNames.Add(attributeName)) { - foreach (var associatedDescriptor in associatedDescriptors) + foreach (var match in matches) { - if (TagHelperMatchingConventions.TryGetFirstBoundAttributeMatch( - associatedDescriptor, - attributeName, - out var associatedAttributeDescriptor, - out var indexerMatch, - out _, - out _)) + if (!match.ExpectsBooleanValue) { - var expectsBooleanValue = associatedAttributeDescriptor.ExpectsBooleanValue(attributeName); - - if (!expectsBooleanValue) - { - // We do not allow minimized non-boolean bound attributes. - return; - } - - var setTagHelperProperty = new TagHelperPropertyIntermediateNode() - { - AttributeName = attributeName, - BoundAttribute = associatedAttributeDescriptor, - TagHelper = associatedDescriptor, - AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, - Source = null, - IsIndexerNameMatch = indexerMatch, - }; + // We do not allow minimized non-boolean bound attributes. + return; + } - setTagHelperProperty.OriginalAttributeSpan = BuildSourceSpanFromNode(node.Name); + var setTagHelperProperty = new TagHelperPropertyIntermediateNode(match) + { + AttributeName = attributeName, + AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, + Source = null, + OriginalAttributeSpan = BuildSourceSpanFromNode(node.Name) + }; - _builder.Add(setTagHelperProperty); - } + _builder.Add(setTagHelperProperty); } } else @@ -1919,72 +1875,40 @@ public override void VisitMarkupMinimizedTagHelperDirectiveAttribute(MarkupMinim var element = node.FirstAncestorOrSelf(); var descriptors = element.TagHelperInfo.BindingResult.Descriptors; var attributeName = node.FullName; - var associatedDescriptors = descriptors.Where(descriptor => - descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor))); - if (associatedDescriptors.Any() && _renderedBoundAttributeNames.Add(attributeName)) + using var matches = new PooledArrayBuilder(); + TagHelperMatchingConventions.GetAttributeMatches(descriptors, attributeName, ref matches.AsRef()); + + if (matches.Any() && _renderedBoundAttributeNames.Add(attributeName)) { - foreach (var associatedDescriptor in associatedDescriptors) + var directiveAttributeName = new DirectiveAttributeName(attributeName); + + foreach (var match in matches) { - if (TagHelperMatchingConventions.TryGetFirstBoundAttributeMatch( - associatedDescriptor, - attributeName, - out var associatedAttributeDescriptor, - out var indexerMatch, - out var parameterMatch, - out var associatedAttributeParameterDescriptor)) + if (!match.ExpectsBooleanValue) { - // Directive attributes should start with '@' unless the descriptors are misconfigured. - // In that case, we would have already logged an error. - var actualAttributeName = attributeName.StartsWith("@", StringComparison.Ordinal) ? attributeName.Substring(1) : attributeName; + // We do not allow minimized non-boolean bound attributes. + return; + } - IntermediateNode attributeNode; - if (parameterMatch && - TagHelperMatchingConventions.TryGetBoundAttributeParameter(actualAttributeName, out var attributeNameWithoutParameter)) + IntermediateNode attributeNode = match.IsParameterMatch && directiveAttributeName.HasParameter + ? new TagHelperDirectiveAttributeParameterIntermediateNode(match) { - var expectsBooleanValue = associatedAttributeParameterDescriptor.IsBooleanProperty; - if (!expectsBooleanValue) - { - // We do not allow minimized non-boolean bound attributes. - return; - } - - attributeNode = new TagHelperDirectiveAttributeParameterIntermediateNode() - { - AttributeName = actualAttributeName, - AttributeNameWithoutParameter = attributeNameWithoutParameter.ToString(), - OriginalAttributeName = attributeName, - BoundAttributeParameter = associatedAttributeParameterDescriptor, - BoundAttribute = associatedAttributeDescriptor, - TagHelper = associatedDescriptor, - IsIndexerNameMatch = indexerMatch, - AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, - Source = null, - }; + AttributeName = directiveAttributeName.Text, + AttributeNameWithoutParameter = directiveAttributeName.TextWithoutParameter, + OriginalAttributeName = attributeName, + AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, + Source = null } - else + : new TagHelperDirectiveAttributeIntermediateNode(match) { - var expectsBooleanValue = associatedAttributeDescriptor.ExpectsBooleanValue(attributeName); - if (!expectsBooleanValue) - { - // We do not allow minimized non-boolean bound attributes. - return; - } - - attributeNode = new TagHelperDirectiveAttributeIntermediateNode() - { - AttributeName = actualAttributeName, - OriginalAttributeName = attributeName, - BoundAttribute = associatedAttributeDescriptor, - TagHelper = associatedDescriptor, - AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, - Source = null, - IsIndexerNameMatch = indexerMatch, - }; - } + AttributeName = directiveAttributeName.Text, + OriginalAttributeName = attributeName, + AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, + Source = null, + }; - _builder.Add(attributeNode); - } + _builder.Add(attributeNode); } } else @@ -2005,37 +1929,25 @@ public override void VisitMarkupTagHelperAttribute(MarkupTagHelperAttributeSynta var descriptors = element.TagHelperInfo.BindingResult.Descriptors; var attributeName = node.Name.GetContent(); var attributeValueNode = node.Value; - var associatedDescriptors = descriptors.Where(descriptor => - descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor))); - if (associatedDescriptors.Any() && _renderedBoundAttributeNames.Add(attributeName)) + using var matches = new PooledArrayBuilder(); + TagHelperMatchingConventions.GetAttributeMatches(descriptors, attributeName, ref matches.AsRef()); + + if (matches.Any() && _renderedBoundAttributeNames.Add(attributeName)) { - foreach (var associatedDescriptor in associatedDescriptors) + foreach (var match in matches) { - if (TagHelperMatchingConventions.TryGetFirstBoundAttributeMatch( - associatedDescriptor, - attributeName, - out var associatedAttributeDescriptor, - out var indexerMatch, - out _, - out _)) + var setTagHelperProperty = new TagHelperPropertyIntermediateNode(match) { - var setTagHelperProperty = new TagHelperPropertyIntermediateNode() - { - AttributeName = attributeName, - BoundAttribute = associatedAttributeDescriptor, - TagHelper = associatedDescriptor, - AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, - Source = BuildSourceSpanFromNode(attributeValueNode), - IsIndexerNameMatch = indexerMatch, - }; - - setTagHelperProperty.OriginalAttributeSpan = BuildSourceSpanFromNode(node.Name); + AttributeName = attributeName, + AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, + Source = BuildSourceSpanFromNode(attributeValueNode), + OriginalAttributeSpan = BuildSourceSpanFromNode(node.Name) + }; - _builder.Push(setTagHelperProperty); - VisitAttributeValue(attributeValueNode); - _builder.Pop(); - } + _builder.Push(setTagHelperProperty); + VisitAttributeValue(attributeValueNode); + _builder.Pop(); } } else @@ -2059,62 +1971,37 @@ public override void VisitMarkupTagHelperDirectiveAttribute(MarkupTagHelperDirec var attributeName = node.FullName; var attributeValueNode = node.Value; - var associatedDescriptors = descriptors.Where(descriptor => - descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor))); + using var matches = new PooledArrayBuilder(); + TagHelperMatchingConventions.GetAttributeMatches(descriptors, attributeName, ref matches.AsRef()); - if (associatedDescriptors.Any() && _renderedBoundAttributeNames.Add(attributeName)) + if (matches.Any() && _renderedBoundAttributeNames.Add(attributeName)) { - foreach (var associatedDescriptor in associatedDescriptors) - { - if (TagHelperMatchingConventions.TryGetFirstBoundAttributeMatch( - associatedDescriptor, - attributeName, - out var associatedAttributeDescriptor, - out var indexerMatch, - 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("@", StringComparison.Ordinal) ? attributeName.Substring(1) : attributeName; + var directiveAttributeName = new DirectiveAttributeName(attributeName); - IntermediateNode attributeNode; - if (parameterMatch && - TagHelperMatchingConventions.TryGetBoundAttributeParameter(actualAttributeName, out var attributeNameWithoutParameter)) + foreach (var match in matches) + { + IntermediateNode attributeNode = match.IsParameterMatch && directiveAttributeName.HasParameter + ? new TagHelperDirectiveAttributeParameterIntermediateNode(match) { - attributeNode = new TagHelperDirectiveAttributeParameterIntermediateNode() - { - AttributeName = actualAttributeName, - AttributeNameWithoutParameter = attributeNameWithoutParameter.ToString(), - OriginalAttributeName = attributeName, - BoundAttributeParameter = associatedAttributeParameterDescriptor, - BoundAttribute = associatedAttributeDescriptor, - TagHelper = associatedDescriptor, - IsIndexerNameMatch = indexerMatch, - AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, - Source = BuildSourceSpanFromNode(attributeValueNode), - OriginalAttributeSpan = BuildSourceSpanFromNode(node.Name) - }; + AttributeName = directiveAttributeName.Text, + AttributeNameWithoutParameter = directiveAttributeName.TextWithoutParameter, + OriginalAttributeName = attributeName, + AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, + Source = BuildSourceSpanFromNode(attributeValueNode), + OriginalAttributeSpan = BuildSourceSpanFromNode(node.Name) } - else + : new TagHelperDirectiveAttributeIntermediateNode(match) { - attributeNode = new TagHelperDirectiveAttributeIntermediateNode() - { - AttributeName = actualAttributeName, - OriginalAttributeName = attributeName, - BoundAttribute = associatedAttributeDescriptor, - TagHelper = associatedDescriptor, - AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, - Source = BuildSourceSpanFromNode(attributeValueNode), - IsIndexerNameMatch = indexerMatch, - OriginalAttributeSpan = BuildSourceSpanFromNode(node.Name) - }; - } + AttributeName = directiveAttributeName.Text, + OriginalAttributeName = attributeName, + AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure, + Source = BuildSourceSpanFromNode(attributeValueNode), + OriginalAttributeSpan = BuildSourceSpanFromNode(node.Name) + }; - _builder.Push(attributeNode); - VisitAttributeValue(attributeValueNode); - _builder.Pop(); - } + _builder.Push(attributeNode); + VisitAttributeValue(attributeValueNode); + _builder.Pop(); } } else @@ -2225,6 +2112,22 @@ private void Combine(HtmlContentIntermediateNode node, SyntaxNode item) } } + private ref struct DirectiveAttributeName(string original) + { + // Directive attributes should start with '@' unless the descriptors are misconfigured. + // In that case, we would have already logged an error. + public readonly ReadOnlySpan Span = original.StartsWith('@') ? original.AsSpan()[1..] : original; + + public string Text => field ??= (Span.Length < original.Length ? Span.ToString() : original); + + private bool? _hasParameter; + + public bool HasParameter => _hasParameter ??= Span.IndexOf(':') >= 0; + + public string TextWithoutParameter + => field ??= Span.IndexOf(':') is int index && index >= 0 ? Span[..index].ToString() : Text; + } + private class ComponentImportFileKindVisitor : LoweringVisitor { public ComponentImportFileKindVisitor( diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperPropertyIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperPropertyIntermediateNode.cs index 824262a23a4..1766a8ebcfc 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperPropertyIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperPropertyIntermediateNode.cs @@ -27,7 +27,6 @@ public DefaultTagHelperPropertyIntermediateNode(TagHelperPropertyIntermediateNod BoundAttribute = propertyNode.BoundAttribute; IsIndexerNameMatch = propertyNode.IsIndexerNameMatch; Source = propertyNode.Source; - TagHelper = propertyNode.TagHelper; for (var i = 0; i < propertyNode.Children.Count; i++) { @@ -51,7 +50,7 @@ public DefaultTagHelperPropertyIntermediateNode(TagHelperPropertyIntermediateNod public string PropertyName { get; set; } - public TagHelperDescriptor TagHelper { get; set; } + public TagHelperDescriptor TagHelper => BoundAttribute.Parent; public override void Accept(IntermediateNodeVisitor visitor) { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/PreallocatedTagHelperPropertyIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/PreallocatedTagHelperPropertyIntermediateNode.cs index 5bb8905eb16..53e44525d26 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/PreallocatedTagHelperPropertyIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/PreallocatedTagHelperPropertyIntermediateNode.cs @@ -29,7 +29,6 @@ public PreallocatedTagHelperPropertyIntermediateNode(DefaultTagHelperPropertyInt IsIndexerNameMatch = propertyNode.IsIndexerNameMatch; PropertyName = propertyNode.PropertyName; Source = propertyNode.Source; - TagHelper = propertyNode.TagHelper; } public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly; @@ -46,7 +45,7 @@ public PreallocatedTagHelperPropertyIntermediateNode(DefaultTagHelperPropertyInt public string PropertyName { get; set; } - public TagHelperDescriptor TagHelper { get; set; } + public TagHelperDescriptor TagHelper => BoundAttribute.Parent; public string VariableName { get; set; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ComponentAttributeIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ComponentAttributeIntermediateNode.cs index 39dd3e815a2..4cfd89ac090 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ComponentAttributeIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ComponentAttributeIntermediateNode.cs @@ -83,7 +83,6 @@ public ComponentAttributeIntermediateNode(TagHelperPropertyIntermediateNode prop OriginalAttributeSpan = propertyNode.OriginalAttributeSpan; PropertyName = propertyNode.BoundAttribute.PropertyName; Source = propertyNode.Source; - TagHelper = propertyNode.TagHelper; TypeName = propertyNode.BoundAttribute.IsWeaklyTyped ? null : propertyNode.BoundAttribute.TypeName; for (var i = 0; i < propertyNode.Children.Count; i++) @@ -107,7 +106,6 @@ public ComponentAttributeIntermediateNode(TagHelperDirectiveAttributeIntermediat OriginalAttributeSpan = directiveAttributeNode.OriginalAttributeSpan; PropertyName = directiveAttributeNode.BoundAttribute.PropertyName; Source = directiveAttributeNode.Source; - TagHelper = directiveAttributeNode.TagHelper; TypeName = directiveAttributeNode.BoundAttribute.IsWeaklyTyped ? null : directiveAttributeNode.BoundAttribute.TypeName; for (var i = 0; i < directiveAttributeNode.Children.Count; i++) @@ -131,7 +129,6 @@ public ComponentAttributeIntermediateNode(TagHelperDirectiveAttributeParameterIn OriginalAttributeSpan = directiveAttributeParameterNode.OriginalAttributeSpan; PropertyName = directiveAttributeParameterNode.BoundAttributeParameter.PropertyName; Source = directiveAttributeParameterNode.Source; - TagHelper = directiveAttributeParameterNode.TagHelper; TypeName = directiveAttributeParameterNode.BoundAttributeParameter.TypeName; for (var i = 0; i < directiveAttributeParameterNode.Children.Count; i++) @@ -152,7 +149,7 @@ public ComponentAttributeIntermediateNode(TagHelperDirectiveAttributeParameterIn public string PropertyName { get; set; } - public TagHelperDescriptor TagHelper { get; set; } + public TagHelperDescriptor TagHelper => BoundAttribute?.Parent; public string TypeName { get; set; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ComponentTypeArgumentIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ComponentTypeArgumentIntermediateNode.cs index 8b6160e3d11..36af93749ed 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ComponentTypeArgumentIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ComponentTypeArgumentIntermediateNode.cs @@ -1,68 +1,43 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System; -using System.Diagnostics; - namespace Microsoft.AspNetCore.Razor.Language.Intermediate; -public sealed class ComponentTypeArgumentIntermediateNode : IntermediateNode +public sealed class ComponentTypeArgumentIntermediateNode( + BoundAttributeDescriptor boundAttribute, CSharpIntermediateToken value) : IntermediateNode { - public ComponentTypeArgumentIntermediateNode(TagHelperPropertyIntermediateNode propertyNode) - { - if (propertyNode == null) - { - throw new ArgumentNullException(nameof(propertyNode)); - } - - BoundAttribute = propertyNode.BoundAttribute; - Source = propertyNode.Source; - TagHelper = propertyNode.TagHelper; - - Debug.Assert(propertyNode.Children.Count == 1); - Value = propertyNode.Children[0] switch - { - CSharpIntermediateToken t => t, - CSharpExpressionIntermediateNode c => (CSharpIntermediateToken)c.Children[0], // TODO: can we break this in error cases? - _ => Assumed.Unreachable() - }; - Children = [Value]; - - AddDiagnosticsFromNode(propertyNode); - } - - public override IntermediateNodeCollection Children { get; } - - public BoundAttributeDescriptor BoundAttribute { get; set; } + public BoundAttributeDescriptor BoundAttribute { get; } = boundAttribute; + public TagHelperDescriptor TagHelper => BoundAttribute.Parent; public string TypeParameterName => BoundAttribute.Name; - public TagHelperDescriptor TagHelper { get; set; } + public CSharpIntermediateToken Value { get; } = value; - public CSharpIntermediateToken Value { get; set; } + public override IntermediateNodeCollection Children { get; } = [value]; - public override void Accept(IntermediateNodeVisitor visitor) + public ComponentTypeArgumentIntermediateNode(TagHelperPropertyIntermediateNode node) + : this(node.BoundAttribute, GetValue(node)) { - if (visitor == null) + Source = node.Source; + AddDiagnosticsFromNode(node); + } + + private static CSharpIntermediateToken GetValue(TagHelperPropertyIntermediateNode node) + => node.Children switch { - throw new ArgumentNullException(nameof(visitor)); - } + [CSharpIntermediateToken t] => t, + [CSharpExpressionIntermediateNode { Children: [CSharpIntermediateToken t] }] => t, + _ => Assumed.Unreachable() + }; - visitor.VisitComponentTypeArgument(this); - } + public override void Accept(IntermediateNodeVisitor visitor) + => visitor.VisitComponentTypeArgument(this); public override void FormatNode(IntermediateNodeFormatter formatter) { - if (formatter == null) - { - throw new ArgumentNullException(nameof(formatter)); - } - formatter.WriteContent(TypeParameterName); - formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute?.DisplayName); - formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName); + formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute.DisplayName); + formatter.WriteProperty(nameof(TagHelper), TagHelper.DisplayName); } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperDirectiveAttributeIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperDirectiveAttributeIntermediateNode.cs index 78c28d7bdda..f7325d0acbe 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperDirectiveAttributeIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperDirectiveAttributeIntermediateNode.cs @@ -1,40 +1,37 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System; +using System.Diagnostics; 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; } + private readonly TagHelperAttributeMatch _match; - public SourceSpan? OriginalAttributeSpan { get; set; } + public required string AttributeName { get; init; } + public required string OriginalAttributeName { get; init; } - public AttributeStructure AttributeStructure { get; set; } + public required AttributeStructure AttributeStructure { get; init; } + public SourceSpan? OriginalAttributeSpan { get; init; } - public BoundAttributeDescriptor BoundAttribute { get; set; } + public bool IsIndexerNameMatch => _match.IsIndexerMatch; - public TagHelperDescriptor TagHelper { get; set; } + public BoundAttributeDescriptor BoundAttribute => _match.Attribute; + public TagHelperDescriptor TagHelper => BoundAttribute.Parent; - public bool IsIndexerNameMatch { get; set; } + public override IntermediateNodeCollection Children { get => field ??= []; } - public override void Accept(IntermediateNodeVisitor visitor) + internal TagHelperDirectiveAttributeIntermediateNode(TagHelperAttributeMatch match) { - if (visitor == null) - { - throw new ArgumentNullException(nameof(visitor)); - } + Debug.Assert(!match.IsParameterMatch); - visitor.VisitTagHelperDirectiveAttribute(this); + _match = match; } + public override void Accept(IntermediateNodeVisitor visitor) + => visitor.VisitTagHelperDirectiveAttribute(this); + public override void FormatNode(IntermediateNodeFormatter formatter) { formatter.WriteContent(AttributeName); @@ -42,8 +39,8 @@ public override void FormatNode(IntermediateNodeFormatter formatter) 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(BoundAttribute), BoundAttribute.DisplayName); formatter.WriteProperty(nameof(IsIndexerNameMatch), IsIndexerNameMatch.ToString()); - formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName); + formatter.WriteProperty(nameof(TagHelper), TagHelper.DisplayName); } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperDirectiveAttributeParameterIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperDirectiveAttributeParameterIntermediateNode.cs index 3dee9d71539..cd7496f84da 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperDirectiveAttributeParameterIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperDirectiveAttributeParameterIntermediateNode.cs @@ -1,44 +1,36 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System; - namespace Microsoft.AspNetCore.Razor.Language.Intermediate; public sealed class TagHelperDirectiveAttributeParameterIntermediateNode : IntermediateNode { - public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection(); - - public string AttributeName { get; set; } - - public string AttributeNameWithoutParameter { get; set; } - - public string OriginalAttributeName { get; set; } + private readonly TagHelperAttributeMatch _match; - public AttributeStructure AttributeStructure { get; set; } + public required string AttributeName { get; init; } + public required string AttributeNameWithoutParameter { get; init; } - public BoundAttributeParameterDescriptor BoundAttributeParameter { get; set; } + public required string OriginalAttributeName { get; init; } + public SourceSpan? OriginalAttributeSpan { get; init; } - public BoundAttributeDescriptor BoundAttribute { get; set; } + public required AttributeStructure AttributeStructure { get; init; } - public TagHelperDescriptor TagHelper { get; set; } + public bool IsIndexerNameMatch => _match.IsIndexerMatch; - public bool IsIndexerNameMatch { get; set; } + public BoundAttributeParameterDescriptor BoundAttributeParameter => _match.Parameter.AssumeNotNull(); + public BoundAttributeDescriptor BoundAttribute => BoundAttributeParameter.Parent; + public TagHelperDescriptor TagHelper => BoundAttribute.Parent; - public SourceSpan? OriginalAttributeSpan { get; set; } + public override IntermediateNodeCollection Children { get => field ??= []; } - public override void Accept(IntermediateNodeVisitor visitor) + internal TagHelperDirectiveAttributeParameterIntermediateNode(TagHelperAttributeMatch match) { - if (visitor == null) - { - throw new ArgumentNullException(nameof(visitor)); - } - - visitor.VisitTagHelperDirectiveAttributeParameter(this); + _match = match; } + public override void Accept(IntermediateNodeVisitor visitor) + => visitor.VisitTagHelperDirectiveAttributeParameter(this); + public override void FormatNode(IntermediateNodeFormatter formatter) { formatter.WriteContent(AttributeName); @@ -46,8 +38,8 @@ public override void FormatNode(IntermediateNodeFormatter formatter) 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(TagHelper), TagHelper?.DisplayName); + formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute.DisplayName); + formatter.WriteProperty(nameof(BoundAttributeParameter), BoundAttributeParameter.DisplayName); + formatter.WriteProperty(nameof(TagHelper), TagHelper.DisplayName); } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperHtmlAttributeIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperHtmlAttributeIntermediateNode.cs index cc6105f4191..0b72b22c21e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperHtmlAttributeIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperHtmlAttributeIntermediateNode.cs @@ -1,29 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System; - namespace Microsoft.AspNetCore.Razor.Language.Intermediate; public sealed class TagHelperHtmlAttributeIntermediateNode : IntermediateNode { - public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection(); - - public string AttributeName { get; set; } + public required string AttributeName { get; init; } + public required AttributeStructure AttributeStructure { get; init; } - public AttributeStructure AttributeStructure { get; set; } + public override IntermediateNodeCollection Children { get => field ??= []; } public override void Accept(IntermediateNodeVisitor visitor) - { - if (visitor == null) - { - throw new ArgumentNullException(nameof(visitor)); - } - - visitor.VisitTagHelperHtmlAttribute(this); - } + => visitor.VisitTagHelperHtmlAttribute(this); public override void FormatNode(IntermediateNodeFormatter formatter) { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperIntermediateNode.cs index 42ca1805bca..f957e6a0869 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperIntermediateNode.cs @@ -1,51 +1,22 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System; -using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; namespace Microsoft.AspNetCore.Razor.Language.Intermediate; public sealed class TagHelperIntermediateNode : IntermediateNode { - public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection(); - - public TagMode TagMode { get; set; } - - public string TagName { get; set; } + public required TagMode TagMode { get; init; } + public required string TagName { get; init; } - public IList TagHelpers { get; } = new List(); - - public TagHelperBodyIntermediateNode Body => Children.OfType().SingleOrDefault(); - - public IEnumerable Properties - { - get - { - return Children.OfType(); - } - } + public ImmutableArray TagHelpers { get; init => field = value.NullToEmpty(); } = []; - public IEnumerable HtmlAttributes - { - get - { - return Children.OfType(); - } - } + public override IntermediateNodeCollection Children { get => field ??= []; } public override void Accept(IntermediateNodeVisitor visitor) - { - if (visitor == null) - { - throw new ArgumentNullException(nameof(visitor)); - } - - visitor.VisitTagHelper(this); - } + => visitor.VisitTagHelper(this); public override void FormatNode(IntermediateNodeFormatter formatter) { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperPropertyIntermediateNode.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperPropertyIntermediateNode.cs index b2f740ef678..b5a23aa1b0d 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperPropertyIntermediateNode.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperPropertyIntermediateNode.cs @@ -1,46 +1,40 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - -using System; - namespace Microsoft.AspNetCore.Razor.Language.Intermediate; public sealed class TagHelperPropertyIntermediateNode : IntermediateNode { - public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection(); + private readonly TagHelperAttributeMatch _match; - public string AttributeName { get; set; } + public required string AttributeName { get; init; } + public required AttributeStructure AttributeStructure { get; init; } - public AttributeStructure AttributeStructure { get; set; } + public SourceSpan? OriginalAttributeSpan { get; init; } - public BoundAttributeDescriptor BoundAttribute { get; set; } + public bool IsIndexerNameMatch => _match.IsIndexerMatch; - public TagHelperDescriptor TagHelper { get; set; } + public BoundAttributeDescriptor BoundAttribute => _match.Attribute; + public TagHelperDescriptor TagHelper => BoundAttribute.Parent; - public bool IsIndexerNameMatch { get; set; } + public override IntermediateNodeCollection Children { get => field ??= []; } - public SourceSpan? OriginalAttributeSpan { get; set; } - - public override void Accept(IntermediateNodeVisitor visitor) + internal TagHelperPropertyIntermediateNode(TagHelperAttributeMatch match) { - if (visitor == null) - { - throw new ArgumentNullException(nameof(visitor)); - } - - visitor.VisitTagHelperProperty(this); + _match = match; } + public override void Accept(IntermediateNodeVisitor visitor) + => visitor.VisitTagHelperProperty(this); + public override void FormatNode(IntermediateNodeFormatter formatter) { formatter.WriteContent(AttributeName); formatter.WriteProperty(nameof(AttributeName), AttributeName); formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString()); - formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute?.DisplayName); + formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute.DisplayName); formatter.WriteProperty(nameof(IsIndexerNameMatch), IsIndexerNameMatch.ToString()); - formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName); + formatter.WriteProperty(nameof(TagHelper), TagHelper.DisplayName); } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperBlockRewriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperBlockRewriter.cs index c731b873077..ecd1fb3de02 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperBlockRewriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Legacy/TagHelperBlockRewriter.cs @@ -438,16 +438,11 @@ private static string GetPropertyType(string name, IEnumerable _isIndexerMatch ??= TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(Attribute, Name.AsSpan()); + + [MemberNotNullWhen(true, nameof(Parameter))] + public readonly bool IsParameterMatch => Parameter is not null; + + public bool ExpectsStringValue + { + get + { + if (IsParameterMatch) + { + return Parameter.IsStringProperty; + } + + return Attribute.IsStringProperty || (IsIndexerMatch && Attribute.IsIndexerStringProperty); + } + } + + public bool ExpectsBooleanValue + { + get + { + if (IsParameterMatch) + { + return Parameter.IsBooleanProperty; + } + + return Attribute.IsBooleanProperty || (IsIndexerMatch && Attribute.IsIndexerBooleanProperty); + } + } +} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperMatchingConventions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperMatchingConventions.cs index ab907a0833e..3d6e23a4b48 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperMatchingConventions.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/TagHelperMatchingConventions.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using Microsoft.AspNetCore.Razor.PooledObjects; namespace Microsoft.AspNetCore.Razor.Language; @@ -175,34 +176,44 @@ private static bool TryGetBoundAttributeParameter(string fullAttributeName, out return true; } - public static bool TryGetFirstBoundAttributeMatch( - TagHelperDescriptor descriptor, + /// + /// Gets all attribute matches from the specified tag helpers for the given attribute name. + /// + /// The collection of tag helper descriptors to search through. + /// The attribute name to match against. + /// A pooled array builder that will be populated with matching attribute descriptors. + /// + /// This method iterates through all provided tag helpers and attempts to find bound attribute matches + /// for the specified attribute name. Each successful match is added to the provided matches collection. + /// + public static void GetAttributeMatches( + ImmutableArray tagHelpers, string name, - out BoundAttributeDescriptor? boundAttribute, - out bool indexerMatch, - out bool parameterMatch, - out BoundAttributeParameterDescriptor? boundAttributeParameter) + ref PooledArrayBuilder matches) { - indexerMatch = false; - parameterMatch = false; - boundAttribute = null; - boundAttributeParameter = null; + foreach (var tagHelper in tagHelpers) + { + if (TryGetFirstBoundAttributeMatch(tagHelper, name, out var match)) + { + matches.Add(match); + } + } + } + public static bool TryGetFirstBoundAttributeMatch(TagHelperDescriptor descriptor, string name, out TagHelperAttributeMatch match) + { if (descriptor == null || name.IsNullOrEmpty()) { + match = default; return false; } // First, check if we have a bound attribute descriptor that matches the parameter if it exists. foreach (var attribute in descriptor.BoundAttributes) { - boundAttributeParameter = GetSatisfyingBoundAttributeWithParameter(attribute, name); - - if (boundAttributeParameter != null) + if (GetSatisfyingBoundAttributeWithParameter(attribute, name) is { } parameter) { - boundAttribute = attribute; - indexerMatch = SatisfiesBoundAttributeIndexer(attribute, name.AsSpan()); - parameterMatch = true; + match = new(name, attribute, parameter); return true; } } @@ -213,13 +224,13 @@ public static bool TryGetFirstBoundAttributeMatch( { if (CanSatisfyBoundAttribute(name, attribute)) { - boundAttribute = attribute; - indexerMatch = SatisfiesBoundAttributeIndexer(attribute, name.AsSpan()); + match = new(name, attribute, parameter: null); return true; } } // No matches found. + match = default; return false; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/GenericTypeNameRewriterTest.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/GenericTypeNameRewriterTest.cs index 967296692d2..7679a3d66f0 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor/test/GenericTypeNameRewriterTest.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor/test/GenericTypeNameRewriterTest.cs @@ -69,9 +69,9 @@ public void GenericTypeNameRewriter_CanReplaceTypeParametersWithTypeArguments(st // Arrange var visitor = new GenericTypeNameRewriter(new Dictionary() { - { "TItem1", new ComponentTypeArgumentIntermediateNode(new() { Children = { IntermediateNodeFactory.CSharpToken("Type1") } })}, - { "TItem2", new ComponentTypeArgumentIntermediateNode(new() { Children = { IntermediateNodeFactory.CSharpToken("Type2") } })}, - { "TItem3", new ComponentTypeArgumentIntermediateNode(new() { Children = { IntermediateNodeFactory.CSharpToken(null!) } })}, + { "TItem1", Create("Type1") }, + { "TItem2", Create("Type2") }, + { "TItem3", Create(null) }, }); // Act @@ -79,5 +79,10 @@ public void GenericTypeNameRewriter_CanReplaceTypeParametersWithTypeArguments(st // Assert Assert.Equal(expected, actual.ToString()); + + static ComponentTypeArgumentIntermediateNode Create(string? typeName) + { + return new(boundAttribute: null!, IntermediateNodeFactory.CSharpToken(typeName!)); + } } } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/DirectiveAttributeCompletionItemProvider.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/DirectiveAttributeCompletionItemProvider.cs index ce867a17a2c..06b42fe17f2 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/DirectiveAttributeCompletionItemProvider.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/DirectiveAttributeCompletionItemProvider.cs @@ -128,7 +128,7 @@ internal static ImmutableArray GetAttributeCompletions( // Strip off the @ from the insertion text. This change is here to align the insertion text with the // completion hooks into VS and VSCode. Basically, completion triggers when `@` is typed so we don't // want to insert `@bind` because `@` already exists. - if (SpanExtensions.StartsWith(insertTextSpan, '@')) + if (insertTextSpan.StartsWith('@')) { insertTextSpan = insertTextSpan[1..]; } diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/SpanExtensions.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/SpanExtensions.cs index 4a484c646ed..152829d3fc6 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/SpanExtensions.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/SpanExtensions.cs @@ -5,17 +5,17 @@ using System.Runtime.InteropServices; #endif +#if !NET9_0_OR_GREATER using System.Runtime.CompilerServices; +#endif namespace System; internal static class SpanExtensions { +#if !NET8_0_OR_GREATER public static unsafe void Replace(this ReadOnlySpan source, Span destination, char oldValue, char newValue) { -#if NET8_0_OR_GREATER - source.Replace(destination, oldValue, newValue); -#else var length = source.Length; if (length == 0) { @@ -35,14 +35,10 @@ public static unsafe void Replace(this ReadOnlySpan source, Span des var original = Unsafe.Add(ref src, i); Unsafe.Add(ref dst, i) = original == oldValue ? newValue : original; } -#endif } public static unsafe void Replace(this Span span, char oldValue, char newValue) { -#if NET8_0_OR_GREATER - span.Replace(oldValue, newValue); -#else var length = span.Length; if (length == 0) { @@ -60,9 +56,10 @@ public static unsafe void Replace(this Span span, char oldValue, char newV slot = newValue; } } -#endif } +#endif +#if !NET9_0_OR_GREATER /// /// Determines whether the specified value appears at the start of the span. /// @@ -71,11 +68,7 @@ public static unsafe void Replace(this Span span, char oldValue, char newV [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool StartsWith(this ReadOnlySpan span, T value) where T : IEquatable? => -#if NET9_0_OR_GREATER - MemoryExtensions.StartsWith(span, value); -#else span.Length != 0 && (span[0]?.Equals(value) ?? (object?)value is null); -#endif /// /// Determines whether the specified value appears at the end of the span. @@ -85,9 +78,6 @@ public static bool StartsWith(this ReadOnlySpan span, T value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EndsWith(this ReadOnlySpan span, T value) where T : IEquatable? => -#if NET9_0_OR_GREATER - MemoryExtensions.EndsWith(span, value); -#else span.Length != 0 && (span[^1]?.Equals(value) ?? (object?)value is null); #endif }