File: Mvc\ModelExpressionPass.cs
Web Access
Project: src\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;
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);
            }
        }
    }
}