File: src\Analyzers\CSharp\CodeFixes\UseCollectionExpression\CSharpUseCollectionExpressionForArrayCodeFixProvider.cs
Web Access
Project: src\src\CodeStyle\CSharp\CodeFixes\Microsoft.CodeAnalysis.CSharp.CodeStyle.Fixes.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle.Fixes)
// 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;
using System.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.UseCollectionExpression;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression;
 
using static UseCollectionExpressionHelpers;
 
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseCollectionExpressionForArray), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed partial class CSharpUseCollectionExpressionForArrayCodeFixProvider()
    : AbstractUseCollectionExpressionCodeFixProvider<ExpressionSyntax>(
        CSharpCodeFixesResources.Use_collection_expression,
        IDEDiagnosticIds.UseCollectionExpressionForArrayDiagnosticId)
{
    public override ImmutableArray<string> FixableDiagnosticIds { get; } = [IDEDiagnosticIds.UseCollectionExpressionForArrayDiagnosticId];
 
    protected sealed override async Task FixAsync(
        Document document,
        SyntaxEditor editor,
        ExpressionSyntax arrayCreationExpression,
        ImmutableDictionary<string, string?> properties,
        CancellationToken cancellationToken)
    {
        var services = document.Project.Solution.Services;
        var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
        var originalRoot = editor.OriginalRoot;
 
        if (arrayCreationExpression
                is not ArrayCreationExpressionSyntax
                and not ImplicitArrayCreationExpressionSyntax
                and not InitializerExpressionSyntax)
        {
            return;
        }
 
        if (arrayCreationExpression is InitializerExpressionSyntax initializer)
        {
            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
            editor.ReplaceNode(
                initializer,
                ConvertInitializerToCollectionExpression(
                    initializer,
                    IsOnSingleLine(text, initializer)));
        }
        else
        {
            var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
            var expressionType = semanticModel.Compilation.ExpressionOfTType();
            var matches = GetMatches(semanticModel, arrayCreationExpression, expressionType);
            if (matches.IsDefault)
                return;
 
            var collectionExpression = await CSharpCollectionExpressionRewriter.CreateCollectionExpressionAsync(
                document,
                arrayCreationExpression,
                preMatches: [],
                matches,
                static e => e switch
                {
                    ArrayCreationExpressionSyntax arrayCreation => arrayCreation.Initializer,
                    ImplicitArrayCreationExpressionSyntax implicitArrayCreation => implicitArrayCreation.Initializer,
                    _ => throw ExceptionUtilities.Unreachable(),
                },
                static (e, i) => e switch
                {
                    ArrayCreationExpressionSyntax arrayCreation => arrayCreation.WithInitializer(i),
                    ImplicitArrayCreationExpressionSyntax implicitArrayCreation => implicitArrayCreation.WithInitializer(i),
                    _ => throw ExceptionUtilities.Unreachable(),
                },
                cancellationToken).ConfigureAwait(false);
 
            editor.ReplaceNode(arrayCreationExpression, collectionExpression);
            foreach (var match in matches)
                editor.RemoveNode(match.Node);
        }
 
        return;
 
        static bool IsOnSingleLine(SourceText sourceText, SyntaxNode node)
            => sourceText.AreOnSameLine(node.GetFirstToken(), node.GetLastToken());
 
        ImmutableArray<CollectionMatch<StatementSyntax>> GetMatches(
            SemanticModel semanticModel, ExpressionSyntax expression, INamedTypeSymbol? expressionType)
            => expression switch
            {
                ImplicitArrayCreationExpressionSyntax arrayCreation
                    => CSharpUseCollectionExpressionForArrayDiagnosticAnalyzer.TryGetMatches(
                        semanticModel, arrayCreation, CreateReplacementCollectionExpressionForAnalysis(arrayCreation.Initializer), expressionType, allowSemanticsChange: true, cancellationToken, out _),
 
                ArrayCreationExpressionSyntax arrayCreation
                    => CSharpUseCollectionExpressionForArrayDiagnosticAnalyzer.TryGetMatches(
                        semanticModel, arrayCreation, CreateReplacementCollectionExpressionForAnalysis(arrayCreation.Initializer), expressionType, allowSemanticsChange: true, cancellationToken, out _),
 
                // We validated this is unreachable in the caller.
                _ => throw ExceptionUtilities.Unreachable(),
            };
    }
}