| File: FlowAnalysis\AbstractFlowPass_Switch.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; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { internal abstract partial class AbstractFlowPass<TLocalState, TLocalFunctionState> { public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { // dispatch to the switch sections var afterSwitchState = VisitSwitchStatementDispatch(node); // visit switch sections var switchSections = node.SwitchSections; var iLastSection = (switchSections.Length - 1); for (var iSection = 0; iSection <= iLastSection; iSection++) { VisitSwitchSection(switchSections[iSection], iSection == iLastSection); // 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). Join(ref afterSwitchState, ref this.State); } ResolveBreaks(afterSwitchState, node.BreakLabel); return null; } protected virtual TLocalState VisitSwitchStatementDispatch(BoundSwitchStatement node) { // visit switch header VisitRvalue(node.Expression); TLocalState initialState = this.State.Clone(); var reachableLabels = node.ReachabilityDecisionDag.ReachableLabels; foreach (var section in node.SwitchSections) { foreach (var label in section.SwitchLabels) { if (reachableLabels.Contains(label.Label) || label.HasErrors || label == node.DefaultLabel && node.Expression.ConstantValueOpt == null && IsTraditionalSwitch(node)) { SetState(initialState.Clone()); } else { SetUnreachable(); } VisitPattern(label.Pattern); SetState(StateWhenTrue); if (label.WhenClause != null) { VisitCondition(label.WhenClause); SetState(StateWhenTrue); } PendingBranches.Add(new PendingBranch(label, this.State, label.Label)); } } TLocalState afterSwitchState = UnreachableState(); if (node.ReachabilityDecisionDag.ReachableLabels.Contains(node.BreakLabel) || (node.DefaultLabel == null && node.Expression.ConstantValueOpt == null && IsTraditionalSwitch(node))) { Join(ref afterSwitchState, ref initialState); } return afterSwitchState; } /// <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 (compilation.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; } protected virtual void VisitSwitchSection(BoundSwitchSection node, bool isLastSection) { SetState(UnreachableState()); foreach (var label in node.SwitchLabels) { VisitLabel(label.Label, node); } VisitStatementList(node); } 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 VisitConvertedSwitchExpression(BoundConvertedSwitchExpression node) { return this.VisitSwitchExpression(node); } public override BoundNode VisitUnconvertedSwitchExpression(BoundUnconvertedSwitchExpression node) { return this.VisitSwitchExpression(node); } private BoundNode VisitSwitchExpression(BoundSwitchExpression node) { VisitRvalue(node.Expression); var dispatchState = this.State; var endState = UnreachableState(); var reachableLabels = node.ReachabilityDecisionDag.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; } } }