Beispiel #1
0
 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)
                ));
 }
Beispiel #2
0
        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());
        }
Beispiel #3
0
        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);
        }
Beispiel #4
0
 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;
 }
Beispiel #5
0
        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));
Beispiel #6
0
        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));
        }
Beispiel #8
0
        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);
            }
Beispiel #9
0
        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)
                ));
 }
Beispiel #11
0
 internal SwitchExpressionBinder(SwitchExpressionSyntax switchExpressionSyntax, Binder next)
     : base(next)
 {
     SwitchExpressionSyntax = switchExpressionSyntax;
 }
Beispiel #12
0
        /// <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.
 }
Beispiel #15
0
        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));
                }
            }
        }
Beispiel #16
0
 public override void VisitSwitchExpression(SwitchExpressionSyntax node)
 {
     this.VisitExpression(node);
 }
Beispiel #17
0
        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));