File: FlowAnalysis\VariablesDeclaredWalker.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// 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.
 
#nullable disable
 
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System.Diagnostics;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// A region analysis walker that records declared variables.
    /// </summary>
    internal class VariablesDeclaredWalker : AbstractRegionControlFlowPass
    {
        internal static IEnumerable<Symbol> Analyze(CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion)
        {
            var walker = new VariablesDeclaredWalker(compilation, member, node, firstInRegion, lastInRegion);
            try
            {
                bool badRegion = false;
                walker.Analyze(ref badRegion);
                return badRegion ? SpecializedCollections.EmptyEnumerable<Symbol>() : walker._variablesDeclared;
            }
            finally
            {
                walker.Free();
            }
        }
 
        private HashSet<Symbol> _variablesDeclared = new HashSet<Symbol>();
 
        internal VariablesDeclaredWalker(CSharpCompilation compilation, Symbol member, BoundNode node, BoundNode firstInRegion, BoundNode lastInRegion)
            : base(compilation, member, node, firstInRegion, lastInRegion)
        {
        }
 
        protected override void Free()
        {
            base.Free();
            _variablesDeclared = null;
        }
 
        public override void VisitPattern(BoundPattern pattern)
        {
            base.VisitPattern(pattern);
            NoteDeclaredPatternVariables(pattern);
        }
 
        protected override void VisitSwitchSection(BoundSwitchSection node, bool isLastSection)
        {
            foreach (var label in node.SwitchLabels)
            {
                NoteDeclaredPatternVariables(label.Pattern);
            }
 
            base.VisitSwitchSection(node, isLastSection);
        }
 
        /// <summary>
        /// Record declared variables in the pattern.
        /// </summary>
        private void NoteDeclaredPatternVariables(BoundPattern pattern)
        {
            if (IsInside)
            {
                switch (pattern)
                {
                    case BoundObjectPattern p:
                        {
                            // The variable may be null if it is a discard designation `_`.
                            if (p.Variable?.Kind == SymbolKind.Local)
                            {
                                // Because this API only returns local symbols and parameters,
                                // we exclude pattern variables that have become fields in scripts.
                                _variablesDeclared.Add(p.Variable);
                            }
                        }
                        break;
                }
            }
        }
 
        public override BoundNode VisitLocalDeclaration(BoundLocalDeclaration node)
        {
            if (IsInside)
            {
                _variablesDeclared.Add(node.LocalSymbol);
            }
 
            return base.VisitLocalDeclaration(node);
        }
 
        public override BoundNode VisitLambda(BoundLambda node)
        {
            if (IsInside && !node.WasCompilerGenerated)
            {
                foreach (var parameter in node.Symbol.Parameters)
                {
                    _variablesDeclared.Add(parameter);
                }
            }
 
            return base.VisitLambda(node);
        }
 
        public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node)
        {
            if (IsInside && !node.WasCompilerGenerated)
            {
                foreach (var parameter in node.Symbol.Parameters)
                {
                    _variablesDeclared.Add(parameter);
                }
            }
 
            return base.VisitLocalFunctionStatement(node);
        }
 
        public override void VisitForEachIterationVariables(BoundForEachStatement node)
        {
            if (IsInside)
            {
                var deconstructionAssignment = node.DeconstructionOpt?.DeconstructionAssignment;
 
                if (deconstructionAssignment == null)
                {
                    _variablesDeclared.AddAll(node.IterationVariables);
                }
                else
                {
                    // Deconstruction foreach declares multiple variables.
                    ((BoundTupleExpression)deconstructionAssignment.Left).VisitAllElements((x, self) => self.Visit(x), this);
                }
            }
        }
 
        protected override void VisitCatchBlock(BoundCatchBlock catchBlock, ref LocalState finallyState)
        {
            if (IsInside)
            {
                var local = catchBlock.Locals.FirstOrDefault();
 
                if (local?.DeclarationKind == LocalDeclarationKind.CatchVariable)
                {
                    _variablesDeclared.Add(local);
                }
            }
 
            base.VisitCatchBlock(catchBlock, ref finallyState);
        }
 
        public override BoundNode VisitQueryClause(BoundQueryClause node)
        {
            if (IsInside)
            {
                if ((object)node.DefinedSymbol != null)
                {
                    _variablesDeclared.Add(node.DefinedSymbol);
                }
            }
 
            return base.VisitQueryClause(node);
        }
 
        protected override void VisitLvalue(BoundLocal node)
        {
            VisitLocal(node);
        }
 
        public override BoundNode VisitLocal(BoundLocal node)
        {
            if (IsInside && node.DeclarationKind != BoundLocalDeclarationKind.None)
            {
                _variablesDeclared.Add(node.LocalSymbol);
            }
 
            return null;
        }
    }
}