File: IntroduceUsingStatement\CSharpIntroduceUsingStatementCodeRefactoringProvider.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.
 
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.IntroduceUsingStatement;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.IntroduceUsingStatement;
 
using static CSharpSyntaxTokens;
using static SyntaxFactory;
 
[ExtensionOrder(Before = PredefinedCodeRefactoringProviderNames.IntroduceVariable)]
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.IntroduceUsingStatement), Shared]
[method: ImportingConstructor]
[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
internal sealed class CSharpIntroduceUsingStatementCodeRefactoringProvider()
    : AbstractIntroduceUsingStatementCodeRefactoringProvider<
        StatementSyntax,
        ExpressionStatementSyntax,
        LocalDeclarationStatementSyntax,
        TryStatementSyntax>
{
    protected override string CodeActionTitle => CSharpFeaturesResources.Introduce_using_statement;
 
    protected override bool PreferSimpleUsingStatement(AnalyzerOptionsProvider options)
        => ((CSharpAnalyzerOptionsProvider)options).PreferSimpleUsingStatement.Value;
 
    protected override bool HasCatchBlocks(TryStatementSyntax tryStatement)
        => tryStatement.Catches.Count > 0;
 
    protected override (SyntaxList<StatementSyntax> tryStatements, SyntaxList<StatementSyntax> finallyStatements) GetTryFinallyStatements(TryStatementSyntax tryStatement)
        => (tryStatement.Block.Statements, tryStatement.Finally?.Block.Statements ?? default);
 
    protected override bool CanRefactorToContainBlockStatements(SyntaxNode parent)
        => parent is BlockSyntax || parent is SwitchSectionSyntax || parent.IsEmbeddedStatementOwner();
 
    protected override SyntaxList<StatementSyntax> GetSurroundingStatements(StatementSyntax statement)
        => statement.GetRequiredParent() switch
        {
            BlockSyntax block => block.Statements,
            SwitchSectionSyntax switchSection => switchSection.Statements,
            _ => [statement],
        };
 
    protected override SyntaxNode WithStatements(SyntaxNode parentOfStatementsToSurround, SyntaxList<StatementSyntax> statements)
    {
        return
            parentOfStatementsToSurround is BlockSyntax block ? block.WithStatements(statements) as SyntaxNode :
            parentOfStatementsToSurround is SwitchSectionSyntax switchSection ? switchSection.WithStatements(statements) :
            throw ExceptionUtilities.UnexpectedValue(parentOfStatementsToSurround);
    }
 
    protected override StatementSyntax CreateUsingBlockStatement(ExpressionStatementSyntax expressionStatement, SyntaxList<StatementSyntax> statementsToSurround)
        => UsingStatement(
            UsingKeyword.WithLeadingTrivia(expressionStatement.GetLeadingTrivia()),
            OpenParenToken,
            declaration: null,
            expression: expressionStatement.Expression.WithoutTrivia(),
            CloseParenToken.WithTrailingTrivia(expressionStatement.GetTrailingTrivia()),
            statement: Block(statementsToSurround));
 
    protected override StatementSyntax CreateUsingLocalDeclarationStatement(
        ExpressionStatementSyntax expressionStatement, SyntaxToken newVariableName)
    {
        return LocalDeclarationStatement(VariableDeclaration(
                IdentifierName("var"),
                SingletonSeparatedList(VariableDeclarator(
                    newVariableName,
                    argumentList: null,
                    initializer: EqualsValueClause(expressionStatement.Expression)))))
            .WithUsingKeyword(UsingKeyword)
            .WithSemicolonToken(expressionStatement.SemicolonToken).WithTriviaFrom(expressionStatement);
    }
 
    protected override StatementSyntax CreateUsingStatement(LocalDeclarationStatementSyntax declarationStatement, SyntaxList<StatementSyntax> statementsToSurround)
        => UsingStatement(
            UsingKeyword.WithLeadingTrivia(declarationStatement.GetLeadingTrivia()),
            OpenParenToken,
            declaration: declarationStatement.Declaration.WithoutTrivia(),
            expression: null, // Declaration already has equals token and expression
            CloseParenToken.WithTrailingTrivia(declarationStatement.GetTrailingTrivia()),
            statement: Block(statementsToSurround));
 
    protected override bool TryCreateUsingLocalDeclaration(
        ParseOptions options,
        LocalDeclarationStatementSyntax declarationStatement,
        [NotNullWhen(true)] out LocalDeclarationStatementSyntax? usingDeclarationStatement)
    {
        usingDeclarationStatement = null;
 
        // using-declarations are not allowed in switch sections (due to craziness with how switch section scoping works).
        if (declarationStatement.Parent is SwitchSectionSyntax ||
            options.LanguageVersion() < LanguageVersion.CSharp8)
        {
            return false;
        }
 
        usingDeclarationStatement = declarationStatement
            .WithoutLeadingTrivia()
            .WithUsingKeyword(Token(declarationStatement.GetLeadingTrivia(), SyntaxKind.UsingKeyword, [Space]));
        return true;
    }
}