Exemple #1
0
        private static bool IsElseIfOrElseClauseEquivalent(
            ISyntaxFactsService syntaxFacts,
            IBlockFactsService blockFacts,
            IIfLikeStatementGenerator ifGenerator,
            SyntaxNode elseIfOrElseClause1,
            SyntaxNode elseIfOrElseClause2)
        {
            // Compare Else/ElseIf clauses for equality.

            var isIfStatement = ifGenerator.IsIfOrElseIf(elseIfOrElseClause1);

            if (isIfStatement != ifGenerator.IsIfOrElseIf(elseIfOrElseClause2))
            {
                // If we have one Else and one ElseIf, they're not equal.
                return(false);
            }

            if (isIfStatement)
            {
                // If we have two ElseIf blocks, their conditions have to match.
                var condition1 = ifGenerator.GetCondition(elseIfOrElseClause1);
                var condition2 = ifGenerator.GetCondition(elseIfOrElseClause2);

                if (!syntaxFacts.AreEquivalent(condition1, condition2))
                {
                    return(false);
                }
            }

            var statements1 = WalkDownScopeBlocks(blockFacts, blockFacts.GetStatementContainerStatements(elseIfOrElseClause1));
            var statements2 = WalkDownScopeBlocks(blockFacts, blockFacts.GetStatementContainerStatements(elseIfOrElseClause2));

            return(statements1.SequenceEqual(statements2, syntaxFacts.AreEquivalent));
        }
        private static SyntaxRemoveOptions CreateSyntaxRemoveOptions(
            TLocalDeclarationStatement localDeclaration,
            IBlockFactsService blockFacts)
        {
            var removeOptions = SyntaxGenerator.DefaultRemoveOptions;

            if (localDeclaration != null)
            {
                if (localDeclaration.GetLeadingTrivia().Contains(t => t.IsDirective))
                {
                    removeOptions |= SyntaxRemoveOptions.KeepLeadingTrivia;
                }
                else
                {
                    var statementParent = localDeclaration.Parent;
                    if (blockFacts.IsExecutableBlock(statementParent))
                    {
                        var siblings = blockFacts.GetExecutableBlockStatements(statementParent);
                        var localDeclarationIndex = siblings.IndexOf(localDeclaration);
                        if (localDeclarationIndex != 0)
                        {
                            // if we're removing the first statement in a block, then we
                            // want to have the elastic marker on it so that the next statement
                            // properly formats with the space left behind.  But if it's
                            // not the first statement then just keep the trivia as is
                            // so that the statement before and after it stay appropriately
                            // spaced apart.
                            removeOptions &= ~SyntaxRemoveOptions.AddElasticMarker;
                        }
                    }
                }
            }

            return(removeOptions);
        }
Exemple #3
0
        private static bool IsFirstStatementIfStatement(
            IBlockFactsService blockFacts,
            IIfLikeStatementGenerator ifGenerator,
            SyntaxNode ifOrElseIf,
            out SyntaxNode ifStatement)
        {
            // Check whether the first statement inside an if or else if is an if statement.
            // If the if statement is inside a block, it has to be the first statement of the block.

            // An if or else if should always be a statement container, but we'll do a defensive check anyway.
            Debug.Assert(blockFacts.IsStatementContainer(ifOrElseIf));
            if (blockFacts.IsStatementContainer(ifOrElseIf))
            {
                var rootStatements = blockFacts.GetStatementContainerStatements(ifOrElseIf);

                var statements = WalkDownScopeBlocks(blockFacts, rootStatements);
                if (statements.Count > 0 && ifGenerator.IsIfOrElseIf(statements[0]))
                {
                    ifStatement = statements[0];
                    return(true);
                }
            }

            ifStatement = null;
            return(false);
        }
        protected static void RemoveNode(SyntaxEditor editor, SyntaxNode node, IBlockFactsService blockFacts)
        {
            var localDeclaration = node.GetAncestorOrThis <TLocalDeclarationStatement>();
            var removeOptions    = CreateSyntaxRemoveOptions(localDeclaration, blockFacts);

            editor.RemoveNode(node, removeOptions);
        }
