| File: Lowering\UnmatchedGotoFinder.cs | Web Access |
| Project: src\roslyn\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.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { /// <summary> /// Compiles a list of all labels that are targeted by gotos within a /// node, but are not declared within the node. /// </summary> internal sealed class UnmatchedGotoFinder : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator { private readonly Dictionary<BoundNode, HashSet<LabelSymbol>> _unmatchedLabelsCache; // NB: never modified. private HashSet<LabelSymbol> _gotos; private HashSet<LabelSymbol> _targets; private UnmatchedGotoFinder(Dictionary<BoundNode, HashSet<LabelSymbol>> unmatchedLabelsCache, int recursionDepth) : base(recursionDepth) { Debug.Assert(unmatchedLabelsCache != null); _unmatchedLabelsCache = unmatchedLabelsCache; } public static HashSet<LabelSymbol> Find(BoundNode node, Dictionary<BoundNode, HashSet<LabelSymbol>> unmatchedLabelsCache, int recursionDepth) { UnmatchedGotoFinder finder = new UnmatchedGotoFinder(unmatchedLabelsCache, recursionDepth); finder.Visit(node); HashSet<LabelSymbol> gotos = finder._gotos; HashSet<LabelSymbol> targets = finder._targets; if (gotos != null && targets != null) { gotos.RemoveAll(targets); } return gotos; } public override BoundNode Visit(BoundNode node) { HashSet<LabelSymbol> unmatched; if (node != null && _unmatchedLabelsCache.TryGetValue(node, out unmatched)) { if (unmatched != null) { foreach (LabelSymbol label in unmatched) { AddGoto(label); } } return null; // Don't visit children. } return base.Visit(node); } public override BoundNode VisitGotoStatement(BoundGotoStatement node) { AddGoto(node.Label); return base.VisitGotoStatement(node); } public override BoundNode VisitConditionalGoto(BoundConditionalGoto node) { AddGoto(node.Label); return base.VisitConditionalGoto(node); } public override BoundNode VisitSwitchDispatch(BoundSwitchDispatch node) { AddGoto(node.DefaultLabel); foreach ((_, LabelSymbol label) in node.Cases) { AddGoto(label); } return base.VisitSwitchDispatch(node); } public override BoundNode VisitLabelStatement(BoundLabelStatement node) { AddTarget(node.Label); return base.VisitLabelStatement(node); } public override BoundNode VisitLabeledStatement(BoundLabeledStatement node) { AddTarget(node.Label); return base.VisitLabeledStatement(node); } private void AddGoto(LabelSymbol label) { if (_gotos == null) { _gotos = new HashSet<LabelSymbol>(); } _gotos.Add(label); } private void AddTarget(LabelSymbol label) { if (_targets == null) { _targets = new HashSet<LabelSymbol>(); } _targets.Add(label); } } }