File: src\Analyzers\CSharp\Tests\AddAccessibilityModifiers\AddAccessibilityModifiersTests.cs
Web Access
Project: src\src\CodeStyle\CSharp\Tests\Microsoft.CodeAnalysis.CSharp.CodeStyle.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle.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.
 
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.AddAccessibilityModifiers;
using Microsoft.CodeAnalysis.CSharp.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AddAccessibilityModifiers;
 
using VerifyCS = CSharpCodeFixVerifier<
    CSharpAddAccessibilityModifiersDiagnosticAnalyzer,
    CSharpAddAccessibilityModifiersCodeFixProvider>;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsAddAccessibilityModifiers)]
public class AddAccessibilityModifiersTests
{
    [Theory, CombinatorialData]
    public void TestStandardProperty(AnalyzerProperty property)
        => VerifyCS.VerifyStandardProperty(property);
 
    [Fact]
    public async Task TestAllConstructs()
    {
        await VerifyCS.VerifyCodeFixAsync(
            """
            using System;
            namespace Outer
            {
                namespace Inner1.Inner2
                {
                    partial class [|C|] : I
                    {
                        class [|NestedClass|] { }
 
                        struct [|NestedStruct|] { }
 
                        int [|f1|];
                        int [|f2|], f3;
                        public int f4;
 
                        event Action [|e1|], e2;
                        public event Action e3;
 
                        event Action [|e4|] { add { } remove { } }
                        public event Action e5 { add { } remove { } }
                        event Action I.e6 { add { } remove { } }
 
                        static C() { }
                        [|C|]() { }
                        public C(int i) { }
 
                        ~C() { }
 
                        void [|M1|]() { }
                        public void M2() { }
                        void I.M3() { }
                        partial void M4();
                        partial void M4() { }
 
                        int [|P1|] { get; }
                        public int P2 { get; }
                        int I.P3 { get; }
 
                        int [|this|][int i] => throw null;
                        public int this[string s] => throw null;
                        int I.this[bool b] => throw null;
                    }
 
                    interface [|I|]
                    {
                        event Action e6;
                        void M3();
                        int P3 { get; }
                        int this[bool b] { get; }
                    }
 
                    delegate void [|D|]();
 
                    enum [|E|]
                    {
                        EMember
                    }
                }
            }
            """,
            """
            using System;
            namespace Outer
            {
                namespace Inner1.Inner2
                {
                    internal partial class C : I
                    {
                        private class NestedClass { }
 
                        private struct NestedStruct { }
 
                        private int f1;
                        private int f2, f3;
                        public int f4;
 
                        private event Action e1, e2;
                        public event Action e3;
 
                        private event Action e4 { add { } remove { } }
                        public event Action e5 { add { } remove { } }
                        event Action I.e6 { add { } remove { } }
 
                        static C() { }
 
                        private C() { }
                        public C(int i) { }
 
                        ~C() { }
 
                        private void M1() { }
                        public void M2() { }
                        void I.M3() { }
                        partial void M4();
                        partial void M4() { }
 
                        private int P1 { get; }
                        public int P2 { get; }
                        int I.P3 { get; }
 
                        private int this[int i] => throw null;
                        public int this[string s] => throw null;
                        int I.this[bool b] => throw null;
                    }
 
                    internal interface I
                    {
                        event Action e6;
                        void M3();
                        int P3 { get; }
                        int this[bool b] { get; }
                    }
 
                    internal delegate void D();
 
                    internal enum E
                    {
                        EMember
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestRefStructs()
    {
        await VerifyCS.VerifyCodeFixAsync("""
            namespace Test
            {
                ref struct [|S1|] { }
            }
            """, """
            namespace Test
            {
                internal ref struct S1 { }
            }
            """);
    }
 
    [Fact]
    public async Task TestRecords()
    {
        var source = """
            record [|Record|]
            {
                int [|field|];
            }
            namespace System.Runtime.CompilerServices
            {
                public sealed class IsExternalInit
                {
                }
            }
            """;
        var fixedSource = """
            internal record Record
            {
                private int field;
            }
            namespace System.Runtime.CompilerServices
            {
                public sealed class IsExternalInit
                {
                }
            }
            """;
 
        var test = new VerifyCS.Test
        {
            TestCode = source,
            FixedCode = fixedSource,
            LanguageVersion = LanguageVersion.CSharp9,
        };
 
        await test.RunAsync();
    }
 
    [Fact]
    public async Task TestRecordStructs()
    {
        var source = """
            record struct [|Record|]
            {
                int [|field|];
            }
            """;
        var fixedSource = """
            internal record struct Record
            {
                private int field;
            }
            """;
 
        var test = new VerifyCS.Test
        {
            TestCode = source,
            FixedCode = fixedSource,
            LanguageVersion = LanguageVersion.CSharp12,
        };
 
        await test.RunAsync();
    }
 
    [Fact]
    public async Task TestReadOnlyStructs()
    {
        await VerifyCS.VerifyCodeFixAsync("""
            namespace Test
            {
                readonly struct [|S1|] { }
            }
            """, """
            namespace Test
            {
                internal readonly struct S1 { }
            }
            """);
    }
 
    [Fact]
    public async Task TestAllConstructsWithOmit()
    {
        await new VerifyCS.Test
        {
            TestState =
            {
                Sources =
                {
                    """
                    using System;
                    namespace Outer
                    {
                        namespace Inner1.Inner2
                        {
                            internal partial class [|C|] : I
                            {
                                private class [|NestedClass|] { }
 
                                private struct [|NestedStruct|] { }
 
                                private int [|f1|];
                                private int [|f2|], f3;
                                public int f4;
 
                                private event Action [|e1|], e2;
                                public event Action e3;
 
                                private event Action [|e4|] { add { } remove { } }
                                public event Action e5 { add { } remove { } }
                                event Action I.e6 { add { } remove { } }
 
                                static C() { }
 
                                private [|C|]() { }
                                public C(int i) { }
 
                                ~C() { }
 
                                private void [|M1|]() { }
                                public void M2() { }
                                void I.M3() { }
                                partial void M4();
                                partial void M4() { }
 
                                private int [|P1|] { get; }
                                public int P2 { get; }
                                int I.P3 { get; }
 
                                private int [|this|][int i] => throw null;
                                public int this[string s] => throw null;
                                int I.this[bool b] => throw null;
                            }
 
                            internal interface [|I|]
                            {
                                event Action e6;
                                void M3();
                                int P3 { get; }
                                int this[bool b] { get; }
                            }
 
                            internal delegate void [|D|]();
 
                            internal enum [|E|]
                            {
                                EMember
                            }
                        }
                    }
                    """,
                },
            },
            FixedState =
            {
                Sources =
                {
                    """
                    using System;
                    namespace Outer
                    {
                        namespace Inner1.Inner2
                        {
                            partial class C : I
                            {
                                class NestedClass { }
 
                                struct NestedStruct { }
 
                                int f1;
                                int f2, f3;
                                public int f4;
 
                                event Action e1, e2;
                                public event Action e3;
 
                                event Action e4 { add { } remove { } }
                                public event Action e5 { add { } remove { } }
                                event Action I.e6 { add { } remove { } }
 
                                static C() { }
 
                                C() { }
                                public C(int i) { }
 
                                ~C() { }
 
                                void M1() { }
                                public void M2() { }
                                void I.M3() { }
                                partial void M4();
                                partial void M4() { }
 
                                int P1 { get; }
                                public int P2 { get; }
                                int I.P3 { get; }
 
                                int this[int i] => throw null;
                                public int this[string s] => throw null;
                                int I.this[bool b] => throw null;
                            }
 
                            interface I
                            {
                                event Action e6;
                                void M3();
                                int P3 { get; }
                                int this[bool b] { get; }
                            }
 
                            delegate void D();
 
                            enum E
                            {
                                EMember
                            }
                        }
                    }
                    """,
                },
            },
            Options =
            {
                { CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault },
            },
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestRefStructsWithOmit()
    {
        await new VerifyCS.Test
        {
            TestCode = """
            namespace Test
            {
                internal ref struct [|S1|] { }
            }
            """,
            FixedCode = """
            namespace Test
            {
                ref struct S1 { }
            }
            """,
            Options =
            {
                { CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault },
            },
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestReadOnlyStructsWithOmit()
    {
        await new VerifyCS.Test
        {
            TestCode = """
            namespace Test
            {
                internal readonly struct [|S1|] { }
            }
            """,
            FixedCode = """
            namespace Test
            {
                readonly struct S1 { }
            }
            """,
            Options =
            {
                { CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault },
            },
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestClassOutsideNamespace()
    {
        await new VerifyCS.Test
        {
            TestCode = """
            internal class [|C1|] { }
            """,
            FixedCode = """
            class C1 { }
            """,
            Options =
            {
                { CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault },
            },
        }.RunAsync();
    }
 
    [Fact]
    public async Task TestExternMethod()
    {
        await VerifyCS.VerifyCodeFixAsync("""
            using System;
            using System.Runtime.InteropServices;
 
            internal class Program
            {
                [DllImport("User32.dll", CharSet = CharSet.Unicode)]
                static extern int [|MessageBox|](IntPtr h, string m, string c, int type);
            }
            """,
            """
            using System;
            using System.Runtime.InteropServices;
 
            internal class Program
            {
                [DllImport("User32.dll", CharSet = CharSet.Unicode)]
                private static extern int [|MessageBox|](IntPtr h, string m, string c, int type);
            }
            """);
    }
 
    [Fact]
    public async Task TestVolatile()
    {
        await VerifyCS.VerifyCodeFixAsync("""
            internal class Program
            {
                volatile int [|x|];
            }
            """,
            """
            internal class Program
            {
                private volatile int x;
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48899")]
    public async Task TestAbstractMethod()
    {
        await VerifyCS.VerifyCodeFixAsync("""
            public abstract class TestClass
            {
                abstract string {|CS0621:[|Test|]|} { get; }
            }
            """,
            """
            public abstract class TestClass
            {
                protected abstract string Test { get; }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48899")]
    public async Task TestOverriddenMethod()
    {
        await VerifyCS.VerifyCodeFixAsync("""
            public abstract class TestClass
            {
                public abstract string Test { get; }
            }
 
            public class Derived : TestClass
            {
                override string {|CS0507:{|CS0621:[|Test|]|}|} { get; }
            }
            """,
            """
            public abstract class TestClass
            {
                public abstract string Test { get; }
            }
 
            public class Derived : TestClass
            {
                public override string Test { get; }
            }
            """);
    }
 
    [Fact]
    public async Task TestFileScopedNamespaces()
    {
        await new VerifyCS.Test
        {
            TestCode = """
            namespace Test;
 
            struct [|S1|] { }
            """,
            FixedCode = """
            namespace Test;
 
            internal struct S1 { }
            """,
            LanguageVersion = LanguageVersion.CSharp10,
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/55703")]
    public async Task TestPartial_WithExistingModifier()
    {
        var source = """
            partial class [|C|]
            {
            }
 
            public partial class C
            {
            }
            """;
        var fixedSource = """
            public partial class C
            {
            }
 
            public partial class C
            {
            }
            """;
 
        var test = new VerifyCS.Test
        {
            TestCode = source,
            FixedCode = fixedSource,
            LanguageVersion = LanguageVersion.CSharp12,
        };
 
        await test.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58914")]
    public async Task TestStaticOperatorInInterface()
    {
        var source = """
            internal interface I<T> where T : I<T>
            {
                abstract static int operator +(T x);
            }
 
            internal class C : I<C>
            {
                static int I<C>.operator +(C x)
                {
                    throw new System.NotImplementedException();
                }
            }
            """;
 
        var test = new VerifyCS.Test
        {
            TestCode = source,
            LanguageVersion = LanguageVersion.CSharp12,
            ReferenceAssemblies = Testing.ReferenceAssemblies.Net.Net60
        };
 
        await test.RunAsync();
    }
 
    [Theory]
    [InlineData("class")]
    [InlineData("struct")]
    [InlineData("record")]
    [InlineData("record struct")]
    [InlineData("interface")]
    [InlineData("enum")]
    [WorkItem("https://github.com/dotnet/roslyn/issues/62259")]
    public async Task TestFileDeclaration(string declarationKind)
    {
        var source = $"file {declarationKind} C {{ }}";
 
        await new VerifyCS.Test
        {
            TestCode = source,
            LanguageVersion = LanguageVersion.CSharp12,
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/62259")]
    public async Task TestFileDelegate()
    {
        var source = "file delegate void M();";
 
        await new VerifyCS.Test
        {
            TestCode = source,
            LanguageVersion = LanguageVersion.CSharp12,
        }.RunAsync();
    }
 
    [Theory]
    [InlineData("class")]
    [InlineData("struct")]
    [InlineData("record")]
    [InlineData("record struct")]
    [InlineData("interface")]
    [InlineData("enum")]
    [WorkItem("https://github.com/dotnet/roslyn/issues/62259")]
    public async Task TestNestedFileDeclaration(string declarationKind)
    {
        var source = $$"""
            file class C1
            {
                {{declarationKind}} [|C2|] { }
            }
            """;
 
        var fixedSource = $$"""
            file class C1
            {
                private {{declarationKind}} C2 { }
            }
            """;
 
        await new VerifyCS.Test
        {
            TestCode = source,
            FixedCode = fixedSource,
            LanguageVersion = LanguageVersion.CSharp12,
        }.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/29633")]
    public async Task TestTitle1()
    {
        var source = """
            internal class C
            {
                int [|field|];
            }
            """;
        var fixedSource = """
            internal class C
            {
                private int field;
            }
            """;
 
        var test = new VerifyCS.Test
        {
            TestCode = source,
            FixedCode = fixedSource,
            LanguageVersion = LanguageVersion.CSharp12,
            CodeActionEquivalenceKey = nameof(AnalyzersResources.Add_accessibility_modifiers),
        };
 
        await test.RunAsync();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/29633")]
    public async Task TestTitle2()
    {
        var source = """
            class C
            {
                private int [|field|];
            }
            """;
        var fixedSource = """
            class C
            {
                int field;
            }
            """;
 
        var test = new VerifyCS.Test
        {
            TestCode = source,
            FixedCode = fixedSource,
            LanguageVersion = LanguageVersion.CSharp12,
            CodeActionEquivalenceKey = nameof(AnalyzersResources.Remove_accessibility_modifiers),
            Options =
            {
                { CodeStyleOptions2.AccessibilityModifiersRequired,AccessibilityModifiersRequired.OmitIfDefault }
            }
        };
 
        await test.RunAsync();
    }
}