public static Doc Print(SwitchExpressionSyntax node) { return(Doc.Concat( Node.Print(node.GoverningExpression), " ", Token.Print(node.SwitchKeyword), Doc.HardLine, Token.Print(node.OpenBraceToken), Doc.Group( Doc.Indent( Doc.HardLine, SeparatedSyntaxList.Print( node.Arms, o => Doc.Concat( Node.Print(o.Pattern), " ", o.WhenClause != null ? Doc.Concat(Node.Print(o.WhenClause), " ") : Doc.Null, Token.PrintWithSuffix(o.EqualsGreaterThanToken, " "), Node.Print(o.Expression) ), Doc.HardLine ) ), Doc.HardLine ), Token.Print(node.CloseBraceToken) )); }
private ImmutableArray <BoundSwitchExpressionArm> BindSwitchExpressionArms( SwitchExpressionSyntax node, Binder originalBinder, BoundExpression inputExpression, BindingDiagnosticBag diagnostics ) { var builder = ArrayBuilder <BoundSwitchExpressionArm> .GetInstance(); (TypeSymbol inputType, uint valEscape) = GetInputTypeAndValEscape(inputExpression); foreach (var arm in node.Arms) { var armBinder = originalBinder.GetRequiredBinder(arm); Debug.Assert(inputExpression.Type is not null); var boundArm = armBinder.BindSwitchExpressionArm( arm, inputType, valEscape, diagnostics ); builder.Add(boundArm); } return(builder.ToImmutableAndFree()); }
public static void ComputeRefactorings(RefactoringContext context, SwitchExpressionSyntax switchExpression) { SeparatedSyntaxList <SwitchExpressionArmSyntax> arms = switchExpression.Arms; if (!context.Span.IsEmptyAndContainedInSpan(switchExpression.SwitchKeyword)) { return; } if (!switchExpression.IsParentKind(SyntaxKind.ReturnStatement)) { return; } if (switchExpression.GoverningExpression.IsMissing) { return; } if (!arms.Any()) { return; } if (!arms.All(f => f.Pattern.IsKind(SyntaxKind.ConstantPattern, SyntaxKind.DiscardPattern))) { return; } context.RegisterRefactoring( "Convert to 'switch' statement", ct => ConvertSwitchExpressionToSwitchStatement(context.Document, switchExpression, ct), RefactoringIdentifiers.ConvertSwitchExpressionToSwitchStatement); }
internal override BoundExpression BindSwitchExpressionCore( SwitchExpressionSyntax node, Binder originalBinder, BindingDiagnosticBag diagnostics ) { // There's supposed to be a SwitchExpressionBinder (or other overrider of this method) in the chain. throw ExceptionUtilities.Unreachable; }
private static Task <Document> ConvertSwitchExpressionToSwitchStatement( Document document, SwitchExpressionSyntax switchExpression, CancellationToken cancellationToken) { IEnumerable <SwitchSectionSyntax> sections = switchExpression.Arms.Select((arm, i) => { SyntaxToken separator = switchExpression.Arms.GetSeparator(i); SyntaxToken semicolon = Token(SyntaxKind.SemicolonToken); if (!separator.IsMissing) { semicolon = semicolon.WithTriviaFrom(separator); } PatternSyntax pattern = arm.Pattern; switch (pattern.Kind()) { case SyntaxKind.ConstantPattern: { CaseSwitchLabelSyntax label = CaseSwitchLabel( Token(SyntaxKind.CaseKeyword).WithLeadingTrivia(pattern.GetLeadingTrivia()), ((ConstantPatternSyntax)pattern).Expression.WithoutLeadingTrivia(), Token(SyntaxKind.ColonToken).WithTriviaFrom(arm.EqualsGreaterThanToken)); return(SwitchSection(label, CreateStatement(arm.Expression, semicolon))); } case SyntaxKind.DiscardPattern: { DefaultSwitchLabelSyntax label = DefaultSwitchLabel(Token(SyntaxKind.DefaultKeyword), Token(SyntaxKind.ColonToken)); return(SwitchSection(label, CreateStatement(arm.Expression, semicolon))); } default: { throw new InvalidOperationException(); } } }); var returnStatement = (ReturnStatementSyntax)switchExpression.Parent; SwitchStatementSyntax switchStatement = SwitchStatement( switchExpression.SwitchKeyword.WithTriviaFrom(returnStatement.ReturnKeyword), OpenParenToken(), switchExpression.GoverningExpression, CloseParenToken().WithTrailingTrivia(switchExpression.SwitchKeyword.TrailingTrivia), switchExpression.OpenBraceToken, sections.ToSyntaxList(), switchExpression.CloseBraceToken); switchStatement = switchStatement.WithFormatterAnnotation(); return(document.ReplaceNodeAsync(returnStatement, switchStatement, cancellationToken));
private ImmutableArray <BoundSwitchExpressionArm> BindSwitchExpressionArms(SwitchExpressionSyntax node, Binder originalBinder, DiagnosticBag diagnostics) { bool hasErrors = InputExpression.HasErrors; var builder = ArrayBuilder <BoundSwitchExpressionArm> .GetInstance(); foreach (var arm in node.Arms) { var armBinder = originalBinder.GetBinder(arm); var boundArm = armBinder.BindSwitchExpressionArm(arm, diagnostics); builder.Add(boundArm); } return(builder.ToImmutableAndFree()); }
internal override BoundExpression BindSwitchExpressionCore(SwitchExpressionSyntax node, Binder originalBinder, BindingDiagnosticBag diagnostics) { Debug.Assert(node == SwitchExpressionSyntax); // Bind switch expression and set the switch governing type. var boundInputExpression = BindSwitchGoverningExpression(diagnostics); ImmutableArray <BoundSwitchExpressionArm> switchArms = BindSwitchExpressionArms(node, originalBinder, boundInputExpression, diagnostics); TypeSymbol?naturalType = InferResultType(switchArms, diagnostics); bool reportedNotExhaustive = CheckSwitchExpressionExhaustive(node, boundInputExpression, switchArms, out BoundDecisionDag decisionDag, out LabelSymbol? defaultLabel, 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(boundInputExpression); return(new BoundUnconvertedSwitchExpression( node, boundInputExpression, switchArms, decisionDag, defaultLabel: defaultLabel, reportedNotExhaustive: reportedNotExhaustive, type: naturalType)); }
public static void Analyze( SyntaxNodeAnalysisContext context, SwitchExpressionSyntax switchExpression) { var switchKind = IsExhaustive(context, switchExpression); if (!switchKind.IsExhaustive) { return; } ReportWhenGuardNotSupported(context, switchExpression); var switchOnType = context.GetExpressionType(switchExpression.GoverningExpression); if (switchOnType != null && switchOnType.IsEnum(context, out var enumType, out var nullable)) { AnalyzeSwitchOnEnum(context, switchExpression, enumType, nullable); }
public override void VisitSwitchExpression(SwitchExpressionSyntax node) { var switchExpressionBinder = new SwitchExpressionBinder(node, _enclosing); AddToMap(node, switchExpressionBinder); Visit(node.GoverningExpression, switchExpressionBinder); foreach (SwitchExpressionArmSyntax arm in node.Arms) { var armScopeBinder = new ExpressionVariableBinder(arm, switchExpressionBinder); var armBinder = new SwitchExpressionArmBinder(arm, armScopeBinder, switchExpressionBinder); AddToMap(arm, armBinder); Visit(arm.Pattern, armBinder); if (arm.WhenClause != null) { Visit(arm.WhenClause, armBinder); } Visit(arm.Expression, armBinder); } }
private Doc PrintSwitchExpressionSyntax(SwitchExpressionSyntax node) { return(Concat( this.Print(node.GoverningExpression), SpaceIfNoPreviousComment, this.PrintSyntaxToken(node.SwitchKeyword), HardLine, this.PrintSyntaxToken(node.OpenBraceToken), Group( Indent( Concat( HardLine, this.PrintSeparatedSyntaxList( node.Arms, o => Concat( this.Print(o.Pattern), SpaceIfNoPreviousComment, o.WhenClause != null ? Concat( this.Print(o.WhenClause), SpaceIfNoPreviousComment ) : Doc.Null, this.PrintSyntaxToken( o.EqualsGreaterThanToken, " " ), this.Print(o.Expression) ), HardLine ) ) ), HardLine ), this.PrintSyntaxToken(node.CloseBraceToken) )); }
internal SwitchExpressionBinder(SwitchExpressionSyntax switchExpressionSyntax, Binder next) : base(next) { SwitchExpressionSyntax = switchExpressionSyntax; }
/// <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)); } } }
public override void VisitSwitchExpression(SwitchExpressionSyntax node) { Log(node, "Unsupported Syntax !"); }
public override void VisitSwitchExpression(SwitchExpressionSyntax node) { Visit(node.GoverningExpression); // Each case has its own scope. }
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)); } } }
public override void VisitSwitchExpression(SwitchExpressionSyntax node) { this.VisitExpression(node); }
private static Task <Document> ConvertSwitchExpressionToSwitchStatement( Document document, SwitchExpressionSyntax switchExpression, CancellationToken cancellationToken) { SeparatedSyntaxList <SwitchExpressionArmSyntax> arms = switchExpression.Arms; SyntaxToken[] separators = arms.GetSeparators().ToArray(); IEnumerable <SwitchSectionSyntax> sections = arms.Select((arm, i) => { PatternSyntax pattern = arm.Pattern; ExpressionSyntax expression = arm.Expression; SyntaxToken semicolon = Token(SyntaxKind.SemicolonToken); SyntaxToken separator = default; if (i < separators.Length) { separator = separators[i]; } if (separator.IsKind(SyntaxKind.None) || separator.IsMissing) { semicolon = semicolon.WithTrailingTrivia(arm.GetTrailingTrivia()); expression = expression.WithoutTrailingTrivia(); } else { semicolon = semicolon.WithTriviaFrom(separator); } SyntaxKind kind = pattern.Kind(); SwitchLabelSyntax label = default; if (kind == SyntaxKind.ConstantPattern) { label = CaseSwitchLabel( Token(SyntaxKind.CaseKeyword).WithLeadingTrivia(pattern.GetLeadingTrivia()), ((ConstantPatternSyntax)pattern).Expression.WithoutLeadingTrivia(), Token(SyntaxKind.ColonToken).WithTriviaFrom(arm.EqualsGreaterThanToken)); } else if (kind == SyntaxKind.DiscardPattern) { label = DefaultSwitchLabel(Token(SyntaxKind.DefaultKeyword), Token(SyntaxKind.ColonToken)); } else { throw new InvalidOperationException(); } StatementSyntax statement = CreateStatement(expression, semicolon); return(SwitchSection(label, statement)); }); var returnStatement = (ReturnStatementSyntax)switchExpression.Parent; SwitchStatementSyntax switchStatement = SwitchStatement( switchExpression.SwitchKeyword.WithTriviaFrom(returnStatement.ReturnKeyword), OpenParenToken(), switchExpression.GoverningExpression, CloseParenToken().WithTrailingTrivia(switchExpression.SwitchKeyword.TrailingTrivia), switchExpression.OpenBraceToken, sections.ToSyntaxList(), switchExpression.CloseBraceToken); switchStatement = switchStatement.WithFormatterAnnotation(); return(document.ReplaceNodeAsync(returnStatement, switchStatement, cancellationToken));