File: AddRequiredExpressionParenthesesTests.cs
Web Access
Project: ..\..\..\src\EditorFeatures\CSharpTest\Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.EditorFeatures.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.AddRequiredParentheses;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.AddRequiredParentheses;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AddRequiredParentheses
{
    [Trait(Traits.Feature, Traits.Features.CodeActionsAddRequiredParentheses)]
    public partial class AddRequiredExpressionParenthesesTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
    {
        public AddRequiredExpressionParenthesesTests(ITestOutputHelper logger)
          : base(logger)
        {
        }
 
        internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
            => (new CSharpAddRequiredExpressionParenthesesDiagnosticAnalyzer(), new AddRequiredParenthesesCodeFixProvider());
 
        private Task TestMissingAsync(string initialMarkup, OptionsCollection options)
            => TestMissingInRegularAndScriptAsync(initialMarkup, new TestParameters(options: options));
 
        private Task TestAsync(string initialMarkup, string expected, OptionsCollection options)
            => TestInRegularAndScript1Async(initialMarkup, expected, parameters: new TestParameters(options: options));
 
        [Fact]
        public async Task TestArithmeticPrecedence()
        {
            await TestAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 + 2 $$* 3;
                    }
                }
                """,
                """
                class C
                {
                    void M()
                    {
                        int x = 1 + (2 * 3);
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNoArithmeticOnLowerPrecedence()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 $$+ 2 * 3;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotIfArithmeticPrecedenceStaysTheSame()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 + 2 $$+ 3;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotIfArithmeticPrecedenceIsNotEnforced1()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 + 2 $$+ 3;
                    }
                }
                """, RequireOtherBinaryParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotIfArithmeticPrecedenceIsNotEnforced2()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 + 2 $$* 3;
                    }
                }
                """, RequireOtherBinaryParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestRelationalPrecedence()
        {
            await TestAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a $$> b == c;
                    }
                }
                """,
                """
                class C
                {
                    void M()
                    {
                        int x = (a > b) == c;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestLogicalPrecedence()
        {
            await TestAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a || b $$&& c;
                    }
                }
                """,
                """
                class C
                {
                    void M()
                    {
                        int x = a || (b && c);
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNoLogicalOnLowerPrecedence()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a $$|| b && c;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotIfLogicalPrecedenceStaysTheSame()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a || b $$|| c;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotIfLogicalPrecedenceIsNotEnforced()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a || b $$|| c;
                    }
                }
                """, RequireArithmeticBinaryParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestMixedArithmeticAndLogical()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a == b $$&& c == d;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestLogicalPrecedenceMultipleEqualPrecedenceParts1()
        {
            await TestAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a || b $$&& c && d;
                    }
                }
                """,
                """
                class C
                {
                    void M()
                    {
                        int x = a || (b && c && d);
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestLogicalPrecedenceMultipleEqualPrecedenceParts2()
        {
            await TestAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a || b && c $$&& d;
                    }
                }
                """,
                """
                class C
                {
                    void M()
                    {
                        int x = a || (b && c && d);
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestShiftPrecedence1()
        {
            await TestAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 $$+ 2 << 3;
                    }
                }
                """,
                """
                class C
                {
                    void M()
                    {
                        int x = (1 + 2) << 3;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestShiftPrecedence2()
        {
            await TestAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 $$+ 2 << 3;
                    }
                }
                """,
                """
                class C
                {
                    void M()
                    {
                        int x = (1 + 2) << 3;
                    }
                }
                """, RequireArithmeticBinaryParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestShiftPrecedence3()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 $$+ 2 << 3;
                    }
                }
                """, RequireOtherBinaryParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotIfShiftPrecedenceStaysTheSame1()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 $$<< 2 << 3;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotIfShiftPrecedenceStaysTheSame2()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 << 2 $$<< 3;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestEqualityPrecedence1()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 $$+ 2 == 2 + 3;
                    }
                }
                """, RequireOtherBinaryParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestEqualityPrecedence2()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 + 2 == 2 $$+ 3;
                    }
                }
                """, RequireOtherBinaryParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestEqualityPrecedence3()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 $$+ 2 == 2 + 3;
                    }
                }
                """, RequireRelationalBinaryParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestEqualityPrecedence4()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 + 2 == 2 $$+ 3;
                    }
                }
                """, RequireRelationalBinaryParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestCoalescePrecedence1()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a $$+ b ?? c;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestCoalescePrecedence2()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a $$?? b ?? c;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestCoalescePrecedence3()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a ?? b $$?? c;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestBitwisePrecedence1()
        {
            await TestAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 $$+ 2 & 3;
                    }
                }
                """,
                """
                class C
                {
                    void M()
                    {
                        int x = (1 + 2) & 3;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestBitwisePrecedence2()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a $$| b | c;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestBitwisePrecedence3()
        {
            await TestAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a | b $$& c;
                    }
                }
                """,
                """
                class C
                {
                    void M()
                    {
                        int x = a | (b & c);
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestBitwisePrecedence4()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = a $$| b & c;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotForEqualityAfterEquals()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 $$== 2;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotForAssignmentEqualsAfterLocal()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M(int a)
                    {
                        int x = a $$+= 2;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestForAssignmentAndEquality1()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M(bool x, bool y, bool z)
                    {
                        x $$= y == z;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestMissingForAssignmentAndEquality2()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M(bool x, bool y, bool z)
                    {
                        x = y $$== z;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestUnclearCast1()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = (int)$$-y;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestUnclearCast_NotOfferedWithIgnore()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = (int)$$-y;
                    }
                }
                """, IgnoreAllParentheses);
        }
 
        [Fact]
        public async Task TestUnclearCast_NotOfferedWithRemoveForClarity()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = (int)$$-y;
                    }
                }
                """, RemoveAllUnnecessaryParentheses);
        }
 
        [Fact]
        public async Task TestUnclearCast2()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = (int)$$+y;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestUnclearCast3()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = (int)$$&y;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestUnclearCast4()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = (int)$$*y;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotForPrimary()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = (int)$$y;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotForMemberAccess()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = (int)$$y.z;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotForCastOfCast()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = (int)$$(y);
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestNotForNonAmbiguousUnary()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = (int)$$!y;
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestFixAll1()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        if (0 {|FixAllInDocument:>=|} 3 * 2 + 4)
                        {
                        }
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestFixAll2()
        {
            await TestInRegularAndScriptAsync(
                """
                class C
                {
                    void M()
                    {
                        if (3 * 2 + 4 >= 3 {|FixAllInDocument:*|} 2 + 4)
                        {
                        }
                    }
                }
                """,
                """
                class C
                {
                    void M()
                    {
                        if ((3 * 2) + 4 >= (3 * 2) + 4)
                        {
                        }
                    }
                }
                """, options: RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestFixAll3()
        {
            await TestMissingAsync(
                """
                class C
                {
                    void M()
                    {
                        if (3 * 2 + 4 >= 3 * 2 {|FixAllInDocument:+|} 4)
                        {
                        }
                    }
                }
                """, RequireAllParenthesesForClarity);
        }
 
        [Fact]
        public async Task TestSeams1()
        {
            await TestInRegularAndScriptAsync(
                """
                class C
                {
                    void M()
                    {
                        int x = 1 + 2 {|FixAllInDocument:*|} 3 == 1 + 2 * 3;
                    }
                }
                """,
                """
                class C
                {
                    void M()
                    {
                        int x = 1 + (2 * 3) == 1 + (2 * 3);
                    }
                }
                """, options: RequireAllParenthesesForClarity);
        }
    }
}