diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForArrayCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForArrayCodeFixProvider.cs index b3092135ddf0d..a907dc9e8c8ed 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForArrayCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForArrayCodeFixProvider.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; @@ -70,7 +71,7 @@ protected override async Task FixAllAsync( return; - bool IsOnSingleLine(SyntaxNode node) + static bool IsOnSingleLine(SourceText sourceText, SyntaxNode node) => sourceText.AreOnSameLine(node.GetFirstToken(), node.GetLastToken()); void RewriteInitializerExpression(InitializerExpressionSyntax initializer) @@ -79,40 +80,103 @@ void RewriteInitializerExpression(InitializerExpressionSyntax initializer) initializer, (current, _) => ConvertInitializerToCollectionExpression( (InitializerExpressionSyntax)current, - IsOnSingleLine(initializer))); + IsOnSingleLine(sourceText, initializer))); + } + + bool ShouldReplaceExistingExpressionEntirely(ExpressionSyntax explicitOrImplicitArray, InitializerExpressionSyntax initializer) + { + // Any time we have `{ x, y, z }` in any form, then always just replace the whole original expression + // with `[x, y, z]`. + if (IsOnSingleLine(sourceText, initializer)) + return true; + + // initializer was on multiple lines, but started on the same line as the 'new' keyword. e.g.: + // + // var v = new[] { + // 1, 2, 3 + // }; + // + // Just remove the `new...` section entirely, but otherwise keep the initialize multiline: + // + // var v = [ + // 1, 2, 3 + // ]; + var newKeyword = explicitOrImplicitArray.GetFirstToken(); + if (sourceText.AreOnSameLine(newKeyword, initializer.OpenBraceToken)) + return true; + + // Initializer was on multiple lines, and was not on the same line as the 'new' keyword, and the 'new' is on a newline: + // + // var v2 = + // new[] + // { + // 1, 2, 3 + // }; + // + // For this latter, we want to just remove the new portion and move the collection to subsume it. + var previousToken = newKeyword.GetPreviousToken(); + if (previousToken == default) + return true; + + if (!sourceText.AreOnSameLine(previousToken, newKeyword)) + return true; + + // All that is left is: + // + // var v2 = new[] + // { + // 1, 2, 3 + // }; + // + // For this we want to remove the 'new' portion, but keep the collection on its own line. + return false; } void RewriteArrayCreationExpression(ArrayCreationExpressionSyntax arrayCreation) { + Contract.ThrowIfNull(arrayCreation.Initializer); + var shouldReplaceExpressionEntirely = ShouldReplaceExistingExpressionEntirely(arrayCreation, arrayCreation.Initializer); + editor.ReplaceNode( arrayCreation, (current, _) => { var currentArrayCreation = (ArrayCreationExpressionSyntax)current; Contract.ThrowIfNull(currentArrayCreation.Initializer); + var collectionExpression = ConvertInitializerToCollectionExpression( currentArrayCreation.Initializer, - IsOnSingleLine(arrayCreation)); + IsOnSingleLine(sourceText, arrayCreation.Initializer)); - collectionExpression = collectionExpression.WithLeadingTrivia(currentArrayCreation.GetLeadingTrivia()); - return collectionExpression; + return shouldReplaceExpressionEntirely + ? collectionExpression.WithTriviaFrom(currentArrayCreation) + : collectionExpression + .WithPrependedLeadingTrivia(currentArrayCreation.Type.GetTrailingTrivia()) + .WithPrependedLeadingTrivia(ElasticMarker); }); } void RewriteImplicitArrayCreationExpression(ImplicitArrayCreationExpressionSyntax implicitArrayCreation) { + Contract.ThrowIfNull(implicitArrayCreation.Initializer); + var shouldReplaceExpressionEntirely = ShouldReplaceExistingExpressionEntirely(implicitArrayCreation, implicitArrayCreation.Initializer); + editor.ReplaceNode( implicitArrayCreation, (current, _) => { var currentArrayCreation = (ImplicitArrayCreationExpressionSyntax)current; Contract.ThrowIfNull(currentArrayCreation.Initializer); + var collectionExpression = ConvertInitializerToCollectionExpression( currentArrayCreation.Initializer, - IsOnSingleLine(implicitArrayCreation)); + IsOnSingleLine(sourceText, implicitArrayCreation)); - collectionExpression = collectionExpression.WithLeadingTrivia(currentArrayCreation.GetLeadingTrivia()); - return collectionExpression; + return shouldReplaceExpressionEntirely + ? collectionExpression.WithTriviaFrom(currentArrayCreation) + : collectionExpression + .WithPrependedLeadingTrivia(currentArrayCreation.CloseBracketToken.TrailingTrivia) + .WithPrependedLeadingTrivia(ElasticMarker); }); } } diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs index 4ff0621a3174c..694cf4ff8f0fc 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs @@ -243,7 +243,8 @@ class C FixedCode = """ class C { - object[] i = [ + object[] i = + [ "" ]; } @@ -347,7 +348,8 @@ class C FixedCode = """ class C { - string[] i = [ + string[] i = + [ "" ]; } @@ -1126,4 +1128,726 @@ void M(int[] x) LanguageVersion = LanguageVersionExtensions.CSharpNext, }.RunAsync(); } + + [Fact] + public async Task TestInitializerFormatting1() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = [|{|] 1, 2, 3 }; + } + """, + FixedCode = """ + class C + { + int[] i = [1, 2, 3]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting2() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|{|] 1, 2, 3 }; + } + """, + FixedCode = """ + class C + { + int[] i = + [1, 2, 3]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting3() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = [|{|] + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting4() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|{|] + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting5() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|{|] 1, 2, 3 }; + } + """, + FixedCode = """ + class C + { + int[] i = + [1, 2, 3]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting6() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = [|{|] + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting7() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|{|] + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting8() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + + [|{|] + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting1_Explicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = [|[|new|] int[]|] { 1, 2, 3 }; + } + """, + FixedCode = """ + class C + { + int[] i = [1, 2, 3]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting2_Explicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|[|new|] int[]|] { 1, 2, 3 }; + } + """, + FixedCode = """ + class C + { + int[] i = + [1, 2, 3]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting3_Explicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = [|[|new|] int[]|] { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting4_Explicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = [|[|new|] int[]|] + { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting5_Explicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|[|new|] int[]|] + { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting6_Explicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|[|new|] int[]|] { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting7_Explicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|[|new|] int[]|] + { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting8_Explicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|[|new|] int[]|] { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting9_Explicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = [|[|new|] int[]|] { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting10_Explicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + + [|[|new|] int[]|] + { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting1_Implicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = [|[|new|][]|] { 1, 2, 3 }; + } + """, + FixedCode = """ + class C + { + int[] i = [1, 2, 3]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting2_Implicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|[|new|][]|] { 1, 2, 3 }; + } + """, + FixedCode = """ + class C + { + int[] i = + [1, 2, 3]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting3_Implicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = [|[|new|][]|] { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting4_Implicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = [|[|new|][]|] + { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting5_Implicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|[|new|][]|] + { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting6_Implicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|[|new|][]|] { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting7_Implicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|[|new|][]|] + { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting8_Implicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + [|[|new|][]|] { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting9_Implicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = [|[|new|][]|] { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInitializerFormatting10_Implicit() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + int[] i = + + [|[|new|][]|] + { + 1, 2, 3 + }; + } + """, + FixedCode = """ + class C + { + int[] i = + + [ + 1, 2, 3 + ]; + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } }