File: Formatting\FormattingTests_Patterns.cs
Web Access
Project: src\src\Workspaces\CSharpTest\Microsoft.CodeAnalysis.CSharp.Workspaces.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Workspaces.UnitTests)
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Formatting;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Formatting;
 
[Trait(Traits.Feature, Traits.Features.Formatting)]
public sealed class FormattingTests_Patterns : CSharpFormattingTestBase
{
    [Theory, CombinatorialData]
    public async Task FormatRelationalPatterns1(
        [CombinatorialValues("<", "<=", ">", ">=")] string operatorText,
        BinaryOperatorSpacingOptions spacing)
    {
        var expectedSingle = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is {{operatorText}} 3 or {{operatorText}} 5;
                }
            }
            """;
        var expectedIgnore = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is  {{operatorText}}  3  or  {{operatorText}}  5;
                }
            }
            """;
        var expectedRemove = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is {{operatorText}}3 or {{operatorText}}5;
                }
            }
            """;
 
        var expected = spacing switch
        {
            BinaryOperatorSpacingOptions.Single => expectedSingle,
            BinaryOperatorSpacingOptions.Ignore => expectedIgnore,
            BinaryOperatorSpacingOptions.Remove => expectedRemove,
            _ => throw ExceptionUtilities.Unreachable(),
        };
 
        var changingOptions = new OptionsCollection(LanguageNames.CSharp)
        {
            { CSharpFormattingOptions2.SpacingAroundBinaryOperator, spacing },
        };
        await AssertFormatAsync(expected, $$"""
            class A
            {
                bool Method(int value)
                {
                    return value  is  {{operatorText}}  3  or  {{operatorText}}  5;
                }
            }
            """, changedOptionSet: changingOptions);
    }
 
    [Theory, CombinatorialData]
    public async Task FormatRelationalPatterns2(
        [CombinatorialValues("<", "<=", ">", ">=")] string operatorText,
        BinaryOperatorSpacingOptions spacing,
        bool spaceWithinExpressionParentheses)
    {
        var expectedSingleFalse = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is ({{operatorText}} 3) or ({{operatorText}} 5);
                }
            }
            """;
        var expectedIgnoreFalse = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is ({{operatorText}}  3)  or  ({{operatorText}}  5);
                }
            }
            """;
        var expectedRemoveFalse = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is ({{operatorText}}3) or ({{operatorText}}5);
                }
            }
            """;
        var expectedSingleTrue = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is ( {{operatorText}} 3 ) or ( {{operatorText}} 5 );
                }
            }
            """;
        var expectedIgnoreTrue = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is ( {{operatorText}}  3 )  or  ( {{operatorText}}  5 );
                }
            }
            """;
        var expectedRemoveTrue = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is ( {{operatorText}}3 ) or ( {{operatorText}}5 );
                }
            }
            """;
 
        var expected = (spacing, spaceWithinExpressionParentheses) switch
        {
            (BinaryOperatorSpacingOptions.Single, false) => expectedSingleFalse,
            (BinaryOperatorSpacingOptions.Ignore, false) => expectedIgnoreFalse,
            (BinaryOperatorSpacingOptions.Remove, false) => expectedRemoveFalse,
            (BinaryOperatorSpacingOptions.Single, true) => expectedSingleTrue,
            (BinaryOperatorSpacingOptions.Ignore, true) => expectedIgnoreTrue,
            (BinaryOperatorSpacingOptions.Remove, true) => expectedRemoveTrue,
            _ => throw ExceptionUtilities.Unreachable(),
        };
 
        var changingOptions = new OptionsCollection(LanguageNames.CSharp)
        {
            { CSharpFormattingOptions2.SpacingAroundBinaryOperator, spacing },
            { CSharpFormattingOptions2.SpaceBetweenParentheses, CSharpFormattingOptions2.SpaceBetweenParentheses.DefaultValue.WithFlagValue(SpacePlacementWithinParentheses.Expressions, spaceWithinExpressionParentheses) },
        };
        await AssertFormatAsync(expected, $$"""
            class A
            {
                bool Method(int value)
                {
                    return value  is  (  {{operatorText}}  3  )  or  (  {{operatorText}}  5  )  ;
                }
            }
            """, changedOptionSet: changingOptions);
    }
 
    [Theory, CombinatorialData]
    public async Task FormatNotPatterns1(BinaryOperatorSpacingOptions spacing)
    {
        var expectedSingle = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is not 3 or not 5;
                }
            }
            """;
        var expectedIgnore = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is not 3  or  not 5;
                }
            }
            """;
        var expectedRemove = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is not 3 or not 5;
                }
            }
            """;
 
        var expected = spacing switch
        {
            BinaryOperatorSpacingOptions.Single => expectedSingle,
            BinaryOperatorSpacingOptions.Ignore => expectedIgnore,
            BinaryOperatorSpacingOptions.Remove => expectedRemove,
            _ => throw ExceptionUtilities.Unreachable(),
        };
 
        var changingOptions = new OptionsCollection(LanguageNames.CSharp)
        {
            { CSharpFormattingOptions2.SpacingAroundBinaryOperator, spacing },
        };
        await AssertFormatAsync(expected, $$"""
            class A
            {
                bool Method(int value)
                {
                    return value  is  not  3  or  not  5;
                }
            }
            """, changedOptionSet: changingOptions);
    }
 
    [Theory, CombinatorialData]
    public async Task FormatNotPatterns2(
        BinaryOperatorSpacingOptions spacing,
        bool spaceWithinExpressionParentheses)
    {
        var expectedSingleFalse = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is (not 3) or (not 5);
                }
            }
            """;
        var expectedIgnoreFalse = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is (not 3)  or  (not 5);
                }
            }
            """;
        var expectedRemoveFalse = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is (not 3) or (not 5);
                }
            }
            """;
        var expectedSingleTrue = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is ( not 3 ) or ( not 5 );
                }
            }
            """;
        var expectedIgnoreTrue = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is ( not 3 )  or  ( not 5 );
                }
            }
            """;
        var expectedRemoveTrue = $$"""
            class A
            {
                bool Method(int value)
                {
                    return value is ( not 3 ) or ( not 5 );
                }
            }
            """;
 
        var expected = (spacing, spaceWithinExpressionParentheses) switch
        {
            (BinaryOperatorSpacingOptions.Single, false) => expectedSingleFalse,
            (BinaryOperatorSpacingOptions.Ignore, false) => expectedIgnoreFalse,
            (BinaryOperatorSpacingOptions.Remove, false) => expectedRemoveFalse,
            (BinaryOperatorSpacingOptions.Single, true) => expectedSingleTrue,
            (BinaryOperatorSpacingOptions.Ignore, true) => expectedIgnoreTrue,
            (BinaryOperatorSpacingOptions.Remove, true) => expectedRemoveTrue,
            _ => throw ExceptionUtilities.Unreachable(),
        };
 
        var changingOptions = new OptionsCollection(LanguageNames.CSharp)
        {
            { CSharpFormattingOptions2.SpacingAroundBinaryOperator, spacing },
            { CSharpFormattingOptions2.SpaceBetweenParentheses, CSharpFormattingOptions2.SpaceBetweenParentheses.DefaultValue.WithFlagValue(SpacePlacementWithinParentheses.Expressions, spaceWithinExpressionParentheses) },
        };
        await AssertFormatAsync(expected, $$"""
            class A
            {
                bool Method(int value)
                {
                    return value  is  (  not  3  )  or  (  not  5  );
                }
            }
            """, changedOptionSet: changingOptions);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46284")]
    public Task FormatMultiLinePattern1()
        => AssertFormatAsync("""
            class TypeName
            {
                bool MethodName(string value)
                {
                    return value is object
                           && value is
                           {
                               Length: 2,
                           };
                }
            }
            """, """
            class TypeName
            {
                bool MethodName(string value)
                {
                    return value is object
                           && value is
                             {
                                 Length: 2,
                             };
                }
            }
            """);
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46284")]
    public Task FormatMultiLinePattern2()
        => AssertFormatAsync("""
            class TypeName
            {
                private static bool IsCallingConventionModifier(CustomModifier modifier)
                {
                    var modifierType = ((CSharpCustomModifier)modifier).ModifierSymbol;
                    return (object)modifierType.ContainingAssembly == modifierType.ContainingAssembly.CorLibrary
                           && modifierType.Name != "CallConv"
                           && modifierType.Arity == 0
                           && modifierType.Name.StartsWith("CallConv", StringComparison.Ordinal)
                           && modifierType.ContainingNamespace is
                           {
                               Name: "CompilerServices",
                               ContainingNamespace:
                               {
                                   Name: "Runtime",
                                   ContainingNamespace:
                                   {
                                       Name: "System",
                                       ContainingNamespace: { IsGlobalNamespace: true }
                                   }
                               }
                           };
                }
            }
            """, """
            class TypeName
            {
                private static bool IsCallingConventionModifier(CustomModifier modifier)
                {
                    var modifierType = ((CSharpCustomModifier)modifier).ModifierSymbol;
                    return (object)modifierType.ContainingAssembly == modifierType.ContainingAssembly.CorLibrary
                           && modifierType.Name != "CallConv"
                           && modifierType.Arity == 0
                           && modifierType.Name.StartsWith("CallConv", StringComparison.Ordinal)
                           && modifierType.ContainingNamespace is
                              {
                                  Name: "CompilerServices",
                                  ContainingNamespace:
                                  {
                                      Name: "Runtime",
                                      ContainingNamespace:
                                      {
                                          Name: "System",
                                          ContainingNamespace: { IsGlobalNamespace: true }
                                      }
                                  }
                              };
                }
            }
            """);
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46284")]
    public Task FormatMultiLinePattern3()
        => AssertFormatAsync("""
            class TypeName
            {
                private static bool IsCallingConventionModifier(CustomModifier modifier)
                {
                    var modifierType = ((CSharpCustomModifier)modifier).ModifierSymbol;
                    return (object)modifierType.ContainingAssembly == modifierType.ContainingAssembly.CorLibrary
                           && modifierType.Name != "CallConv"
                           && modifierType.Arity == 0
                           && modifierType.Name.StartsWith("CallConv", StringComparison.Ordinal)
                           && modifierType.ContainingNamespace is
                           {
                               Name: "CompilerServices",
                               ContainingNamespace:
                               {
                                   Name: "Runtime",
                                   ContainingNamespace:
                                   {
                                       Name: "System",
                                       ContainingNamespace: { IsGlobalNamespace: true }
                                   }
                               }
                           };
                }
            }
            """, """
            class TypeName
            {
                private static bool IsCallingConventionModifier(CustomModifier modifier)
                {
                    var modifierType = ((CSharpCustomModifier)modifier).ModifierSymbol;
                    return (object)modifierType.ContainingAssembly == modifierType.ContainingAssembly.CorLibrary
                           && modifierType.Name != "CallConv"
                           && modifierType.Arity == 0
                           && modifierType.Name.StartsWith("CallConv", StringComparison.Ordinal)
                           && modifierType.ContainingNamespace is
            {
            Name: "CompilerServices",
            ContainingNamespace:
            {
            Name: "Runtime",
            ContainingNamespace:
            {
            Name: "System",
            ContainingNamespace: { IsGlobalNamespace: true }
            }
            }
            };
                }
            }
            """);
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42861")]
    public Task FormatMultiLinePattern4()
        => AssertFormatAsync("""
            class TypeName
            {
                void MethodName(string value)
                {
                    if (value is
                        {
                            Length: 2,
                        })
                    {
                    }
                }
            }
            """, """
            class TypeName
            {
                void MethodName(string value)
                {
                    if (value is
                             {
                                 Length: 2,
                             })
            {
            }
                }
            }
            """);
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42861")]
    public Task FormatMultiLinePattern5()
        => AssertFormatAsync("""
            class TypeName
            {
                void MethodName(string value)
                {
                    while (value is
                        {
                            Length: 2,
                        })
                    {
                    }
                }
            }
            """, """
            class TypeName
            {
                void MethodName(string value)
                {
                    while (value is
                             {
                                 Length: 2,
                             })
            {
            }
                }
            }
            """);
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42861")]
    public Task FormatNestedListPattern1()
        => AssertFormatAsync("""
            class C
            {
                void M(string[] ss)
                {
                    if (ss is [[]])
                    {
            
                    }
                }
            }
            """, """
            class C
            {
                void M(string[] ss)
                {
                    if (ss is [ [  ]  ])
                    {
 
                    }
                }
            }
            """);
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42861")]
    public Task FormatNestedListPattern2()
        => AssertFormatAsync("""
            class C
            {
                void M(string[] ss)
                {
                    if (ss is [[], []])
                    {
            
                    }
                }
            }
            """, """
            class C
            {
                void M(string[] ss)
                {
                    if (ss is [ [  ],[ ]     ])
                    {
 
                    }
                }
            }
            """);
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42861")]
    public Task FormatNestedListPattern3()
        => AssertFormatAsync("""
            class C
            {
                void M(string[] ss)
                {
                    if (ss is [[], [], []])
                    {
            
                    }
                }
            }
            """, """
            class C
            {
                void M(string[] ss)
                {
                    if (ss is [    [  ],[ ]     , [   ]  ] )
                    {
 
                    }
                }
            }
            """);
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42861")]
    public Task FormatNestedListPattern4()
        => AssertFormatAsync("""
            class C
            {
                void M(string[][] ss)
                {
                    if (ss is [[[]]])
                    {
            
                    }
                }
            }
            """, """
            class C
            {
                void M(string[][] ss)
                {
                    if (ss is [    [ [ ] ] ] )
                    {
 
                    }
                }
            }
            """);
}