diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs
index d6fb6742da..294304dd18 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs
@@ -297,6 +297,11 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
Context.NodeWriter.WriteReferenceCapture(Context, node);
}
+ public override void VisitSetKey(SetKeyIntermediateNode node)
+ {
+ Context.NodeWriter.WriteSetKey(Context, node);
+ }
+
public override void VisitDefault(IntermediateNode node)
{
Context.RenderChildren(node);
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/IntermediateNodeWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/IntermediateNodeWriter.cs
index c8a3c6659a..d4e49066f9 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/IntermediateNodeWriter.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/IntermediateNodeWriter.cs
@@ -64,6 +64,11 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
throw new NotSupportedException("This writer does not support components.");
}
+ public virtual void WriteSetKey(CodeRenderingContext context, SetKeyIntermediateNode node)
+ {
+ throw new NotSupportedException("This writer does not support components.");
+ }
+
public abstract void BeginWriterScope(CodeRenderingContext context, string writer);
public abstract void EndWriterScope(CodeRenderingContext context);
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.Designer.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.Designer.cs
index 5d4e39e752..10af5b8063 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.Designer.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.Designer.cs
@@ -168,6 +168,15 @@ namespace Microsoft.AspNetCore.Razor.Language {
}
}
+ ///
+ /// Looks up a localized string similar to Ensures that the component or element will be preserved across renders if (and only if) the supplied key value matches..
+ ///
+ internal static string KeyTagHelper_Documentation {
+ get {
+ return ResourceManager.GetString("KeyTagHelper_Documentation", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Declares a layout type for the current document..
///
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.resx b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.resx
index 4a27c934ae..48b636ec27 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.resx
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/ComponentResources.resx
@@ -153,6 +153,9 @@
TypeName
+
+ Ensures that the component or element will be preserved across renders if (and only if) the supplied key value matches.
+
Declares a layout type for the current document.
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDesignTimeNodeWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDesignTimeNodeWriter.cs
index f18235f463..9b38d73700 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDesignTimeNodeWriter.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDesignTimeNodeWriter.cs
@@ -314,6 +314,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
// builder.OpenComponent(0);
// builder.AddAttribute(1, "Foo", ...);
// builder.AddAttribute(2, "ChildContent", ...);
+ // builder.SetKey(someValue);
// builder.AddElementCapture(3, (__value) => _field = __value);
// builder.CloseComponent();
foreach (var typeArgument in node.TypeArguments)
@@ -347,6 +348,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
});
}
+ foreach (var setKey in node.SetKeys)
+ {
+ context.RenderNode(setKey);
+ }
+
foreach (var capture in node.Captures)
{
context.RenderNode(capture);
@@ -362,7 +368,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
var attributes = node.Attributes.ToList();
var childContents = node.ChildContents.ToList();
var captures = node.Captures.ToList();
- var remaining = attributes.Count + childContents.Count + captures.Count;
+ var setKeys = node.SetKeys.ToList();
+ var remaining = attributes.Count + childContents.Count + captures.Count + setKeys.Count;
context.CodeWriter.Write(node.TypeInferenceNode.FullTypeName);
context.CodeWriter.Write(".");
@@ -406,6 +413,20 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
}
}
+ for (var i = 0; i < setKeys.Count; i++)
+ {
+ context.CodeWriter.Write("-1");
+ context.CodeWriter.Write(", ");
+
+ WriteSetKeyInnards(context, setKeys[i]);
+
+ remaining--;
+ if (remaining > 0)
+ {
+ context.CodeWriter.Write(", ");
+ }
+ }
+
for (var i = 0; i < captures.Count; i++)
{
context.CodeWriter.Write("-1");
@@ -717,6 +738,42 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
_scopeStack.CloseScope(context);
}
+ public override void WriteSetKey(CodeRenderingContext context, SetKeyIntermediateNode node)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (node == null)
+ {
+ throw new ArgumentNullException(nameof(node));
+ }
+
+ // Looks like:
+ //
+ // builder.SetKey(_keyValue);
+
+ var codeWriter = context.CodeWriter;
+
+ codeWriter
+ .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{ComponentsApi.RenderTreeBuilder.SetKey}");
+ WriteSetKeyInnards(context, node);
+ codeWriter.WriteEndMethodInvocation();
+ }
+
+ private void WriteSetKeyInnards(CodeRenderingContext context, SetKeyIntermediateNode node)
+ {
+ WriteCSharpCode(context, new CSharpCodeIntermediateNode
+ {
+ Source = node.Source,
+ Children =
+ {
+ node.KeyValueToken
+ }
+ });
+ }
+
public override void WriteReferenceCapture(CodeRenderingContext context, ReferenceCaptureIntermediateNode node)
{
if (context == null)
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentKeyLoweringPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentKeyLoweringPass.cs
new file mode 100644
index 0000000000..7dad9740d5
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentKeyLoweringPass.cs
@@ -0,0 +1,76 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Razor.Language.Intermediate;
+
+namespace Microsoft.AspNetCore.Razor.Language.Components
+{
+ internal class ComponentKeyLoweringPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
+ {
+ // Run after component lowering pass
+ public override int Order => 50;
+
+ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
+ {
+ if (!IsComponentDocument(documentNode))
+ {
+ return;
+ }
+
+ var @namespace = documentNode.FindPrimaryNamespace();
+ var @class = documentNode.FindPrimaryClass();
+ if (@namespace == null || @class == null)
+ {
+ // Nothing to do, bail. We can't function without the standard structure.
+ return;
+ }
+
+ var references = documentNode.FindDescendantReferences();
+ for (var i = 0; i < references.Count; i++)
+ {
+ var reference = references[i];
+ var node = (TagHelperPropertyIntermediateNode)reference.Node;
+
+ if (node.TagHelper.IsKeyTagHelper())
+ {
+ reference.Replace(RewriteUsage(reference.Parent, node));
+ }
+ }
+ }
+
+ private IntermediateNode RewriteUsage(IntermediateNode parent, TagHelperPropertyIntermediateNode node)
+ {
+ // If we can't get a nonempty attribute value, do nothing because there will
+ // already be a diagnostic for empty values
+ var keyValueToken = DetermineKeyValueToken(node);
+ if (keyValueToken == null)
+ {
+ return node;
+ }
+
+ return new SetKeyIntermediateNode(keyValueToken);
+ }
+
+ private IntermediateToken DetermineKeyValueToken(TagHelperPropertyIntermediateNode attributeNode)
+ {
+ IntermediateToken foundToken = null;
+
+ if (attributeNode.Children.Count == 1)
+ {
+ if (attributeNode.Children[0] is IntermediateToken token)
+ {
+ foundToken = token;
+ }
+ else if (attributeNode.Children[0] is CSharpExpressionIntermediateNode csharpNode)
+ {
+ if (csharpNode.Children.Count == 1)
+ {
+ foundToken = csharpNode.Children[0] as IntermediateToken;
+ }
+ }
+ }
+
+ return !string.IsNullOrWhiteSpace(foundToken?.Content) ? foundToken : null;
+ }
+ }
+}
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentMetadata.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentMetadata.cs
index 17966db799..f57daad348 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentMetadata.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentMetadata.cs
@@ -23,6 +23,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
tagHelperKind == ChildContent.TagHelperKind ||
tagHelperKind == EventHandler.TagHelperKind ||
tagHelperKind == Bind.TagHelperKind ||
+ tagHelperKind == Key.TagHelperKind ||
tagHelperKind == Ref.TagHelperKind;
}
@@ -116,6 +117,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
public readonly static string TagHelperKind = "Components.EventHandler";
}
+ public static class Key
+ {
+ public readonly static string TagHelperKind = "Components.Key";
+
+ public static readonly string RuntimeName = "Components.None";
+ }
+
public static class Ref
{
public readonly static string TagHelperKind = "Components.Ref";
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentNodeWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentNodeWriter.cs
index 0629f7dc69..ca8704fef0 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentNodeWriter.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentNodeWriter.cs
@@ -160,6 +160,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
index++;
}
+ foreach (var setKey in node.Component.SetKeys)
+ {
+ context.CodeWriter.WriteStartInstanceMethodInvocation("builder", ComponentsApi.RenderTreeBuilder.SetKey);
+ context.CodeWriter.Write(parameters[index].parameterName);
+ context.CodeWriter.WriteEndMethodInvocation();
+
+ index++;
+ }
+
foreach (var capture in node.Component.Captures)
{
context.CodeWriter.WriteStartInstanceMethodInvocation("builder", capture.IsComponentCapture ? ComponentsApi.RenderTreeBuilder.AddComponentReferenceCapture : ComponentsApi.RenderTreeBuilder.AddElementReferenceCapture);
@@ -200,6 +209,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
p.Add(($"__seq{p.Count}", typeName, $"__arg{p.Count}"));
}
+ foreach (var capture in node.Component.SetKeys)
+ {
+ p.Add(($"__seq{p.Count}", "object", $"__arg{p.Count}"));
+ }
+
foreach (var capture in node.Component.Captures)
{
// The capture type name should already contain the global:: prefix.
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentRuntimeNodeWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentRuntimeNodeWriter.cs
index 83d1c2941f..a3224213a9 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentRuntimeNodeWriter.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentRuntimeNodeWriter.cs
@@ -190,6 +190,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
context.RenderNode(attribute);
}
+ foreach (var setKey in node.SetKeys)
+ {
+ context.RenderNode(setKey);
+ }
+
foreach (var capture in node.Captures)
{
context.RenderNode(capture);
@@ -317,6 +322,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
// builder.OpenComponent(0);
// builder.AddAttribute(1, "Foo", ...);
// builder.AddAttribute(2, "ChildContent", ...);
+ // builder.SetKey(someValue);
// builder.AddElementCapture(3, (__value) => _field = __value);
// builder.CloseComponent();
@@ -344,6 +350,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
context.RenderNode(childContent);
}
+ foreach (var setKey in node.SetKeys)
+ {
+ context.RenderNode(setKey);
+ }
+
foreach (var capture in node.Captures)
{
context.RenderNode(capture);
@@ -366,7 +377,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
var attributes = node.Attributes.ToList();
var childContents = node.ChildContents.ToList();
var captures = node.Captures.ToList();
- var remaining = attributes.Count + childContents.Count + captures.Count;
+ var setKeys = node.SetKeys.ToList();
+ var remaining = attributes.Count + childContents.Count + captures.Count + setKeys.Count;
context.CodeWriter.Write(node.TypeInferenceNode.FullTypeName);
context.CodeWriter.Write(".");
@@ -410,6 +422,20 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
}
}
+ for (var i = 0; i < setKeys.Count; i++)
+ {
+ context.CodeWriter.Write((_sourceSequence++).ToString());
+ context.CodeWriter.Write(", ");
+
+ WriteSetKeyInnards(context, setKeys[i]);
+
+ remaining--;
+ if (remaining > 0)
+ {
+ context.CodeWriter.Write(", ");
+ }
+ }
+
for (var i = 0; i < captures.Count; i++)
{
context.CodeWriter.Write((_sourceSequence++).ToString());
@@ -651,6 +677,32 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
_scopeStack.CloseScope(context);
}
+ public override void WriteSetKey(CodeRenderingContext context, SetKeyIntermediateNode node)
+ {
+ // Looks like:
+ //
+ // builder.SetKey(_keyValue);
+
+ var codeWriter = context.CodeWriter;
+
+ codeWriter
+ .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{ComponentsApi.RenderTreeBuilder.SetKey}");
+ WriteSetKeyInnards(context, node);
+ codeWriter.WriteEndMethodInvocation();
+ }
+
+ private void WriteSetKeyInnards(CodeRenderingContext context, SetKeyIntermediateNode node)
+ {
+ WriteCSharpCode(context, new CSharpCodeIntermediateNode
+ {
+ Source = node.Source,
+ Children =
+ {
+ node.KeyValueToken
+ }
+ });
+ }
+
public override void WriteReferenceCapture(CodeRenderingContext context, ReferenceCaptureIntermediateNode node)
{
// Looks like:
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentsApi.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentsApi.cs
index 59db7873dd..858dd7169b 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentsApi.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentsApi.cs
@@ -87,6 +87,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
public static readonly string GetFrames = nameof(GetFrames);
public static readonly string ChildContent = nameof(ChildContent);
+
+ public static readonly string SetKey = nameof(SetKey);
}
public static class RuntimeHelpers
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/TagHelperDescriptorExtensions.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/TagHelperDescriptorExtensions.cs
index 1bbd8d8ace..8d16b94f9d 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/TagHelperDescriptorExtensions.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/TagHelperDescriptorExtensions.cs
@@ -139,6 +139,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
string.Equals(ComponentMetadata.EventHandler.TagHelperKind, kind);
}
+ public static bool IsKeyTagHelper(this TagHelperDescriptor tagHelper)
+ {
+ if (tagHelper == null)
+ {
+ throw new ArgumentNullException(nameof(tagHelper));
+ }
+
+ return
+ tagHelper.Metadata.TryGetValue(ComponentMetadata.SpecialKindKey, out var kind) &&
+ string.Equals(ComponentMetadata.Key.TagHelperKind, kind);
+ }
+
public static bool IsRefTagHelper(this TagHelperDescriptor tagHelper)
{
if (tagHelper == null)
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/ComponentIntermediateNode.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/ComponentIntermediateNode.cs
index f3cb7feea0..7b623a0fe9 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/ComponentIntermediateNode.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/ComponentIntermediateNode.cs
@@ -16,6 +16,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
public IEnumerable Captures => Children.OfType();
+ public IEnumerable SetKeys => Children.OfType();
+
public IEnumerable ChildContents => Children.OfType();
public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection();
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/IntermediateNodeVisitor.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/IntermediateNodeVisitor.cs
index af4c6c41af..134294394d 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/IntermediateNodeVisitor.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/IntermediateNodeVisitor.cs
@@ -168,5 +168,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
VisitDefault(node);
}
+
+ public virtual void VisitSetKey(SetKeyIntermediateNode node)
+ {
+ VisitDefault(node);
+ }
}
}
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/MarkupElementIntermediateNode.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/MarkupElementIntermediateNode.cs
index 7ea2b95882..bf70855bc3 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/MarkupElementIntermediateNode.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/MarkupElementIntermediateNode.cs
@@ -14,10 +14,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
public IEnumerable Captures => Children.OfType();
+ public IEnumerable SetKeys => Children.OfType();
+
public IEnumerable Body => Children.Where(c =>
{
return
c as HtmlAttributeIntermediateNode == null &&
+ c as SetKeyIntermediateNode == null &&
c as ReferenceCaptureIntermediateNode == null;
});
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/SetKeyIntermediateNode.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/SetKeyIntermediateNode.cs
new file mode 100644
index 0000000000..507f89c863
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Intermediate/SetKeyIntermediateNode.cs
@@ -0,0 +1,42 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.AspNetCore.Razor.Language.Intermediate
+{
+ public sealed class SetKeyIntermediateNode : IntermediateNode
+ {
+ public SetKeyIntermediateNode(IntermediateToken keyValueToken)
+ {
+ KeyValueToken = keyValueToken ?? throw new ArgumentNullException(nameof(keyValueToken));
+ Source = KeyValueToken.Source;
+ }
+
+ public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly;
+
+ public IntermediateToken KeyValueToken { get; }
+
+ public override void Accept(IntermediateNodeVisitor visitor)
+ {
+ if (visitor == null)
+ {
+ throw new ArgumentNullException(nameof(visitor));
+ }
+
+ visitor.VisitSetKey(this);
+ }
+
+ public override void FormatNode(IntermediateNodeFormatter formatter)
+ {
+ if (formatter == null)
+ {
+ throw new ArgumentNullException(nameof(formatter));
+ }
+
+ formatter.WriteContent(KeyValueToken.Content);
+
+ formatter.WriteProperty(nameof(KeyValueToken), KeyValueToken.Content);
+ }
+ }
+}
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectEngine.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectEngine.cs
index 82ca3509a8..cdc6b9ff4a 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectEngine.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectEngine.cs
@@ -226,6 +226,7 @@ namespace Microsoft.AspNetCore.Razor.Language
builder.Features.Add(new ComponentLoweringPass());
builder.Features.Add(new ComponentScriptTagPass());
builder.Features.Add(new ComponentEventHandlerLoweringPass());
+ builder.Features.Add(new ComponentKeyLoweringPass());
builder.Features.Add(new ComponentReferenceCaptureLoweringPass());
builder.Features.Add(new ComponentBindLoweringPass());
builder.Features.Add(new ComponentTemplateDiagnosticPass());
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs
index 7f76f8cdd4..7cbc74266f 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs
@@ -2724,6 +2724,68 @@ namespace Test
CompileToAssembly(generated);
}
+ [Fact]
+ public void GenericComponent_WithKey()
+ {
+ // Arrange
+ AdditionalSyntaxTrees.Add(Parse(@"
+using Microsoft.AspNetCore.Components;
+
+namespace Test
+{
+ public class MyComponent : ComponentBase
+ {
+ [Parameter] TItem Item { get; set; }
+ }
+}
+"));
+
+ // Act
+ var generated = CompileToCSharp(@"
+
+
+@functions {
+ private object _someKey = new object();
+}
+");
+
+ // Assert
+ AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
+ AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
+ CompileToAssembly(generated);
+ }
+
+ [Fact]
+ public void GenericComponent_WithKey_TypeInference()
+ {
+ // Arrange
+ AdditionalSyntaxTrees.Add(Parse(@"
+using Microsoft.AspNetCore.Components;
+
+namespace Test
+{
+ public class MyComponent : ComponentBase
+ {
+ [Parameter] TItem Item { get; set; }
+ }
+}
+"));
+
+ // Act
+ var generated = CompileToCSharp(@"
+
+
+@functions {
+ private object _someKey = new object();
+}
+");
+
+ // Assert
+ AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
+ AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
+ CompileToAssembly(generated);
+ }
+
[Fact]
public void GenericComponent_WithComponentRef()
{
@@ -2831,6 +2893,106 @@ namespace Test.Shared
#endregion
+ #region Key
+
+ [Fact]
+ public void Element_WithKey()
+ {
+ // Arrange/Act
+ var generated = CompileToCSharp(@"
+Hello
+
+@functions {
+ private object someObject = new object();
+}
+");
+
+ // Assert
+ AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
+ AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
+ CompileToAssembly(generated);
+ }
+
+ [Fact]
+ public void Element_WithKey_AndOtherAttributes()
+ {
+ // Arrange/Act
+ var generated = CompileToCSharp(@"
+
+
+@functions {
+ private object someObject = new object();
+
+ [Parameter] protected int Min { get; set; }
+ }
+");
+
+ // Assert
+ AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
+ AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
+ CompileToAssembly(generated);
+ }
+
+ [Fact]
+ public void Component_WithKey()
+ {
+ // Arrange
+ AdditionalSyntaxTrees.Add(Parse(@"
+using Microsoft.AspNetCore.Components;
+
+namespace Test
+{
+ public class MyComponent : ComponentBase
+ {
+ }
+}
+"));
+
+ // Arrange/Act
+ var generated = CompileToCSharp(@"
+
+
+@functions {
+ private DateTime someDate = DateTime.Now;
+}
+");
+
+ // Assert
+ AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
+ AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
+ CompileToAssembly(generated);
+ }
+
+ [Fact]
+ public void Component_WithKey_WithChildContent()
+ {
+ // Arrange
+ AdditionalSyntaxTrees.Add(Parse(@"
+using Microsoft.AspNetCore.Components;
+
+namespace Test
+{
+ public class MyComponent : ComponentBase
+ {
+ }
+}
+"));
+
+ // Arrange/Act
+ var generated = CompileToCSharp(@"
+
+ Some further content
+
+");
+
+ // Assert
+ AssertDocumentNodeMatchesBaseline(generated.CodeDocument);
+ AssertCSharpDocumentMatchesBaseline(generated.CodeDocument);
+ CompileToAssembly(generated);
+ }
+
+ #endregion
+
#region Ref
[Fact]
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/RazorProjectEngineTest.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/RazorProjectEngineTest.cs
index 65600a9b88..9a5673a70b 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/RazorProjectEngineTest.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/RazorProjectEngineTest.cs
@@ -52,6 +52,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Test
feature => Assert.IsType(feature),
feature => Assert.IsType(feature),
feature => Assert.IsType(feature),
+ feature => Assert.IsType(feature),
feature => Assert.IsType(feature),
feature => Assert.IsType(feature),
feature => Assert.IsType(feature),
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey/TestComponent.codegen.cs
new file mode 100644
index 0000000000..b3049fa6f9
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey/TestComponent.codegen.cs
@@ -0,0 +1,56 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 219
+ private void __RazorDirectiveTokenHelpers__() {
+ }
+ #pragma warning restore 219
+ #pragma warning disable 0414
+ private static System.Object __o = null;
+ #pragma warning restore 0414
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ __o = "";
+ __o = "";
+ builder.AddAttribute(-1, "ChildContent", (Microsoft.AspNetCore.Components.RenderFragment)((builder2) => {
+ }
+ ));
+ builder.SetKey(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ someDate.Day
+
+#line default
+#line hidden
+#nullable disable
+ );
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+__o = typeof(MyComponent);
+
+#line default
+#line hidden
+#nullable disable
+ }
+ #pragma warning restore 1998
+#nullable restore
+#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
+
+ private DateTime someDate = DateTime.Now;
+
+#line default
+#line hidden
+#nullable disable
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey/TestComponent.ir.txt
new file mode 100644
index 0000000000..6b4cf2695b
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey/TestComponent.ir.txt
@@ -0,0 +1,30 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [12] ) - System
+ UsingDirective - (18:2,1 [32] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [17] ) - System.Linq
+ UsingDirective - (73:4,1 [28] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [37] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ DesignTimeDirective -
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning disable 0414
+ CSharpCode -
+ IntermediateToken - - CSharp - private static System.Object __o = null;
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning restore 0414
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ Component - (0:0,0 [74] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
+ ComponentAttribute - - ParamBefore - AttributeStructure.DoubleQuotes
+ HtmlContent - (26:0,26 [6] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (26:0,26 [6] x:\dir\subdir\Test\TestComponent.cshtml) - Html - before
+ SetKey - (39:0,39 [12] x:\dir\subdir\Test\TestComponent.cshtml) - someDate.Day
+ ComponentAttribute - - ParamAfter - AttributeStructure.DoubleQuotes
+ HtmlContent - (65:0,65 [5] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (65:0,65 [5] x:\dir\subdir\Test\TestComponent.cshtml) - Html - after
+ HtmlContent - (74:0,74 [4] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (74:0,74 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n\n
+ HtmlContent - (140:4,1 [2] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (140:4,1 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
+ CSharpCode - (90:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (90:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private DateTime someDate = DateTime.Now;\n
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey/TestComponent.mappings.txt
new file mode 100644
index 0000000000..c1f44c953c
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey/TestComponent.mappings.txt
@@ -0,0 +1,14 @@
+Source Location: (39:0,39 [12] x:\dir\subdir\Test\TestComponent.cshtml)
+|someDate.Day|
+Generated Location: (1108:30,39 [12] )
+|someDate.Day|
+
+Source Location: (90:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml)
+|
+ private DateTime someDate = DateTime.Now;
+|
+Generated Location: (1467:47,12 [49] )
+|
+ private DateTime someDate = DateTime.Now;
+|
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.codegen.cs
new file mode 100644
index 0000000000..014af5ca83
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.codegen.cs
@@ -0,0 +1,47 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 219
+ private void __RazorDirectiveTokenHelpers__() {
+ }
+ #pragma warning restore 219
+ #pragma warning disable 0414
+ private static System.Object __o = null;
+ #pragma warning restore 0414
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ __o = "";
+ builder.AddAttribute(-1, "ChildContent", (Microsoft.AspNetCore.Components.RenderFragment)((builder2) => {
+ }
+ ));
+ builder.SetKey(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ 123 + 456
+
+#line default
+#line hidden
+#nullable disable
+ );
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+__o = typeof(MyComponent);
+
+#line default
+#line hidden
+#nullable disable
+ }
+ #pragma warning restore 1998
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.ir.txt
new file mode 100644
index 0000000000..7540550b11
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.ir.txt
@@ -0,0 +1,31 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [12] ) - System
+ UsingDirective - (18:2,1 [32] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [17] ) - System.Linq
+ UsingDirective - (73:4,1 [28] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [37] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ DesignTimeDirective -
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning disable 0414
+ CSharpCode -
+ IntermediateToken - - CSharp - private static System.Object __o = null;
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning restore 0414
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ Component - (0:0,0 [95] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
+ ComponentChildContent - - ChildContent - context
+ HtmlContent - (44:0,44 [11] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (44:0,44 [11] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n Some
+ MarkupElement - (55:1,9 [16] x:\dir\subdir\Test\TestComponent.cshtml) - el
+ HtmlContent - (59:1,13 [7] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (59:1,13 [7] x:\dir\subdir\Test\TestComponent.cshtml) - Html - further
+ HtmlContent - (71:1,25 [10] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (71:1,25 [10] x:\dir\subdir\Test\TestComponent.cshtml) - Html - content\n
+ SetKey - (18:0,18 [9] x:\dir\subdir\Test\TestComponent.cshtml) - 123 + 456
+ ComponentAttribute - - SomeProp - AttributeStructure.DoubleQuotes
+ HtmlContent - (39:0,39 [3] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (39:0,39 [3] x:\dir\subdir\Test\TestComponent.cshtml) - Html - val
+ HtmlContent - (95:2,14 [2] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (95:2,14 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.mappings.txt
new file mode 100644
index 0000000000..e508dba546
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.mappings.txt
@@ -0,0 +1,5 @@
+Source Location: (18:0,18 [9] x:\dir\subdir\Test\TestComponent.cshtml)
+|123 + 456|
+Generated Location: (1064:29,18 [9] )
+|123 + 456|
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey/TestComponent.codegen.cs
new file mode 100644
index 0000000000..445881ed56
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey/TestComponent.codegen.cs
@@ -0,0 +1,44 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 219
+ private void __RazorDirectiveTokenHelpers__() {
+ }
+ #pragma warning restore 219
+ #pragma warning disable 0414
+ private static System.Object __o = null;
+ #pragma warning restore 0414
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ builder.SetKey(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ someObject
+
+#line default
+#line hidden
+#nullable disable
+ );
+ }
+ #pragma warning restore 1998
+#nullable restore
+#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
+
+ private object someObject = new object();
+
+#line default
+#line hidden
+#nullable disable
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey/TestComponent.ir.txt
new file mode 100644
index 0000000000..5761655406
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey/TestComponent.ir.txt
@@ -0,0 +1,32 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [12] ) - System
+ UsingDirective - (18:2,1 [32] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [17] ) - System.Linq
+ UsingDirective - (73:4,1 [28] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [37] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ DesignTimeDirective -
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning disable 0414
+ CSharpCode -
+ IntermediateToken - - CSharp - private static System.Object __o = null;
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning restore 0414
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ MarkupElement - (0:0,0 [83] x:\dir\subdir\Test\TestComponent.cshtml) - elem
+ HtmlContent - (71:0,71 [5] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (71:0,71 [5] x:\dir\subdir\Test\TestComponent.cshtml) - Html - Hello
+ HtmlAttribute - - attributebefore=" - "
+ HtmlAttributeValue - (23:0,23 [6] x:\dir\subdir\Test\TestComponent.cshtml) -
+ IntermediateToken - (23:0,23 [6] x:\dir\subdir\Test\TestComponent.cshtml) - Html - before
+ SetKey - (36:0,36 [10] x:\dir\subdir\Test\TestComponent.cshtml) - someObject
+ HtmlAttribute - - attributeafter=" - "
+ HtmlAttributeValue - (64:0,64 [5] x:\dir\subdir\Test\TestComponent.cshtml) -
+ IntermediateToken - (64:0,64 [5] x:\dir\subdir\Test\TestComponent.cshtml) - Html - after
+ HtmlContent - (83:0,83 [4] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (83:0,83 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n\n
+ HtmlContent - (149:4,1 [2] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (149:4,1 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
+ CSharpCode - (99:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (99:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private object someObject = new object();\n
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey/TestComponent.mappings.txt
new file mode 100644
index 0000000000..eb409186f8
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey/TestComponent.mappings.txt
@@ -0,0 +1,14 @@
+Source Location: (36:0,36 [10] x:\dir\subdir\Test\TestComponent.cshtml)
+|someObject|
+Generated Location: (908:25,36 [10] )
+|someObject|
+
+Source Location: (99:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml)
+|
+ private object someObject = new object();
+|
+Generated Location: (1117:35,12 [49] )
+|
+ private object someObject = new object();
+|
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.codegen.cs
new file mode 100644
index 0000000000..897386b74a
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.codegen.cs
@@ -0,0 +1,56 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 219
+ private void __RazorDirectiveTokenHelpers__() {
+ }
+ #pragma warning restore 219
+ #pragma warning disable 0414
+ private static System.Object __o = null;
+ #pragma warning restore 0414
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ __o =
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ Min
+
+#line default
+#line hidden
+#nullable disable
+ ;
+ builder.SetKey(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ someObject
+
+#line default
+#line hidden
+#nullable disable
+ );
+ }
+ #pragma warning restore 1998
+#nullable restore
+#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
+
+ private object someObject = new object();
+
+ [Parameter] protected int Min { get; set; }
+
+
+#line default
+#line hidden
+#nullable disable
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.ir.txt
new file mode 100644
index 0000000000..55b45fa3e4
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.ir.txt
@@ -0,0 +1,30 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [12] ) - System
+ UsingDirective - (18:2,1 [32] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [17] ) - System.Linq
+ UsingDirective - (73:4,1 [28] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [37] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ DesignTimeDirective -
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning disable 0414
+ CSharpCode -
+ IntermediateToken - - CSharp - private static System.Object __o = null;
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning restore 0414
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ MarkupElement - (0:0,0 [62] x:\dir\subdir\Test\TestComponent.cshtml) - input
+ HtmlAttribute - - type=" - "
+ HtmlAttributeValue - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
+ IntermediateToken - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - text
+ HtmlAttribute - - data-slider-min=" - "
+ CSharpExpressionAttributeValue - (36:0,36 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
+ IntermediateToken - (37:0,37 [3] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - Min
+ SetKey - (48:0,48 [10] x:\dir\subdir\Test\TestComponent.cshtml) - someObject
+ HtmlContent - (62:0,62 [4] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (62:0,62 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n\n
+ HtmlContent - (191:6,5 [2] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (191:6,5 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
+ CSharpCode - (78:2,12 [112] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (78:2,12 [112] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private object someObject = new object();\n\n [Parameter] protected int Min { get; set; }\n
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.mappings.txt
new file mode 100644
index 0000000000..aa3e3e4fdc
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.mappings.txt
@@ -0,0 +1,23 @@
+Source Location: (37:0,37 [3] x:\dir\subdir\Test\TestComponent.cshtml)
+|Min|
+Generated Location: (900:25,37 [3] )
+|Min|
+
+Source Location: (48:0,48 [10] x:\dir\subdir\Test\TestComponent.cshtml)
+|someObject|
+Generated Location: (1117:34,48 [10] )
+|someObject|
+
+Source Location: (78:2,12 [112] x:\dir\subdir\Test\TestComponent.cshtml)
+|
+ private object someObject = new object();
+
+ [Parameter] protected int Min { get; set; }
+ |
+Generated Location: (1326:44,12 [112] )
+|
+ private object someObject = new object();
+
+ [Parameter] protected int Min { get; set; }
+ |
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.codegen.cs
new file mode 100644
index 0000000000..709829ce9a
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.codegen.cs
@@ -0,0 +1,72 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 219
+ private void __RazorDirectiveTokenHelpers__() {
+ }
+ #pragma warning restore 219
+ #pragma warning disable 0414
+ private static System.Object __o = null;
+ #pragma warning restore 0414
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ __o = typeof(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ int
+
+#line default
+#line hidden
+#nullable disable
+ );
+ __o = Microsoft.AspNetCore.Components.RuntimeHelpers.TypeCheck(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ 3
+
+#line default
+#line hidden
+#nullable disable
+ );
+ builder.AddAttribute(-1, "ChildContent", (Microsoft.AspNetCore.Components.RenderFragment)((builder2) => {
+ }
+ ));
+ builder.SetKey(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ _someKey
+
+#line default
+#line hidden
+#nullable disable
+ );
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+__o = typeof(MyComponent<>);
+
+#line default
+#line hidden
+#nullable disable
+ }
+ #pragma warning restore 1998
+#nullable restore
+#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
+
+ private object _someKey = new object();
+
+#line default
+#line hidden
+#nullable disable
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.ir.txt
new file mode 100644
index 0000000000..4738739183
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.ir.txt
@@ -0,0 +1,28 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [12] ) - System
+ UsingDirective - (18:2,1 [32] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [17] ) - System.Linq
+ UsingDirective - (73:4,1 [28] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [37] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ DesignTimeDirective -
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning disable 0414
+ CSharpCode -
+ IntermediateToken - - CSharp - private static System.Object __o = null;
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning restore 0414
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ Component - (0:0,0 [49] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
+ ComponentTypeArgument - (19:0,19 [3] x:\dir\subdir\Test\TestComponent.cshtml) - TItem
+ IntermediateToken - (19:0,19 [3] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - int
+ ComponentAttribute - (29:0,29 [1] x:\dir\subdir\Test\TestComponent.cshtml) - Item - AttributeStructure.DoubleQuotes
+ IntermediateToken - (29:0,29 [1] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - 3
+ SetKey - (37:0,37 [8] x:\dir\subdir\Test\TestComponent.cshtml) - _someKey
+ HtmlContent - (49:0,49 [4] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (49:0,49 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n\n
+ HtmlContent - (113:4,1 [2] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (113:4,1 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
+ CSharpCode - (65:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (65:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private object _someKey = new object();\n
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.mappings.txt
new file mode 100644
index 0000000000..2a8f37b34e
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.mappings.txt
@@ -0,0 +1,24 @@
+Source Location: (19:0,19 [3] x:\dir\subdir\Test\TestComponent.cshtml)
+|int|
+Generated Location: (889:25,19 [3] )
+|int|
+
+Source Location: (29:0,29 [1] x:\dir\subdir\Test\TestComponent.cshtml)
+|3|
+Generated Location: (1141:34,29 [1] )
+|3|
+
+Source Location: (37:0,37 [8] x:\dir\subdir\Test\TestComponent.cshtml)
+|_someKey|
+Generated Location: (1497:46,37 [8] )
+|_someKey|
+
+Source Location: (65:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml)
+|
+ private object _someKey = new object();
+|
+Generated Location: (1854:63,12 [47] )
+|
+ private object _someKey = new object();
+|
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.codegen.cs
new file mode 100644
index 0000000000..1ee445b21c
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.codegen.cs
@@ -0,0 +1,73 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 219
+ private void __RazorDirectiveTokenHelpers__() {
+ }
+ #pragma warning restore 219
+ #pragma warning disable 0414
+ private static System.Object __o = null;
+ #pragma warning restore 0414
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ __Blazor.Test.TestComponent.TypeInference.CreateMyComponent_0(builder, -1, -1,
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ 3
+
+#line default
+#line hidden
+#nullable disable
+ , -1,
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ _someKey
+
+#line default
+#line hidden
+#nullable disable
+ );
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+__o = typeof(MyComponent<>);
+
+#line default
+#line hidden
+#nullable disable
+ }
+ #pragma warning restore 1998
+#nullable restore
+#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
+
+ private object _someKey = new object();
+
+#line default
+#line hidden
+#nullable disable
+ }
+}
+namespace __Blazor.Test.TestComponent
+{
+ #line hidden
+ internal static class TypeInference
+ {
+ public static void CreateMyComponent_0(global::Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder, int seq, int __seq0, TItem __arg0, int __seq1, object __arg1)
+ {
+ builder.OpenComponent>(seq);
+ builder.AddAttribute(__seq0, "Item", __arg0);
+ builder.SetKey(__arg1);
+ builder.CloseComponent();
+ }
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.ir.txt
new file mode 100644
index 0000000000..3b5cc52c25
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.ir.txt
@@ -0,0 +1,29 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [12] ) - System
+ UsingDirective - (18:2,1 [32] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [17] ) - System.Linq
+ UsingDirective - (73:4,1 [28] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [37] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ DesignTimeDirective -
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning disable 0414
+ CSharpCode -
+ IntermediateToken - - CSharp - private static System.Object __o = null;
+ CSharpCode -
+ IntermediateToken - - CSharp - #pragma warning restore 0414
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ Component - (0:0,0 [39] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
+ ComponentAttribute - (19:0,19 [1] x:\dir\subdir\Test\TestComponent.cshtml) - Item - AttributeStructure.DoubleQuotes
+ IntermediateToken - (19:0,19 [1] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - 3
+ SetKey - (27:0,27 [8] x:\dir\subdir\Test\TestComponent.cshtml) - _someKey
+ HtmlContent - (39:0,39 [4] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (39:0,39 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n\n
+ HtmlContent - (103:4,1 [2] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (103:4,1 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
+ CSharpCode - (55:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (55:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private object _someKey = new object();\n
+ NamespaceDeclaration - - __Blazor.Test.TestComponent
+ ClassDeclaration - - internal static - TypeInference - -
+ ComponentTypeInferenceMethod - - __Blazor.Test.TestComponent.TypeInference - CreateMyComponent_0
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.mappings.txt
new file mode 100644
index 0000000000..474623cead
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.mappings.txt
@@ -0,0 +1,19 @@
+Source Location: (19:0,19 [1] x:\dir\subdir\Test\TestComponent.cshtml)
+|3|
+Generated Location: (955:25,19 [1] )
+|3|
+
+Source Location: (27:0,27 [8] x:\dir\subdir\Test\TestComponent.cshtml)
+|_someKey|
+Generated Location: (1125:33,27 [8] )
+|_someKey|
+
+Source Location: (55:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml)
+|
+ private object _someKey = new object();
+|
+Generated Location: (1482:50,12 [47] )
+|
+ private object _someKey = new object();
+|
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey/TestComponent.codegen.cs
new file mode 100644
index 0000000000..0a8803d967
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey/TestComponent.codegen.cs
@@ -0,0 +1,41 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ builder.OpenComponent(0);
+ builder.AddAttribute(1, "ParamBefore", "before");
+ builder.AddAttribute(2, "ParamAfter", "after");
+ builder.SetKey(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ someDate.Day
+
+#line default
+#line hidden
+#nullable disable
+ );
+ builder.CloseComponent();
+ }
+ #pragma warning restore 1998
+#nullable restore
+#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
+
+ private DateTime someDate = DateTime.Now;
+
+#line default
+#line hidden
+#nullable disable
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey/TestComponent.ir.txt
new file mode 100644
index 0000000000..021fc0aead
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey/TestComponent.ir.txt
@@ -0,0 +1,19 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [14] ) - System
+ UsingDirective - (18:2,1 [34] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [19] ) - System.Linq
+ UsingDirective - (73:4,1 [30] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ Component - (0:0,0 [74] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
+ ComponentAttribute - - ParamBefore - AttributeStructure.DoubleQuotes
+ HtmlContent - (26:0,26 [6] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (26:0,26 [6] x:\dir\subdir\Test\TestComponent.cshtml) - Html - before
+ SetKey - (39:0,39 [12] x:\dir\subdir\Test\TestComponent.cshtml) - someDate.Day
+ ComponentAttribute - - ParamAfter - AttributeStructure.DoubleQuotes
+ HtmlContent - (65:0,65 [5] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (65:0,65 [5] x:\dir\subdir\Test\TestComponent.cshtml) - Html - after
+ CSharpCode - (90:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (90:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private DateTime someDate = DateTime.Now;\n
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey/TestComponent.mappings.txt
new file mode 100644
index 0000000000..bdca049e18
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey/TestComponent.mappings.txt
@@ -0,0 +1,14 @@
+Source Location: (39:0,39 [12] x:\dir\subdir\Test\TestComponent.cshtml)
+|someDate.Day|
+Generated Location: (824:21,39 [12] )
+|someDate.Day|
+
+Source Location: (90:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml)
+|
+ private DateTime someDate = DateTime.Now;
+|
+Generated Location: (1074:32,12 [49] )
+|
+ private DateTime someDate = DateTime.Now;
+|
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.codegen.cs
new file mode 100644
index 0000000000..17c3ae4e46
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.codegen.cs
@@ -0,0 +1,37 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ builder.OpenComponent(0);
+ builder.AddAttribute(1, "SomeProp", "val");
+ builder.AddAttribute(2, "ChildContent", (Microsoft.AspNetCore.Components.RenderFragment)((builder2) => {
+ builder2.AddMarkupContent(3, "\r\n Some ");
+ builder2.AddMarkupContent(4, "further content\r\n");
+ }
+ ));
+ builder.SetKey(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ 123 + 456
+
+#line default
+#line hidden
+#nullable disable
+ );
+ builder.CloseComponent();
+ }
+ #pragma warning restore 1998
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.ir.txt
new file mode 100644
index 0000000000..d5cee0071c
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.ir.txt
@@ -0,0 +1,18 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [14] ) - System
+ UsingDirective - (18:2,1 [34] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [19] ) - System.Linq
+ UsingDirective - (73:4,1 [30] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ Component - (0:0,0 [95] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
+ ComponentChildContent - - ChildContent - context
+ HtmlContent - (44:0,44 [11] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (44:0,44 [11] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n Some
+ MarkupBlock - - further content\n
+ SetKey - (18:0,18 [9] x:\dir\subdir\Test\TestComponent.cshtml) - 123 + 456
+ ComponentAttribute - - SomeProp - AttributeStructure.DoubleQuotes
+ HtmlContent - (39:0,39 [3] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (39:0,39 [3] x:\dir\subdir\Test\TestComponent.cshtml) - Html - val
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.mappings.txt
new file mode 100644
index 0000000000..d41eee6ea6
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Component_WithKey_WithChildContent/TestComponent.mappings.txt
@@ -0,0 +1,5 @@
+Source Location: (18:0,18 [9] x:\dir\subdir\Test\TestComponent.cshtml)
+|123 + 456|
+Generated Location: (1029:27,18 [9] )
+|123 + 456|
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey/TestComponent.codegen.cs
new file mode 100644
index 0000000000..fd9d0844f6
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey/TestComponent.codegen.cs
@@ -0,0 +1,42 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ builder.OpenElement(0, "elem");
+ builder.AddAttribute(1, "attributebefore", "before");
+ builder.AddAttribute(2, "attributeafter", "after");
+ builder.SetKey(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ someObject
+
+#line default
+#line hidden
+#nullable disable
+ );
+ builder.AddContent(3, "Hello");
+ builder.CloseElement();
+ }
+ #pragma warning restore 1998
+#nullable restore
+#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
+
+ private object someObject = new object();
+
+#line default
+#line hidden
+#nullable disable
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey/TestComponent.ir.txt
new file mode 100644
index 0000000000..5c5902d95a
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey/TestComponent.ir.txt
@@ -0,0 +1,21 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [14] ) - System
+ UsingDirective - (18:2,1 [34] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [19] ) - System.Linq
+ UsingDirective - (73:4,1 [30] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ MarkupElement - (0:0,0 [83] x:\dir\subdir\Test\TestComponent.cshtml) - elem
+ HtmlContent - (71:0,71 [5] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (71:0,71 [5] x:\dir\subdir\Test\TestComponent.cshtml) - Html - Hello
+ HtmlAttribute - - attributebefore=" - "
+ HtmlAttributeValue - (23:0,23 [6] x:\dir\subdir\Test\TestComponent.cshtml) -
+ IntermediateToken - (23:0,23 [6] x:\dir\subdir\Test\TestComponent.cshtml) - Html - before
+ SetKey - (36:0,36 [10] x:\dir\subdir\Test\TestComponent.cshtml) - someObject
+ HtmlAttribute - - attributeafter=" - "
+ HtmlAttributeValue - (64:0,64 [5] x:\dir\subdir\Test\TestComponent.cshtml) -
+ IntermediateToken - (64:0,64 [5] x:\dir\subdir\Test\TestComponent.cshtml) - Html - after
+ CSharpCode - (99:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (99:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private object someObject = new object();\n
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey/TestComponent.mappings.txt
new file mode 100644
index 0000000000..0a63f5d85d
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey/TestComponent.mappings.txt
@@ -0,0 +1,14 @@
+Source Location: (36:0,36 [10] x:\dir\subdir\Test\TestComponent.cshtml)
+|someObject|
+Generated Location: (817:21,36 [10] )
+|someObject|
+
+Source Location: (99:2,12 [49] x:\dir\subdir\Test\TestComponent.cshtml)
+|
+ private object someObject = new object();
+|
+Generated Location: (1108:33,12 [49] )
+|
+ private object someObject = new object();
+|
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.codegen.cs
new file mode 100644
index 0000000000..176ad7f171
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.codegen.cs
@@ -0,0 +1,52 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ builder.OpenElement(0, "input");
+ builder.AddAttribute(1, "type", "text");
+ builder.AddAttribute(2, "data-slider-min",
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ Min
+
+#line default
+#line hidden
+#nullable disable
+ );
+ builder.SetKey(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ someObject
+
+#line default
+#line hidden
+#nullable disable
+ );
+ builder.CloseElement();
+ }
+ #pragma warning restore 1998
+#nullable restore
+#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
+
+ private object someObject = new object();
+
+ [Parameter] protected int Min { get; set; }
+
+
+#line default
+#line hidden
+#nullable disable
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.ir.txt
new file mode 100644
index 0000000000..c4b3b00270
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.ir.txt
@@ -0,0 +1,19 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [14] ) - System
+ UsingDirective - (18:2,1 [34] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [19] ) - System.Linq
+ UsingDirective - (73:4,1 [30] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ MarkupElement - (0:0,0 [62] x:\dir\subdir\Test\TestComponent.cshtml) - input
+ HtmlAttribute - - type=" - "
+ HtmlAttributeValue - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
+ IntermediateToken - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - text
+ HtmlAttribute - - data-slider-min=" - "
+ CSharpExpressionAttributeValue - (36:0,36 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
+ IntermediateToken - (37:0,37 [3] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - Min
+ SetKey - (48:0,48 [10] x:\dir\subdir\Test\TestComponent.cshtml) - someObject
+ CSharpCode - (78:2,12 [112] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (78:2,12 [112] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private object someObject = new object();\n\n [Parameter] protected int Min { get; set; }\n
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.mappings.txt
new file mode 100644
index 0000000000..7530b38835
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/Element_WithKey_AndOtherAttributes/TestComponent.mappings.txt
@@ -0,0 +1,18 @@
+Source Location: (48:0,48 [10] x:\dir\subdir\Test\TestComponent.cshtml)
+|someObject|
+Generated Location: (987:29,48 [10] )
+|someObject|
+
+Source Location: (78:2,12 [112] x:\dir\subdir\Test\TestComponent.cshtml)
+|
+ private object someObject = new object();
+
+ [Parameter] protected int Min { get; set; }
+ |
+Generated Location: (1233:40,12 [112] )
+|
+ private object someObject = new object();
+
+ [Parameter] protected int Min { get; set; }
+ |
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.codegen.cs
new file mode 100644
index 0000000000..7afcd337b5
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.codegen.cs
@@ -0,0 +1,48 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ builder.OpenComponent>(0);
+ builder.AddAttribute(1, "Item", Microsoft.AspNetCore.Components.RuntimeHelpers.TypeCheck(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ 3
+
+#line default
+#line hidden
+#nullable disable
+ ));
+ builder.SetKey(
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ _someKey
+
+#line default
+#line hidden
+#nullable disable
+ );
+ builder.CloseComponent();
+ }
+ #pragma warning restore 1998
+#nullable restore
+#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
+
+ private object _someKey = new object();
+
+#line default
+#line hidden
+#nullable disable
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.ir.txt
new file mode 100644
index 0000000000..f3c2f8e143
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.ir.txt
@@ -0,0 +1,17 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [14] ) - System
+ UsingDirective - (18:2,1 [34] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [19] ) - System.Linq
+ UsingDirective - (73:4,1 [30] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ Component - (0:0,0 [49] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
+ ComponentTypeArgument - (19:0,19 [3] x:\dir\subdir\Test\TestComponent.cshtml) - TItem
+ IntermediateToken - (19:0,19 [3] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - int
+ ComponentAttribute - (29:0,29 [1] x:\dir\subdir\Test\TestComponent.cshtml) - Item - AttributeStructure.DoubleQuotes
+ IntermediateToken - (29:0,29 [1] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - 3
+ SetKey - (37:0,37 [8] x:\dir\subdir\Test\TestComponent.cshtml) - _someKey
+ CSharpCode - (65:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (65:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private object _someKey = new object();\n
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.mappings.txt
new file mode 100644
index 0000000000..508332c609
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey/TestComponent.mappings.txt
@@ -0,0 +1,14 @@
+Source Location: (37:0,37 [8] x:\dir\subdir\Test\TestComponent.cshtml)
+|_someKey|
+Generated Location: (980:28,37 [8] )
+|_someKey|
+
+Source Location: (65:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml)
+|
+ private object _someKey = new object();
+|
+Generated Location: (1226:39,12 [47] )
+|
+ private object _someKey = new object();
+|
+
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.codegen.cs
new file mode 100644
index 0000000000..6e8d31af45
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.codegen.cs
@@ -0,0 +1,59 @@
+//
+#pragma warning disable 1591
+namespace Test
+{
+ #line hidden
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using Microsoft.AspNetCore.Components;
+ public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase
+ {
+ #pragma warning disable 1998
+ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
+ {
+ __Blazor.Test.TestComponent.TypeInference.CreateMyComponent_0(builder, 0, 1,
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ 3
+
+#line default
+#line hidden
+#nullable disable
+ , 2,
+#nullable restore
+#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
+ _someKey
+
+#line default
+#line hidden
+#nullable disable
+ );
+ }
+ #pragma warning restore 1998
+#nullable restore
+#line 3 "x:\dir\subdir\Test\TestComponent.cshtml"
+
+ private object _someKey = new object();
+
+#line default
+#line hidden
+#nullable disable
+ }
+}
+namespace __Blazor.Test.TestComponent
+{
+ #line hidden
+ internal static class TypeInference
+ {
+ public static void CreateMyComponent_0(global::Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder, int seq, int __seq0, TItem __arg0, int __seq1, object __arg1)
+ {
+ builder.OpenComponent>(seq);
+ builder.AddAttribute(__seq0, "Item", __arg0);
+ builder.SetKey(__arg1);
+ builder.CloseComponent();
+ }
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.ir.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.ir.txt
new file mode 100644
index 0000000000..35285b1a16
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.ir.txt
@@ -0,0 +1,18 @@
+Document -
+ NamespaceDeclaration - - Test
+ UsingDirective - (3:1,1 [14] ) - System
+ UsingDirective - (18:2,1 [34] ) - System.Collections.Generic
+ UsingDirective - (53:3,1 [19] ) - System.Linq
+ UsingDirective - (73:4,1 [30] ) - System.Threading.Tasks
+ UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
+ ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
+ MethodDeclaration - - protected override - void - BuildRenderTree
+ Component - (0:0,0 [39] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
+ ComponentAttribute - (19:0,19 [1] x:\dir\subdir\Test\TestComponent.cshtml) - Item - AttributeStructure.DoubleQuotes
+ IntermediateToken - (19:0,19 [1] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - 3
+ SetKey - (27:0,27 [8] x:\dir\subdir\Test\TestComponent.cshtml) - _someKey
+ CSharpCode - (55:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml)
+ IntermediateToken - (55:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n private object _someKey = new object();\n
+ NamespaceDeclaration - - __Blazor.Test.TestComponent
+ ClassDeclaration - - internal static - TypeInference - -
+ ComponentTypeInferenceMethod - - __Blazor.Test.TestComponent.TypeInference - CreateMyComponent_0
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.mappings.txt
new file mode 100644
index 0000000000..0873889778
--- /dev/null
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/GenericComponent_WithKey_TypeInference/TestComponent.mappings.txt
@@ -0,0 +1,14 @@
+Source Location: (27:0,27 [8] x:\dir\subdir\Test\TestComponent.cshtml)
+|_someKey|
+Generated Location: (854:26,27 [8] )
+|_someKey|
+
+Source Location: (55:2,12 [47] x:\dir\subdir\Test\TestComponent.cshtml)
+|
+ private object _someKey = new object();
+|
+Generated Location: (1061:36,12 [47] )
+|
+ private object _someKey = new object();
+|
+
diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/src/CompilerFeatures.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/src/CompilerFeatures.cs
index b42e6b5cc8..b555832cc8 100644
--- a/src/Razor/Microsoft.CodeAnalysis.Razor/src/CompilerFeatures.cs
+++ b/src/Razor/Microsoft.CodeAnalysis.Razor/src/CompilerFeatures.cs
@@ -28,6 +28,7 @@ namespace Microsoft.CodeAnalysis.Razor
builder.Features.Add(new ComponentTagHelperDescriptorProvider());
builder.Features.Add(new EventHandlerTagHelperDescriptorProvider());
builder.Features.Add(new RefTagHelperDescriptorProvider());
+ builder.Features.Add(new KeyTagHelperDescriptorProvider());
builder.Features.Add(new DefaultTypeNameFeature());
}
diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/src/KeyTagHelperDescriptorProvider.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/src/KeyTagHelperDescriptorProvider.cs
new file mode 100644
index 0000000000..7ea05126be
--- /dev/null
+++ b/src/Razor/Microsoft.CodeAnalysis.Razor/src/KeyTagHelperDescriptorProvider.cs
@@ -0,0 +1,77 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.AspNetCore.Razor.Language.Components;
+
+namespace Microsoft.CodeAnalysis.Razor
+{
+ internal class KeyTagHelperDescriptorProvider : ITagHelperDescriptorProvider
+ {
+ // Run after the component tag helper provider
+ public int Order { get; set; } = 1000;
+
+ public RazorEngine Engine { get; set; }
+
+ public void Execute(TagHelperDescriptorProviderContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ var compilation = context.GetCompilation();
+ if (compilation == null)
+ {
+ return;
+ }
+
+ var renderTreeBuilderType = compilation.GetTypeByMetadataName(ComponentsApi.RenderTreeBuilder.FullTypeName);
+ if (renderTreeBuilderType == null)
+ {
+ // If we can't find RenderTreeBuilder, then just bail. We won't be able to compile the
+ // generated code anyway.
+ return;
+ }
+
+ context.Results.Add(CreateKeyTagHelper());
+ }
+
+ private TagHelperDescriptor CreateKeyTagHelper()
+ {
+ var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.Key.TagHelperKind, "Key", ComponentsApi.AssemblyName);
+ builder.Documentation = ComponentResources.KeyTagHelper_Documentation;
+
+ builder.Metadata.Add(ComponentMetadata.SpecialKindKey, ComponentMetadata.Key.TagHelperKind);
+ builder.Metadata.Add(TagHelperMetadata.Common.ClassifyAttributesOnly, bool.TrueString);
+ builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.Key.RuntimeName;
+
+ // WTE has a bug in 15.7p1 where a Tag Helper without a display-name that looks like
+ // a C# property will crash trying to create the tooltips.
+ builder.SetTypeName("Microsoft.AspNetCore.Components.Key");
+
+ builder.TagMatchingRule(rule =>
+ {
+ rule.TagName = "*";
+ rule.Attribute(attribute =>
+ {
+ attribute.Name = "key";
+ });
+ });
+
+ builder.BindAttribute(attribute =>
+ {
+ attribute.Documentation = ComponentResources.KeyTagHelper_Documentation;
+ attribute.Name = "key";
+
+ // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like
+ // a C# property will crash trying to create the tooltips.
+ attribute.SetPropertyName("Key");
+ attribute.TypeName = typeof(object).FullName;
+ });
+
+ return builder.Build();
+ }
+ }
+}
diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/test/KeyTagHelperDescriptorProviderTest.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/test/KeyTagHelperDescriptorProviderTest.cs
new file mode 100644
index 0000000000..ae9288bd19
--- /dev/null
+++ b/src/Razor/Microsoft.CodeAnalysis.Razor/test/KeyTagHelperDescriptorProviderTest.cs
@@ -0,0 +1,88 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.AspNetCore.Razor.Language.Components;
+using Xunit;
+
+namespace Microsoft.CodeAnalysis.Razor
+{
+ public class KeyTagHelperDescriptorProviderTest : TagHelperDescriptorProviderTestBase
+ {
+ [Fact]
+ public void Execute_CreatesDescriptor()
+ {
+ // Arrange
+ var context = TagHelperDescriptorProviderContext.Create();
+ context.SetCompilation(BaseCompilation);
+
+ var provider = new KeyTagHelperDescriptorProvider();
+
+ // Act
+ provider.Execute(context);
+
+ // Assert
+ var matches = context.Results.Where(result => result.IsKeyTagHelper());
+ var item = Assert.Single(matches);
+
+ Assert.Empty(item.AllowedChildTags);
+ Assert.Null(item.TagOutputHint);
+ Assert.Empty(item.Diagnostics);
+ Assert.False(item.HasErrors);
+ Assert.Equal(ComponentMetadata.Key.TagHelperKind, item.Kind);
+ Assert.Equal(bool.TrueString, item.Metadata[TagHelperMetadata.Common.ClassifyAttributesOnly]);
+ Assert.Equal(ComponentMetadata.Key.RuntimeName, item.Metadata[TagHelperMetadata.Runtime.Name]);
+ Assert.False(item.IsDefaultKind());
+ Assert.False(item.KindUsesDefaultTagHelperRuntime());
+
+ Assert.Equal(
+ "Ensures that the component or element will be preserved across renders if (and only if) the supplied key value matches.",
+ item.Documentation);
+
+ Assert.Equal("Microsoft.AspNetCore.Components", item.AssemblyName);
+ Assert.Equal("Key", item.Name);
+ Assert.Equal("Microsoft.AspNetCore.Components.Key", item.DisplayName);
+ Assert.Equal("Microsoft.AspNetCore.Components.Key", item.GetTypeName());
+
+ // The tag matching rule for a key is just the attribute name "key"
+ var rule = Assert.Single(item.TagMatchingRules);
+ Assert.Empty(rule.Diagnostics);
+ Assert.False(rule.HasErrors);
+ Assert.Null(rule.ParentTag);
+ Assert.Equal("*", rule.TagName);
+ Assert.Equal(TagStructure.Unspecified, rule.TagStructure);
+
+ var requiredAttribute = Assert.Single(rule.Attributes);
+ Assert.Empty(requiredAttribute.Diagnostics);
+ Assert.Equal("key", requiredAttribute.DisplayName);
+ Assert.Equal("key", requiredAttribute.Name);
+ Assert.Equal(RequiredAttributeDescriptor.NameComparisonMode.FullMatch, requiredAttribute.NameComparison);
+ Assert.Null(requiredAttribute.Value);
+ Assert.Equal(RequiredAttributeDescriptor.ValueComparisonMode.None, requiredAttribute.ValueComparison);
+
+ var attribute = Assert.Single(item.BoundAttributes);
+ Assert.Empty(attribute.Diagnostics);
+ Assert.False(attribute.HasErrors);
+ Assert.Equal(ComponentMetadata.Key.TagHelperKind, attribute.Kind);
+ Assert.False(attribute.IsDefaultKind());
+ Assert.False(attribute.HasIndexer);
+ Assert.Null(attribute.IndexerNamePrefix);
+ Assert.Null(attribute.IndexerTypeName);
+ Assert.False(attribute.IsIndexerBooleanProperty);
+ Assert.False(attribute.IsIndexerStringProperty);
+
+ Assert.Equal(
+ "Ensures that the component or element will be preserved across renders if (and only if) the supplied key value matches.",
+ attribute.Documentation);
+
+ Assert.Equal("key", attribute.Name);
+ Assert.Equal("Key", attribute.GetPropertyName());
+ Assert.Equal("object Microsoft.AspNetCore.Components.Key.Key", attribute.DisplayName);
+ Assert.Equal("System.Object", attribute.TypeName);
+ Assert.False(attribute.IsStringProperty);
+ Assert.False(attribute.IsBooleanProperty);
+ Assert.False(attribute.IsEnum);
+ }
+ }
+}
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs
index 4ae07f5e8d..c83dbb7c79 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/IntermediateNodeWriter.cs
@@ -168,6 +168,11 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
WriteContentNode(node, node.IdentifierToken?.Content);
}
+ public override void VisitSetKey(SetKeyIntermediateNode node)
+ {
+ WriteContentNode(node, node.KeyValueToken?.Content);
+ }
+
void IExtensionIntermediateNodeVisitor.VisitExtension(RouteAttributeExtensionNode node)
{
WriteContentNode(node, node.Template);
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components/RenderTree/RenderTreeBuilder.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components/RenderTree/RenderTreeBuilder.cs
index e07c337a4d..2dfaac26cf 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components/RenderTree/RenderTreeBuilder.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components/RenderTree/RenderTreeBuilder.cs
@@ -103,5 +103,9 @@ namespace Microsoft.AspNetCore.Components.RenderTree
public void AddComponentReferenceCapture(int sequence, Action