File: src\Analyzers\CSharp\CodeFixes\UseCollectionInitializer\CSharpUseCollectionInitializerCodeFixProvider_CollectionInitializer.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;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.UseObjectInitializer;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.UseCollectionExpression;
using Microsoft.CodeAnalysis.UseCollectionInitializer;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.UseCollectionInitializer;
 
using static CSharpSyntaxTokens;
using static SyntaxFactory;
 
internal sealed partial class CSharpUseCollectionInitializerCodeFixProvider
{
    private static BaseObjectCreationExpressionSyntax CreateObjectInitializerExpression(
        BaseObjectCreationExpressionSyntax objectCreation,
        ImmutableArray<CollectionMatch<SyntaxNode>> matches)
    {
        var expressions = CreateCollectionInitializerExpressions();
        var withLineBreaks = AddLineBreaks(expressions);
 
        var newCreation = UseInitializerHelpers.GetNewObjectCreation(objectCreation, withLineBreaks);
        return newCreation.WithAdditionalAnnotations(Formatter.Annotation);
 
        SeparatedSyntaxList<ExpressionSyntax> CreateCollectionInitializerExpressions()
        {
            using var _ = ArrayBuilder<SyntaxNodeOrToken>.GetInstance(out var nodesAndTokens);
 
            UseInitializerHelpers.AddExistingItems<CollectionMatch<SyntaxNode>, ExpressionSyntax>(
                objectCreation, nodesAndTokens, addTrailingComma: matches.Length > 0, static (_, expression) => expression);
 
            for (var i = 0; i < matches.Length; i++)
            {
                var match = matches[i];
                var statement = (ExpressionStatementSyntax)match.Node;
 
                var trivia = statement.GetLeadingTrivia();
                var leadingTrivia = i == 0 ? trivia.WithoutLeadingBlankLines() : trivia;
 
                var trailingTrivia = statement.SemicolonToken.TrailingTrivia.Contains(static t => t.IsSingleOrMultiLineComment())
                    ? statement.SemicolonToken.TrailingTrivia
                    : default;
 
                var expression = ConvertExpression(statement.Expression)
                    .WithTrailingTrivia()
                    .WithLeadingTrivia(leadingTrivia);
 
                if (i < matches.Length - 1)
                {
                    nodesAndTokens.Add(expression);
                    nodesAndTokens.Add(CommaToken.WithTrailingTrivia(trailingTrivia));
                }
                else
                {
                    nodesAndTokens.Add(expression.WithTrailingTrivia(trailingTrivia));
                }
            }
 
            return SeparatedList<ExpressionSyntax>(nodesAndTokens);
        }
 
        static SeparatedSyntaxList<TNode> AddLineBreaks<TNode>(SeparatedSyntaxList<TNode> nodes)
            where TNode : SyntaxNode
        {
            using var _ = ArrayBuilder<SyntaxNodeOrToken>.GetInstance(out var nodesAndTokens);
 
            var nodeOrTokenList = nodes.GetWithSeparators();
            foreach (var item in nodeOrTokenList)
            {
                var addLineBreak = item.IsToken || item == nodeOrTokenList.Last();
                if (addLineBreak && item.GetTrailingTrivia() is not [.., (kind: SyntaxKind.EndOfLineTrivia)])
                {
                    nodesAndTokens.Add(item.WithAppendedTrailingTrivia(ElasticCarriageReturnLineFeed));
                }
                else
                {
                    nodesAndTokens.Add(item);
                }
            }
 
            return SeparatedList<TNode>(nodesAndTokens);
        }
 
        static ExpressionSyntax ConvertExpression(
            ExpressionSyntax expression)
        {
            // This must be called from an expression from the original tree.  Not something we're already transforming.
            // Otherwise, we'll have no idea how to apply the preferredIndentation if present.
            Contract.ThrowIfNull(expression.Parent);
            return expression switch
            {
                InvocationExpressionSyntax invocation => ConvertInvocation(invocation),
                AssignmentExpressionSyntax assignment => ConvertAssignment(assignment),
                _ => throw new InvalidOperationException(),
            };
        }
 
        static AssignmentExpressionSyntax ConvertAssignment(AssignmentExpressionSyntax assignment)
        {
            var elementAccess = (ElementAccessExpressionSyntax)assignment.Left;
            return assignment.WithLeft(
                ImplicitElementAccess(elementAccess.ArgumentList));
        }
 
        static ExpressionSyntax ConvertInvocation(InvocationExpressionSyntax invocation)
        {
            var arguments = invocation.ArgumentList.Arguments;
 
            if (arguments.Count == 1)
            {
                // Assignment expressions in a collection initializer will cause the compiler to report an error.
                // This is because { a = b } is the form for an object initializer, and the two forms are not
                // allowed to mix/match.  Parenthesize the assignment to avoid the ambiguity.
                //
                // Similarly, we cannot have `{ [...] }` in an object initializer (because it will be viewed as a
                // dictionary assignment).
                var expression = arguments[0].Expression;
                return expression is AssignmentExpressionSyntax or CollectionExpressionSyntax
                    ? ParenthesizedExpression(expression)
                    : expression;
            }
            else
            {
                return InitializerExpression(
                    SyntaxKind.ComplexElementInitializerExpression,
                    OpenBraceToken.WithoutTrivia(),
                    SeparatedList(
                        arguments.Select(a => a.Expression),
                        arguments.GetSeparators()),
                    CloseBraceToken.WithoutTrivia());
            }
        }
    }
}