internal override BoundStatement BindSwitchStatementCore(SwitchStatementSyntax node, Binder originalBinder, BindingDiagnosticBag diagnostics) { Debug.Assert(SwitchSyntax.Equals(node)); if (node.Sections.Count == 0) { diagnostics.Add(ErrorCode.WRN_EmptySwitch, node.OpenBraceToken.GetLocation()); } // Bind switch expression and set the switch governing type. BoundExpression boundSwitchGoverningExpression = SwitchGoverningExpression; diagnostics.AddRange(SwitchGoverningDiagnostics, allowMismatchInDependencyAccumulation: true); ImmutableArray <BoundSwitchSection> switchSections = BindSwitchSections(originalBinder, diagnostics, out BoundSwitchLabel defaultLabel); ImmutableArray <LocalSymbol> locals = GetDeclaredLocalsForScope(node); ImmutableArray <LocalFunctionSymbol> functions = GetDeclaredLocalFunctionsForScope(node); BoundDecisionDag decisionDag = DecisionDagBuilder.CreateDecisionDagForSwitchStatement( compilation: this.Compilation, syntax: node, switchGoverningExpression: boundSwitchGoverningExpression, switchSections: switchSections, // If there is no explicit default label, the default action is to break out of the switch defaultLabel: defaultLabel?.Label ?? BreakLabel, diagnostics); // Report subsumption errors, but ignore the input's constant value for that. CheckSwitchErrors(node, boundSwitchGoverningExpression, ref switchSections, decisionDag, diagnostics); // When the input is constant, we use that to reshape the decision dag that is returned // so that flow analysis will see that some of the cases may be unreachable. decisionDag = decisionDag.SimplifyDecisionDagIfConstantInput(boundSwitchGoverningExpression); return(new BoundSwitchStatement( syntax: node, expression: boundSwitchGoverningExpression, innerLocals: locals, innerLocalFunctions: functions, switchSections: switchSections, defaultLabel: defaultLabel, breakLabel: this.BreakLabel, reachabilityDecisionDag: decisionDag)); }
public BoundDecisionDag GetDecisionDagForLowering(CSharpCompilation compilation) { BoundDecisionDag decisionDag = this.ReachabilityDecisionDag; if (decisionDag.ContainsAnySynthesizedNodes()) { decisionDag = DecisionDagBuilder.CreateDecisionDagForSwitchStatement( compilation, this.Syntax, this.Expression, this.SwitchSections, this.DefaultLabel?.Label ?? this.BreakLabel, BindingDiagnosticBag.Discarded, forLowering: true); Debug.Assert(!decisionDag.ContainsAnySynthesizedNodes()); } return(decisionDag); }
public BoundDecisionDag GetDecisionDagForLowering(CSharpCompilation compilation) { BoundDecisionDag decisionDag = this.ReachabilityDecisionDag; if (decisionDag.ContainsAnySynthesizedNodes()) { decisionDag = DecisionDagBuilder.CreateDecisionDagForIsPattern( compilation, this.Syntax, this.Expression, this.Pattern, this.WhenTrueLabel, this.WhenFalseLabel, BindingDiagnosticBag.Discarded, forLowering: true); Debug.Assert(!decisionDag.ContainsAnySynthesizedNodes()); } return(decisionDag); }
public BoundDecisionDag GetDecisionDagForLowering(CSharpCompilation compilation, out LabelSymbol?defaultLabel) { defaultLabel = this.DefaultLabel; BoundDecisionDag decisionDag = this.ReachabilityDecisionDag; if (decisionDag.ContainsAnySynthesizedNodes()) { decisionDag = DecisionDagBuilder.CreateDecisionDagForSwitchExpression( compilation, this.Syntax, this.Expression, this.SwitchArms, // there's no default label if the original switch is exhaustive. // we generate a new label here because the new dag might not be. defaultLabel ??= new GeneratedLabelSymbol("default"), BindingDiagnosticBag.Discarded, forLowering: true); Debug.Assert(!decisionDag.ContainsAnySynthesizedNodes()); } return(decisionDag); }
/// <summary> /// Build the decision dag, giving an error if some cases are subsumed and a warning if the switch expression is not exhaustive. /// </summary> /// <param name="node"></param> /// <param name="boundInputExpression"></param> /// <param name="switchArms"></param> /// <param name="decisionDag"></param> /// <param name="diagnostics"></param> /// <returns>true if there was a non-exhaustive warning reported</returns> private bool CheckSwitchExpressionExhaustive( SwitchExpressionSyntax node, BoundExpression boundInputExpression, ImmutableArray <BoundSwitchExpressionArm> switchArms, out BoundDecisionDag decisionDag, out LabelSymbol defaultLabel, DiagnosticBag diagnostics) { defaultLabel = new GeneratedLabelSymbol("default"); decisionDag = DecisionDagBuilder.CreateDecisionDagForSwitchExpression(this.Compilation, node, boundInputExpression, switchArms, defaultLabel, diagnostics); var reachableLabels = decisionDag.ReachableLabels; foreach (BoundSwitchExpressionArm arm in switchArms) { if (!reachableLabels.Contains(arm.Label)) { diagnostics.Add(ErrorCode.ERR_SwitchArmSubsumed, arm.Pattern.Syntax.Location); } } if (!reachableLabels.Contains(defaultLabel)) { // switch expression is exhaustive; no default label needed. defaultLabel = null; return(false); } // We only report exhaustive warnings when the default label is reachable through some series of // tests that do not include a test in which the value is known to be null. Handling paths with // nulls is the job of the nullable walker. foreach (var n in TopologicalSort.IterativeSort <BoundDecisionDagNode>(new[] { decisionDag.RootNode }, nonNullSuccessors)) { if (n is BoundLeafDecisionDagNode leaf && leaf.Label == defaultLabel) { diagnostics.Add(ErrorCode.WRN_SwitchExpressionNotExhaustive, node.SwitchKeyword.GetLocation()); return(true); } } return(false); ImmutableArray <BoundDecisionDagNode> nonNullSuccessors(BoundDecisionDagNode n) { switch (n) { case BoundTestDecisionDagNode p: switch (p.Test) { case BoundDagNonNullTest t: // checks that the input is not null return(ImmutableArray.Create(p.WhenTrue)); case BoundDagExplicitNullTest t: // checks that the input is null return(ImmutableArray.Create(p.WhenFalse)); default: return(BoundDecisionDag.Successors(n)); } default: return(BoundDecisionDag.Successors(n)); } } }
private bool CheckSwitchExpressionExhaustive( SwitchExpressionSyntax node, BoundExpression boundInputExpression, ImmutableArray <BoundSwitchExpressionArm> switchArms, out BoundDecisionDag decisionDag, out LabelSymbol defaultLabel, BindingDiagnosticBag diagnostics) { defaultLabel = new GeneratedLabelSymbol("default"); decisionDag = DecisionDagBuilder.CreateDecisionDagForSwitchExpression(this.Compilation, node, boundInputExpression, switchArms, defaultLabel, diagnostics); var reachableLabels = decisionDag.ReachableLabels; bool hasErrors = false; foreach (BoundSwitchExpressionArm arm in switchArms) { hasErrors |= arm.HasErrors; if (!hasErrors && !reachableLabels.Contains(arm.Label)) { diagnostics.Add(ErrorCode.ERR_SwitchArmSubsumed, arm.Pattern.Syntax.Location); } } if (!reachableLabels.Contains(defaultLabel)) { // switch expression is exhaustive; no default label needed. defaultLabel = null; return(false); } if (hasErrors) { return(true); } // We only report exhaustive warnings when the default label is reachable through some series of // tests that do not include a test in which the value is known to be null. Handling paths with // nulls is the job of the nullable walker. bool wasAcyclic = TopologicalSort.TryIterativeSort <BoundDecisionDagNode>(new[] { decisionDag.RootNode }, nonNullSuccessors, out var nodes); // Since decisionDag.RootNode is acyclic by construction, its subset of nodes sorted here cannot be cyclic Debug.Assert(wasAcyclic); foreach (var n in nodes) { if (n is BoundLeafDecisionDagNode leaf && leaf.Label == defaultLabel) { var samplePattern = PatternExplainer.SamplePatternForPathToDagNode( BoundDagTemp.ForOriginalInput(boundInputExpression), nodes, n, nullPaths: false, out bool requiresFalseWhenClause, out bool unnamedEnumValue); ErrorCode warningCode = requiresFalseWhenClause ? ErrorCode.WRN_SwitchExpressionNotExhaustiveWithWhen : unnamedEnumValue ? ErrorCode.WRN_SwitchExpressionNotExhaustiveWithUnnamedEnumValue : ErrorCode.WRN_SwitchExpressionNotExhaustive; diagnostics.Add( warningCode, node.SwitchKeyword.GetLocation(), samplePattern); return(true); } } return(false); ImmutableArray <BoundDecisionDagNode> nonNullSuccessors(BoundDecisionDagNode n) { switch (n) { case BoundTestDecisionDagNode p: switch (p.Test) { case BoundDagNonNullTest t: // checks that the input is not null return(ImmutableArray.Create(p.WhenTrue)); case BoundDagExplicitNullTest t: // checks that the input is null return(ImmutableArray.Create(p.WhenFalse)); default: return(BoundDecisionDag.Successors(n)); } default: return(BoundDecisionDag.Successors(n)); } } }