Exemple #5
0
        private static bool IsFirstStatementOfIfOrElseIf(
            IBlockFactsService blockFacts,
            IIfLikeStatementGenerator ifGenerator,
            SyntaxNode statement,
            out SyntaxNode ifOrElseIf)
        {
            // Check whether the statement is a first statement inside an if or else if.
            // If it's inside a block, it has to be the first statement of the block.

            // We can't assume that a statement will always be in a statement container, because an if statement
            // in top level code will be in a GlobalStatement.
            if (blockFacts.IsStatementContainer(statement.Parent))
            {
                var statements = blockFacts.GetStatementContainerStatements(statement.Parent);
                if (statements.Count > 0 && statements[0] == statement)
                {
                    var rootStatements = WalkUpScopeBlocks(blockFacts, statements);
                    if (rootStatements.Count > 0 && ifGenerator.IsIfOrElseIf(rootStatements[0].Parent))
                    {
                        ifOrElseIf = rootStatements[0].Parent;
                        return(true);
                    }
                }
            }

            ifOrElseIf = null;
            return(false);
        }
 private static bool CanBeMergedWithElseIf(
     ISyntaxFactsService syntaxFacts,
     IBlockFactsService blockFacts,
     IIfLikeStatementGenerator ifGenerator,
     SyntaxNode ifOrElseIf,
     out SyntaxNode elseIfClause)
 {
     return(ifGenerator.HasElseIfClause(ifOrElseIf, out elseIfClause) &&
            ContainEquivalentStatements(syntaxFacts, blockFacts, ifOrElseIf, elseIfClause, out _));
 }
 private static bool CanBeMergedWithParent(
     ISyntaxFactsService syntaxFacts,
     IBlockFactsService blockFacts,
     IIfLikeStatementGenerator ifGenerator,
     SyntaxNode ifOrElseIf,
     out SyntaxNode parentIfOrElseIf)
 {
     return(ifGenerator.IsElseIfClause(ifOrElseIf, out parentIfOrElseIf) &&
            ContainEquivalentStatements(syntaxFacts, blockFacts, ifOrElseIf, parentIfOrElseIf, out _));
 }
        private static bool ContainEquivalentStatements(
            ISyntaxFactsService syntaxFacts,
            IBlockFactsService blockFacts,
            SyntaxNode ifStatement1,
            SyntaxNode ifStatement2,
            out IReadOnlyList <SyntaxNode> statements)
        {
            var statements1 = WalkDownScopeBlocks(blockFacts, blockFacts.GetStatementContainerStatements(ifStatement1));
            var statements2 = WalkDownScopeBlocks(blockFacts, blockFacts.GetStatementContainerStatements(ifStatement2));

            statements = statements1;
            return(statements1.SequenceEqual(statements2, syntaxFacts.AreEquivalent));
        }
 private static Task <bool> CanBeMergedWithNextStatementAsync(
     Document document,
     ISyntaxFactsService syntaxFacts,
     IBlockFactsService blockFacts,
     IIfLikeStatementGenerator ifGenerator,
     SyntaxNode ifOrElseIf,
     CancellationToken cancellationToken,
     out SyntaxNode nextStatement)
 {
     return(TryGetSiblingStatement(syntaxFacts, blockFacts, ifOrElseIf, relativeIndex: 1, out nextStatement)
         ? CanStatementsBeMergedAsync(document, syntaxFacts, blockFacts, ifGenerator, ifOrElseIf, nextStatement, cancellationToken)
         : SpecializedTasks.False);
 }
        protected override bool ShouldOfferFixForLocalDeclaration(IBlockFactsService blockFacts, SyntaxNode node)
        {
            // If the fix location is not for a local declaration then we can allow it (eg, when inside a for
            // or catch).
            if (node.Parent?.Parent is not LocalDeclarationStatementSyntax localDeclaration)
            {
                return(true);
            }

            // Local declarations must be parented by an executable block, or global statement, otherwise
            // removing them would be invalid (and more than likely crash the fixer)
            return(localDeclaration.Parent is GlobalStatementSyntax ||
                   blockFacts.IsExecutableBlock(localDeclaration.Parent));
        }
        private static async Task <bool> CanStatementsBeMergedAsync(
            Document document,
            ISyntaxFactsService syntaxFacts,
            IBlockFactsService blockFacts,
            IIfLikeStatementGenerator ifGenerator,
            SyntaxNode firstStatement,
            SyntaxNode secondStatement,
            CancellationToken cancellationToken)
        {
            // We don't support cases where the previous if statement has any else-if or else clauses. In order for that
            // to be mergable, the control flow would have to quit from inside every branch, which is getting a little complex.
            if (!ifGenerator.IsIfOrElseIf(firstStatement) || ifGenerator.GetElseIfAndElseClauses(firstStatement).Length > 0)
            {
                return(false);
            }

            if (!ifGenerator.IsIfOrElseIf(secondStatement))
            {
                return(false);
            }

            if (!ContainEquivalentStatements(syntaxFacts, blockFacts, firstStatement, secondStatement, out var insideStatements))
            {
                return(false);
            }

            if (insideStatements.Count == 0)
            {
                // Even though there are no statements inside, we still can't merge these into one statement
                // because it would change the semantics from always evaluating the second condition to short-circuiting.
                return(false);
            }
            else
            {
                // There are statements inside. We can merge these into one statement if
                // control flow can't reach the end of these statements (otherwise, it would change from running
                // the second 'if' in the case that both conditions are true to only running the statements once).
                // This will typically look like a single return, break, continue or a throw statement.

                var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

                var controlFlow = semanticModel.AnalyzeControlFlow(insideStatements[0], insideStatements[insideStatements.Count - 1]);

                return(!controlFlow.EndPointIsReachable);
            }
        }
        private static bool TryGetSiblingStatement(
            ISyntaxFactsService syntaxFacts,
            IBlockFactsService blockFacts,
            SyntaxNode ifOrElseIf,
            int relativeIndex,
            out SyntaxNode statement)
        {
            if (syntaxFacts.IsExecutableStatement(ifOrElseIf) &&
                blockFacts.IsExecutableBlock(ifOrElseIf.Parent))
            {
                var blockStatements = blockFacts.GetExecutableBlockStatements(ifOrElseIf.Parent);

                statement = blockStatements.ElementAtOrDefault(blockStatements.IndexOf(ifOrElseIf) + relativeIndex);
                return(statement != null);
            }

            statement = null;
            return(false);
        }
        private static async Task <bool> CanBeSeparateStatementsAsync(
            Document document,
            IBlockFactsService blockFacts,
            IIfLikeStatementGenerator ifGenerator,
            SyntaxNode ifOrElseIf,
            CancellationToken cancellationToken)
        {
            // In order to make separate statements, ifOrElseIf must be an if statement, not an else-if clause.
            if (ifGenerator.IsElseIfClause(ifOrElseIf, out _))
            {
                return(false);
            }

            // If there is an else clause, we *could* in theory separate these and move the current else clause to the second
            // statement, but we won't. It would break the else-if chain in an odd way. We'll insert an else-if instead.
            if (ifGenerator.GetElseIfAndElseClauses(ifOrElseIf).Length > 0)
            {
                return(false);
            }

            var insideStatements = blockFacts.GetStatementContainerStatements(ifOrElseIf);

            if (insideStatements.Count == 0)
            {
                // Even though there are no statements inside, we still can't split this into separate statements
                // because it would change the semantics from short-circuiting to always evaluating the second condition,
                // breaking code like 'if (a == null || a.InstanceMethod())'.
                return(false);
            }
            else
            {
                // There are statements inside. We can split this into separate statements and leave out the 'else' if
                // control flow can't reach the end of these statements (otherwise, it would continue to the second 'if'
                // and in the case that both conditions are true, run the same statements twice).
                // This will typically look like a single return, break, continue or a throw statement.

                var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

                var controlFlow = semanticModel.AnalyzeControlFlow(insideStatements[0], insideStatements[insideStatements.Count - 1]);

                return(!controlFlow.EndPointIsReachable);
            }
        }
        protected override void RemoveOrReplaceNode(SyntaxEditor editor, SyntaxNode node, IBlockFactsService blockFacts)
        {
            switch (node.Kind())
            {
            case SyntaxKind.SimpleAssignmentExpression:
                editor.ReplaceNode(node, ((AssignmentExpressionSyntax)node).Right);
                return;

            default:
                RemoveNode(editor, node.IsParentKind(SyntaxKind.GlobalStatement) ? node.Parent : node, blockFacts);
                return;
            }
        }
 protected abstract void RemoveOrReplaceNode(SyntaxEditor editor, SyntaxNode node, IBlockFactsService blockFacts);
