| File: Language\Components\ComponentChildContentDiagnosticPass.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.Threading; using Microsoft.AspNetCore.Razor.Language.Intermediate; namespace Microsoft.AspNetCore.Razor.Language.Components; internal sealed class ComponentChildContentDiagnosticPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass { // Runs after components/eventhandlers/ref/bind/templates. We want to validate every component // and it's usage of ChildContent. public override int Order => 160; protected override void ExecuteCore( RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode, CancellationToken cancellationToken) { if (!IsComponentDocument(documentNode)) { return; } var visitor = new Visitor(); visitor.Visit(documentNode); } private sealed class Visitor : IntermediateNodeWalker { public override void VisitComponent(ComponentIntermediateNode node) { // Check for properties that are set by both element contents (body) and the attribute itself. foreach (var childContent in node.ChildContents) { foreach (var attribute in node.Attributes) { if (attribute.AttributeName == childContent.AttributeName) { node.AddDiagnostic(ComponentDiagnosticFactory.Create_ChildContentSetByAttributeAndBody( attribute.Source, attribute.AttributeName)); } } } VisitDefault(node); } public override void VisitComponentChildContent(ComponentChildContentIntermediateNode node) { // Check that each child content has a unique parameter name within its scope. This is important // because the parameter name can be implicit, and it doesn't work well when nested. if (node.IsParameterized) { var ancestors = Ancestors; var parentComponent = (ComponentIntermediateNode)ancestors[0]; // Skip the immediate parent component as we've already validated against it. // Loop to ancestors.Length - 1 because we're always checking pairs. for (var i = 1; i < ancestors.Length - 1; i++) { if (ancestors[i] is ComponentChildContentIntermediateNode { IsParameterized: true } ancestor && ancestor.ParameterName == node.ParameterName && ancestors[i + 1] is ComponentIntermediateNode ancestorParentComponent) { // Duplicate name. We report an error because this will almost certainly also lead to an error // from the C# compiler that's way less clear. node.AddDiagnostic(ComponentDiagnosticFactory.Create_ChildContentRepeatedParameterName( node.Source, childContent1: node, component1: parentComponent, childContent2: ancestor, component2: ancestorParentComponent)); } } } VisitDefault(node); } } }