File: SplitOrMergeIfStatements\CSharpIfLikeStatementGenerator.cs
Web Access
Project: src\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.Features)
// 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 System.Collections.Immutable;
using System.Composition;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.SplitOrMergeIfStatements;
 
namespace Microsoft.CodeAnalysis.CSharp.SplitOrMergeIfStatements;
 
using static SyntaxFactory;
 
[ExportLanguageService(typeof(IIfLikeStatementGenerator), LanguageNames.CSharp), Shared]
internal sealed class CSharpIfLikeStatementGenerator : IIfLikeStatementGenerator
{
    [ImportingConstructor]
    [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
    public CSharpIfLikeStatementGenerator()
    {
    }
 
    public bool IsIfOrElseIf(SyntaxNode node) => node is IfStatementSyntax;
 
    public bool IsCondition(SyntaxNode expression, out SyntaxNode ifOrElseIf)
    {
        if (expression.Parent is IfStatementSyntax ifStatement && ifStatement.Condition == expression)
        {
            ifOrElseIf = ifStatement;
            return true;
        }
 
        ifOrElseIf = null;
        return false;
    }
 
    public bool IsElseIfClause(SyntaxNode node, out SyntaxNode parentIfOrElseIf)
    {
        if (node is IfStatementSyntax && node.Parent is ElseClauseSyntax)
        {
            parentIfOrElseIf = (IfStatementSyntax)node.Parent.Parent;
            return true;
        }
 
        parentIfOrElseIf = null;
        return false;
    }
 
    public bool HasElseIfClause(SyntaxNode ifOrElseIf, out SyntaxNode elseIfClause)
    {
        var ifStatement = (IfStatementSyntax)ifOrElseIf;
        if (ifStatement.Else?.Statement is IfStatementSyntax elseIfStatement)
        {
            elseIfClause = elseIfStatement;
            return true;
        }
 
        elseIfClause = null;
        return false;
    }
 
    public SyntaxNode GetCondition(SyntaxNode ifOrElseIf)
    {
        var ifStatement = (IfStatementSyntax)ifOrElseIf;
        return ifStatement.Condition;
    }
 
    public SyntaxNode GetRootIfStatement(SyntaxNode ifOrElseIf)
    {
        var ifStatement = (IfStatementSyntax)ifOrElseIf;
 
        while (ifStatement.Parent is ElseClauseSyntax elseClause)
        {
            ifStatement = (IfStatementSyntax)elseClause.Parent;
        }
 
        return ifStatement;
    }
 
    public ImmutableArray<SyntaxNode> GetElseIfAndElseClauses(SyntaxNode ifOrElseIf)
    {
        var ifStatement = (IfStatementSyntax)ifOrElseIf;
 
        var builder = ImmutableArray.CreateBuilder<SyntaxNode>();
 
        while (ifStatement.Else?.Statement is IfStatementSyntax elseIfStatement)
        {
            builder.Add(elseIfStatement);
            ifStatement = elseIfStatement;
        }
 
        if (ifStatement.Else != null)
        {
            builder.Add(ifStatement.Else);
        }
 
        return builder.ToImmutable();
    }
 
    public SyntaxNode WithCondition(SyntaxNode ifOrElseIf, SyntaxNode condition)
    {
        var ifStatement = (IfStatementSyntax)ifOrElseIf;
        return ifStatement.WithCondition((ExpressionSyntax)condition);
    }
 
    public SyntaxNode WithStatementInBlock(SyntaxNode ifOrElseIf, SyntaxNode statement)
    {
        var ifStatement = (IfStatementSyntax)ifOrElseIf;
        return ifStatement.WithStatement(Block((StatementSyntax)statement));
    }
 
    public SyntaxNode WithStatementsOf(SyntaxNode ifOrElseIf, SyntaxNode otherIfOrElseIf)
    {
        var ifStatement = (IfStatementSyntax)ifOrElseIf;
        var otherIfStatement = (IfStatementSyntax)otherIfOrElseIf;
        return ifStatement.WithStatement(otherIfStatement.Statement);
    }
 
    public SyntaxNode WithElseIfAndElseClausesOf(SyntaxNode ifStatement, SyntaxNode otherIfStatement)
        => ((IfStatementSyntax)ifStatement).WithElse(((IfStatementSyntax)otherIfStatement).Else);
 
    public SyntaxNode ToIfStatement(SyntaxNode ifOrElseIf)
        => ifOrElseIf;
 
    public SyntaxNode ToElseIfClause(SyntaxNode ifOrElseIf)
        => ((IfStatementSyntax)ifOrElseIf).WithElse(null);
 
    public void InsertElseIfClause(SyntaxEditor editor, SyntaxNode afterIfOrElseIf, SyntaxNode elseIfClause)
    {
        editor.ReplaceNode(afterIfOrElseIf, (currentNode, _) =>
        {
            var ifStatement = (IfStatementSyntax)currentNode;
            var elseIfStatement = (IfStatementSyntax)elseIfClause;
 
            var newElseIfStatement = elseIfStatement.WithElse(ifStatement.Else);
            var newIfStatement = ifStatement.WithElse(ElseClause(newElseIfStatement));
 
            if (ifStatement.Else == null && ContainsEmbeddedIfStatement(ifStatement))
            {
                // If the if statement contains an embedded if statement (not wrapped inside a block), adding an else
                // clause might introduce a dangling else problem (the 'else' would bind to the inner if statement),
                // so if there used to be no else clause, we'll insert a new block to prevent that.
                newIfStatement = newIfStatement.WithStatement(Block(newIfStatement.Statement));
            }
 
            return newIfStatement;
        });
    }
 
    public void RemoveElseIfClause(SyntaxEditor editor, SyntaxNode elseIfClause)
    {
        editor.ReplaceNode(elseIfClause.Parent.Parent, (currentNode, _) =>
        {
            var parentIfStatement = (IfStatementSyntax)currentNode;
            var elseClause = parentIfStatement.Else;
            var elseIfStatement = (IfStatementSyntax)elseClause.Statement;
            return parentIfStatement.WithElse(elseIfStatement.Else);
        });
    }
 
    private static bool ContainsEmbeddedIfStatement(IfStatementSyntax ifStatement)
    {
        for (var statement = ifStatement.Statement; statement.IsEmbeddedStatementOwner(); statement = statement.GetEmbeddedStatement())
        {
            if (statement.IsKind(SyntaxKind.IfStatement))
            {
                return true;
            }
        }
 
        return false;
    }
}