File: Operations\OperationMapBuilder.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.Operations;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis
{
    internal static class OperationMapBuilder
    {
        /// <summary>
        /// Populates a empty dictionary of SyntaxNode->IOperation, where every key corresponds to an explicit IOperation node.
        /// If there is a SyntaxNode with more than one explicit IOperation, this will throw.
        /// </summary>
        internal static void AddToMap(IOperation root, Dictionary<SyntaxNode, IOperation> dictionary)
        {
            Debug.Assert(dictionary.Count == 0);
            Walker.Instance.Visit(root, dictionary);
        }
 
        private sealed class Walker : OperationWalker<Dictionary<SyntaxNode, IOperation>>
        {
            internal static readonly Walker Instance = new Walker();
 
            public override object? DefaultVisit(IOperation operation, Dictionary<SyntaxNode, IOperation> argument)
            {
                RecordOperation(operation, argument);
                return base.DefaultVisit(operation, argument);
            }
 
            public override object? VisitBinaryOperator([DisallowNull] IBinaryOperation? operation, Dictionary<SyntaxNode, IOperation> argument)
            {
                // In order to handle very large nested operators, we implement manual iteration here. Our operations are not order sensitive,
                // so we don't need to maintain a stack, just iterate through every level.
                while (true)
                {
                    RecordOperation(operation, argument);
                    Visit(operation.RightOperand, argument);
                    if (operation.LeftOperand is IBinaryOperation nested)
                    {
                        operation = nested;
                    }
                    else
                    {
                        Visit(operation.LeftOperand, argument);
                        break;
                    }
                }
 
                return null;
            }
 
            public override object? VisitConditional(IConditionalOperation operation, Dictionary<SyntaxNode, IOperation> argument)
            {
                while (true)
                {
                    RecordOperation(operation, argument);
                    Visit(operation.Condition, argument);
                    Visit(operation.WhenTrue, argument);
                    if (operation.WhenFalse is IConditionalOperation nested)
                    {
                        operation = nested;
                    }
                    else
                    {
                        Visit(operation.WhenFalse, argument);
                        break;
                    }
                }
 
                return null;
            }
 
            public override object? VisitBinaryPattern(IBinaryPatternOperation operation, Dictionary<SyntaxNode, IOperation> argument)
            {
                // In order to handle very large nested patterns, we implement manual iteration here. Our operations are not order sensitive,
                // so we don't need to maintain a stack, just iterate through every level.
                while (true)
                {
                    RecordOperation(operation, argument);
                    Visit(operation.RightPattern, argument);
                    if (operation.LeftPattern is IBinaryPatternOperation nested)
                    {
                        operation = nested;
                    }
                    else
                    {
                        Visit(operation.LeftPattern, argument);
                        break;
                    }
                }
 
                return null;
            }
 
            internal override object? VisitNoneOperation(IOperation operation, Dictionary<SyntaxNode, IOperation> argument)
            {
                // OperationWalker skips these nodes by default, to avoid having public consumers deal with NoneOperation.
                // we need to deal with it here, however, so delegate to DefaultVisit.
                return DefaultVisit(operation, argument);
            }
 
            private static void RecordOperation(IOperation operation, Dictionary<SyntaxNode, IOperation> argument)
            {
                if (!operation.IsImplicit)
                {
                    // IOperation invariant is that all there is at most 1 non-implicit node per syntax node.
                    RoslynDebug.Assert(
                        !argument.ContainsKey(operation.Syntax),
                        $"Duplicate operation node for {operation.Syntax}. Existing node is {(argument.TryGetValue(operation.Syntax, out var original) ? (OperationKind?)original.Kind : null)}, new node is {operation.Kind}.");
                    argument.Add(operation.Syntax, operation);
                }
            }
        }
    }
}