File: Mvc\ModelExpressionPass.cs
Web Access
Project: src\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;
using System.Collections.Generic;
using System.Threading;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
 
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions;
 
public sealed class ModelExpressionPass : IntermediateNodePassBase, IRazorOptimizationPass
{
    private const string ModelExpressionTypeName = "Microsoft.AspNetCore.Mvc.ViewFeatures.ModelExpression";
 
    protected override void ExecuteCore(
        RazorCodeDocument codeDocument,
        DocumentIntermediateNode documentNode,
        CancellationToken cancellationToken)
    {
        if (documentNode.DocumentKind != RazorPageDocumentClassifierPass.RazorPageDocumentKind &&
            documentNode.DocumentKind != MvcViewDocumentClassifierPass.MvcViewDocumentKind)
        {
            // Not a MVC file. Skip.
            return;
        }
 
        var visitor = new Visitor();
        visitor.Visit(documentNode);
    }
 
    private class Visitor : IntermediateNodeWalker
    {
        public List<TagHelperIntermediateNode> TagHelpers { get; } = new List<TagHelperIntermediateNode>();
 
        public override void VisitTagHelperProperty(TagHelperPropertyIntermediateNode node)
        {
            if (string.Equals(node.BoundAttribute.TypeName, ModelExpressionTypeName, StringComparison.Ordinal) ||
                (node.IsIndexerNameMatch &&
                 string.Equals(node.BoundAttribute.IndexerTypeName, ModelExpressionTypeName, StringComparison.Ordinal)))
            {
                var expression = new CSharpExpressionIntermediateNode();
 
                expression.Children.Add(IntermediateNodeFactory.CSharpToken("ModelExpressionProvider.CreateModelExpression(ViewData, __model => "));
 
                if (node.Children.Count == 1 && node.Children[0] is CSharpIntermediateToken token)
                {
                    // A 'simple' expression will look like __model => __model.Foo
 
                    expression.Children.Add(IntermediateNodeFactory.CSharpToken("__model."));
 
                    expression.Children.Add(token);
                }
                else
                {
                    for (var i = 0; i < node.Children.Count; i++)
                    {
                        if (node.Children[i] is CSharpExpressionIntermediateNode nestedExpression)
                        {
                            for (var j = 0; j < nestedExpression.Children.Count; j++)
                            {
                                if (nestedExpression.Children[j] is CSharpIntermediateToken csharpToken)
                                {
                                    expression.Children.Add(csharpToken);
                                }
                            }
 
                            continue;
                        }
                    }
                }
 
                expression.Children.Add(IntermediateNodeFactory.CSharpToken(")"));
 
                node.Children.Clear();
 
                node.Children.Add(expression);
            }
        }
    }
}