AbstractFlowPass_Switch.cs 6.3 KB
Newer Older
1 2
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

3
using System;
4
using Microsoft.CodeAnalysis.CSharp.Symbols;
5
using Microsoft.CodeAnalysis.CSharp.Syntax;
6
using Microsoft.CodeAnalysis.PooledObjects;
7 8 9 10 11
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{
A
Andy Gocke 已提交
12
    internal abstract partial class AbstractFlowPass<TLocalState>
13
    {
14
        public override BoundNode VisitSwitchStatement(BoundSwitchStatement node)
15 16
        {
            // visit switch header
17
            VisitRvalue(node.Expression);
18 19

            // visit switch block
20
            VisitSwitchBlock(node);
21 22 23 24

            return null;
        }

25
        private void VisitSwitchBlock(BoundSwitchStatement node)
26 27
        {
            var initialState = State.Clone();
N
Neal Gafter 已提交
28
            var reachableLabels = node.DecisionDag.ReachableLabels;
29 30 31 32
            foreach (var section in node.SwitchSections)
            {
                foreach (var label in section.SwitchLabels)
                {
33 34
                    if (reachableLabels.Contains(label.Label) || label.HasErrors ||
                        label == node.DefaultLabel && node.Expression.ConstantValue == null && IsTraditionalSwitch(node))
35 36 37 38 39 40 41 42
                    {
                        SetState(initialState.Clone());
                    }
                    else
                    {
                        SetUnreachable();
                    }

43
                    VisitPattern(label.Pattern);
44
                    SetState(StateWhenTrue);
N
Neal Gafter 已提交
45
                    if (label.WhenClause != null)
46
                    {
N
Neal Gafter 已提交
47
                        VisitCondition(label.WhenClause);
48 49
                        SetState(StateWhenTrue);
                    }
50

N
Neal Gafter 已提交
51
                    PendingBranches.Add(new PendingBranch(label, this.State, label.Label));
52 53 54 55 56 57 58 59 60
                }
            }

            // visit switch sections
            var afterSwitchState = UnreachableState();
            var switchSections = node.SwitchSections;
            var iLastSection = (switchSections.Length - 1);
            for (var iSection = 0; iSection <= iLastSection; iSection++)
            {
61
                VisitSwitchSection(switchSections[iSection], iSection == iLastSection);
62 63
                // Even though it is illegal for the end of a switch section to be reachable, in erroneous
                // code it may be reachable.  We treat that as an implicit break (branch to afterSwitchState).
A
Andy Gocke 已提交
64
                Join(ref afterSwitchState, ref this.State);
65 66
            }

67
            if (reachableLabels.Contains(node.BreakLabel) || node.DefaultLabel == null && IsTraditionalSwitch(node))
68
            {
N
Neal Gafter 已提交
69
                Join(ref afterSwitchState, ref initialState);
70
            }
71

72
            ResolveBreaks(afterSwitchState, node.BreakLabel);
73 74
        }

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
        /// <summary>
        /// Is the switch statement one that could be interpreted as a C# 6 or earlier switch statement?
        /// </summary>
        private bool IsTraditionalSwitch(BoundSwitchStatement node)
        {
            // Before recursive patterns were introduced, we did not consider handling both 'true' and 'false' to
            // completely handle all case of a switch on a bool unless there was some patterny syntax or semantics
            // in the switch.  We had two different bound nodes and separate flow analysis handling for
            // "traditional" switch statements and "pattern-based" switch statements.  We simulate that behavior
            // by testing to see if this switch would have been handled under the old rules by the old compiler.

            // If we are in a recent enough language version, we treat the switch as a fully pattern-based switch
            // for the purposes of flow analysis.
            if (((CSharpParseOptions)node.Syntax.SyntaxTree.Options).LanguageVersion >= MessageID.IDS_FeatureRecursivePatterns.RequiredVersion())
            {
                return false;
            }

            if (!node.Expression.Type.IsValidV6SwitchGoverningType())
            {
                return false;
            }

            foreach (var sectionSyntax in ((SwitchStatementSyntax)node.Syntax).Sections)
            {
                foreach (var label in sectionSyntax.Labels)
                {
                    if (label.Kind() == SyntaxKind.CasePatternSwitchLabel)
                    {
                        return false;
                    }
                }
            }

            return true;
        }

112
        protected virtual void VisitSwitchSection(BoundSwitchSection node, bool isLastSection)
113 114 115 116 117 118 119 120 121
        {
            SetState(UnreachableState());
            foreach (var label in node.SwitchLabels)
            {
                VisitLabel(label.Label, node);
            }

            VisitStatementList(node);
        }
N
Neal Gafter 已提交
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

        public override BoundNode VisitSwitchDispatch(BoundSwitchDispatch node)
        {
            VisitRvalue(node.Expression);
            var state = this.State.Clone();
            PendingBranches.Add(new PendingBranch(node, state, node.DefaultLabel));
            foreach ((_, LabelSymbol label) in node.Cases)
            {
                PendingBranches.Add(new PendingBranch(node, state, label));
            }

            SetUnreachable();
            return null;
        }

        public override BoundNode VisitSwitchExpression(BoundSwitchExpression node)
        {
            VisitRvalue(node.Expression);
            var dispatchState = this.State;
            var endState = UnreachableState();
            var reachableLabels = node.DecisionDag.ReachableLabels;
            foreach (var arm in node.SwitchArms)
            {
                SetState(dispatchState.Clone());
                VisitPattern(arm.Pattern);
                SetState(StateWhenTrue);
                if (!reachableLabels.Contains(arm.Label) || arm.Pattern.HasErrors)
                {
                    SetUnreachable();
                }

                if (arm.WhenClause != null)
                {
                    VisitCondition(arm.WhenClause);
                    SetState(StateWhenTrue);
                }

                VisitRvalue(arm.Value);
                Join(ref endState, ref this.State);
            }

            SetState(endState);
            return node;
        }
166 167
    }
}