| File: Language\Components\ComponentComplexAttributeContentPass.cs | Web Access |
| Project: src\roslyn\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Linq; using System.Threading; using Microsoft.AspNetCore.Razor.Language.Intermediate; namespace Microsoft.AspNetCore.Razor.Language.Components; // We don't support 'complex' content for components (mixed C# and markup) right now. // It's not clear yet if components will have a good scenario to use these constructs. // // This is where a lot of the complexity in the Razor/TagHelpers model creeps in and we // might be able to avoid it if these features aren't needed. internal sealed class ComponentComplexAttributeContentPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass { // Run before other Component passes public override int Order => -1000; protected override void ExecuteCore( RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode, CancellationToken cancellationToken) { if (!IsComponentDocument(documentNode)) { return; } foreach (var node in documentNode.FindDescendantNodes<TagHelperIntermediateNode>()) { ProcessAttributes(node); } } private void ProcessAttributes(TagHelperIntermediateNode node) { for (var i = node.Children.Count - 1; i >= 0; i--) { if (node.Children[i] is TagHelperPropertyIntermediateNode propertyNode && node.TagHelpers.Any(t => t.Kind == TagHelperKind.Component)) { ProcessAttribute(node, propertyNode, propertyNode.AttributeName); } else if (node.Children[i] is TagHelperHtmlAttributeIntermediateNode htmlNode && node.TagHelpers.Any(t => t.Kind == TagHelperKind.Component)) { ProcessAttribute(node, htmlNode, htmlNode.AttributeName); } else if (node.Children[i] is TagHelperDirectiveAttributeIntermediateNode directiveAttributeNode) { ProcessAttribute(node, directiveAttributeNode, directiveAttributeNode.OriginalAttributeName); } } } private static void ProcessAttribute(IntermediateNode parent, IntermediateNode node, string attributeName) { var issueDiagnostic = false; if (node.Children is [HtmlAttributeIntermediateNode { Children.Count: > 1 }]) { // This case can be hit for a 'string' attribute issueDiagnostic = true; } else if (node.Children is [CSharpExpressionIntermediateNode { Children.Count: > 1 } cSharpNode]) { // This case can be hit when the attribute has an explicit @ inside, which // 'escapes' any special sugar we provide for codegen. // // There's a special case here for explicit expressions. See https://github.com/aspnet/Razor/issues/2203 // handling this case as a tactical matter since it's important for lambdas. if (cSharpNode.Children is [IntermediateToken { Content: "(" }, _, IntermediateToken { Content: ")" }]) { cSharpNode.Children.RemoveAt(2); cSharpNode.Children.RemoveAt(0); } else { issueDiagnostic = true; } } else if (node.Children is [CSharpCodeIntermediateNode]) { // This is the case when an attribute contains a code block @{ ... } // We don't support this. issueDiagnostic = true; } else if (node.Children.Count > 1) { // This is the common case for 'mixed' content issueDiagnostic = true; } if (issueDiagnostic) { node.AddDiagnostic(ComponentDiagnosticFactory.Create_UnsupportedComplexContent( node, attributeName)); } } }