Exemple #16
0
        private static async Task <bool> CanBeMergedAsync(
            Document document,
            ISyntaxFactsService syntaxFacts,
            IBlockFactsService blockFacts,
            IIfLikeStatementGenerator ifGenerator,
            SyntaxNode outerIfOrElseIf,
            SyntaxNode innerIfStatement,
            CancellationToken cancellationToken)
        {
            // We can only merge this with the outer if statement if any inner else-if and else clauses are equal
            // to else-if and else clauses following the outer if statement because we'll be removing the inner ones.
            // Example of what we can merge:
            //    if (a)
            //    {
            //        if (b)
            //            Console.WriteLine();
            //        else
            //            Foo();
            //    }
            //    else
            //    {
            //        Foo();
            //    }
            if (!System.Linq.ImmutableArrayExtensions.SequenceEqual(
                    ifGenerator.GetElseIfAndElseClauses(outerIfOrElseIf),
                    ifGenerator.GetElseIfAndElseClauses(innerIfStatement),
                    (a, b) => IsElseIfOrElseClauseEquivalent(syntaxFacts, blockFacts, ifGenerator, a, b)))
            {
                return(false);
            }

            var statements = blockFacts.GetStatementContainerStatements(innerIfStatement.Parent);

            if (statements.Count == 1)
            {
                // There are no other statements below the inner if statement. Merging is OK.
                return(true);
            }
            else
            {
                // There are statements below the inner if statement. We can merge if
                // 1. there are equivalent statements below the outer 'if', and
                // 2. control flow can't reach the end of these statements (otherwise, it would continue
                //    below the outer 'if' and run the same statements twice).
                // This will typically look like a single return, break, continue or a throw statement.
                // The opposite refactoring (SplitIntoNestedIfStatements) never generates this but we support it anyway.

                // Example:
                //    if (a)
                //    {
                //        if (b)
                //            Console.WriteLine();
                //        return;
                //    }
                //    return;

                // If we have an else-if, get the topmost if statement.
                var outerIfStatement = ifGenerator.GetRootIfStatement(outerIfOrElseIf);

                // A statement should always be in a statement container, but we'll do a defensive check anyway so that
                // we don't crash if the helper is missing some cases or there's a new language feature it didn't account for.
                Debug.Assert(blockFacts.GetStatementContainer(outerIfStatement) is object);
                if (blockFacts.GetStatementContainer(outerIfStatement) is not {
                } container)
                {
                    return(false);
                }

                var outerStatements       = blockFacts.GetStatementContainerStatements(container);
                var outerIfStatementIndex = outerStatements.IndexOf(outerIfStatement);

                var remainingStatements      = statements.Skip(1);
                var remainingOuterStatements = outerStatements.Skip(outerIfStatementIndex + 1);

                if (!remainingStatements.SequenceEqual(remainingOuterStatements.Take(statements.Count - 1), syntaxFacts.AreEquivalent))
                {
                    return(false);
                }

                var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

                var controlFlow = semanticModel.AnalyzeControlFlow(statements[0], statements[statements.Count - 1]);

                return(!controlFlow.EndPointIsReachable);
            }
        }
 protected abstract bool ShouldOfferFixForLocalDeclaration(IBlockFactsService blockFacts, SyntaxNode node);