File: Microsoft.NetCore.Analyzers\Runtime\SealInternalTypesTests.cs
Web Access
Project: ..\..\..\src\Microsoft.CodeAnalysis.NetAnalyzers\tests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests.csproj (Microsoft.CodeAnalysis.NetAnalyzers.UnitTests)
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the MIT license.  See License.txt in the project root for license information.
 
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Test.Utilities;
using Xunit;
 
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Runtime.SealInternalTypes,
    Microsoft.NetCore.Analyzers.Runtime.SealInternalTypesFixer>;
using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Runtime.SealInternalTypes,
    Microsoft.NetCore.Analyzers.Runtime.SealInternalTypesFixer>;
 
namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests
{
    public class SealInternalTypesTests
    {
        // NOTE: 'SealInternalTypes' analyzer reports a compilation end diagnostic.
        //       Code fixes are not yet supported for compilation end diagnostics,
        //       hence 'SealInternalTypesFixer' is a no-op right now.
        //       However, we have still implemented this fixer so that if code fix support
        //       gets added for compilation end diagnostics in future, it should automatically light up.
        //       All tests in this file are marked with 'CodeFixTestBehaviors.SkipLocalDiagnosticCheck'
        //       as compilation end diagnostics are non-local diagnostics.
 
        #region Diagnostic
        [Theory]
        [InlineData("internal ")]
        [InlineData("")]
        public async Task TopLevelInternalClass_Diagnostic_CS(string accessModifier)
        {
            string source = $"{accessModifier}class {{|#0:C|}} {{ }}";
            string fixedSource = $"{accessModifier}sealed class C {{ }}";
 
            await new VerifyCS.Test
            {
                CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck,
                TestCode = source,
                ExpectedDiagnostics =
                {
                    VerifyCS.Diagnostic(Rule).WithArguments("C").WithLocation(0),
                },
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Theory]
        [InlineData("Friend ")]
        [InlineData("")]
        public async Task TopLevelInternalClass_Diagnostic_VB(string accessModifier)
        {
            string source = $@"
{accessModifier}Class {{|#0:C|}}
End Class";
            string fixedSource = $@"
{accessModifier}NotInheritable Class C
End Class";
 
            await new VerifyVB.Test
            {
                CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck,
                TestCode = source,
                ExpectedDiagnostics =
                {
                    VerifyVB.Diagnostic(Rule).WithArguments("C").WithLocation(0),
                },
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Fact]
        public async Task NonEmptyInternalClass_Diagnostic_CS()
        {
            string source = @"
internal class {|#0:C|}
{
    private int _i;
}";
            string fixedSource = @"
internal sealed class C
{
    private int _i;
}";
 
            await new VerifyCS.Test
            {
                CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck,
                TestCode = source,
                ExpectedDiagnostics =
                {
                    VerifyCS.Diagnostic(Rule).WithArguments("C").WithLocation(0),
                },
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Theory]
        [InlineData("internal ")]
        [InlineData("")]
        public async Task InternalClassInNamespace_Diagnostic_CS(string accessModifier)
        {
            string source = $@"
namespace N
{{
    {accessModifier}class {{|#0:C|}} {{ }}
}}";
            string fixedSource = $@"
namespace N
{{
    {accessModifier}sealed class C {{ }}
}}";
 
            await new VerifyCS.Test
            {
                CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck,
                TestCode = source,
                ExpectedDiagnostics =
                {
                    VerifyCS.Diagnostic(Rule).WithArguments("C").WithLocation(0),
                },
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Theory]
        [InlineData("Friend ")]
        [InlineData("")]
        public async Task InternalClassInNamespace_Diagnostic_VB(string accessModifier)
        {
            string source = $@"
Namespace N
    {accessModifier}Class {{|#0:C|}}
    End Class
End Namespace";
            string fixedSource = $@"
Namespace N
    {accessModifier}NotInheritable Class C
    End Class
End Namespace";
 
            await new VerifyVB.Test
            {
                CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck,
                TestCode = source,
                ExpectedDiagnostics =
                {
                    VerifyVB.Diagnostic(Rule).WithArguments("C").WithLocation(0),
                },
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Theory]
        [InlineData("public", "internal")]
        [InlineData("public", "private")]
        [InlineData("public", "private protected")]
        [InlineData("internal", "public")]
        [InlineData("internal", "protected internal")]
        public async Task NestedOneDeep_NotExternallyVisible_Diagnostic_CS(string outerModifiers, string innerModifiers)
        {
            string source = $@"
{outerModifiers} sealed class Outer
{{
    {innerModifiers} class {{|#0:C|}} {{ }}
}}";
            string fixedSource = $@"
{outerModifiers} sealed class Outer
{{
    {innerModifiers} sealed class C {{ }}
}}";
 
            await new VerifyCS.Test
            {
                CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck,
                TestCode = source,
                ExpectedDiagnostics =
                {
                    VerifyCS.Diagnostic(Rule).WithArguments("C").WithLocation(0),
                },
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Theory]
        [InlineData("Public", "Friend")]
        [InlineData("Public", "Private")]
        [InlineData("Public", "Private Protected")]
        [InlineData("Friend", "Public")]
        [InlineData("Friend", "Friend Protected")]
        public async Task NestedOneDeep_NotExternallyVisible_Diagnostic_VB(string outerModifiers, string innerModifiers)
        {
            string source = $@"
{outerModifiers} NotInheritable Class Outer
    {innerModifiers} Class {{|#0:C|}}
    End Class
End Class";
            string fixedSource = $@"
{outerModifiers} NotInheritable Class Outer
    {innerModifiers} NotInheritable Class C
    End Class
End Class";
 
            await new VerifyVB.Test
            {
                CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck,
                TestCode = source,
                ExpectedDiagnostics =
                {
                    VerifyVB.Diagnostic(Rule).WithArguments("C").WithLocation(0),
                },
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Theory]
        [InlineData("public", "public", "internal")]
        [InlineData("internal", "internal protected", "public")]
        [InlineData("public", "private protected", "public")]
        public async Task NestedTwoDeep_NotExternallyVisible_Diagnostic_CS(string outerModifiers, string middleModifiers, string innerModifiers)
        {
            string source = $@"
{outerModifiers} sealed class Outer
{{
    {middleModifiers} sealed class Middle
    {{
        {innerModifiers} class {{|#0:C|}} {{ }}
    }}
}}";
            string fixedSource = $@"
{outerModifiers} sealed class Outer
{{
    {middleModifiers} sealed class Middle
    {{
        {innerModifiers} sealed class {{|#0:C|}} {{ }}
    }}
}}";
 
            await new VerifyCS.Test
            {
                CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck,
                TestCode = source,
                ExpectedDiagnostics =
                {
                    VerifyCS.Diagnostic(Rule).WithArguments("C").WithLocation(0),
                },
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Theory]
        [InlineData("Public", "Public", "Friend")]
        [InlineData("Friend", "Friend Protected", "Public")]
        [InlineData("Public", "Private Protected", "Public")]
        public async Task NestedTwoDeep_NotExternallyVisible_Diagnostic_VB(string outerModifiers, string middleModifiers, string innerModifiers)
        {
            string source = $@"
{outerModifiers} NotInheritable Class Outer
    {middleModifiers} NotInheritable Class Middle
        {innerModifiers} Class {{|#0:C|}}
        End Class
    End Class
End Class";
            string fixedSource = $@"
{outerModifiers} NotInheritable Class Outer
    {middleModifiers} NotInheritable Class Middle
        {innerModifiers} NotInheritable Class C
        End Class
    End Class
End Class";
 
            await new VerifyVB.Test
            {
                CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck,
                TestCode = source,
                ExpectedDiagnostics =
                {
                    VerifyVB.Diagnostic(Rule).WithArguments("C").WithLocation(0),
                },
                FixedCode = fixedSource,
            }.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public async Task InternalsVisibleTo_Diagnostic_WhenOptionsDemandIt(bool ignoreInternalsVisibleTo)
        {
            string source = @"[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""TestProject"")]
                              internal class {|#0:C|} { }";
 
            var test = new VerifyCS.Test
            {
                TestCode = source,
                TestState =
                {
                    AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true
 
[*]
dotnet_code_quality.CA1852.ignore_internalsvisibleto = {ignoreInternalsVisibleTo}
") }
                }
            };
 
            if (ignoreInternalsVisibleTo)
            {
                test.ExpectedDiagnostics.Add(
                    VerifyCS.Diagnostic(Rule)
                        .WithLocation(0)
                        .WithArguments("C"));
            }
 
            await test.RunAsync();
        }
 
        #endregion
 
        #region No Diagnostic
        [Fact, WorkItem(6141, "https://github.com/dotnet/roslyn-analyzers/issues/6141")]
        public Task TopLevelStatementsProgram()
        {
            return new VerifyCS.Test()
            {
                TestState =
                {
                    Sources = { "System.Console.WriteLine();" },
                    OutputKind = OutputKind.ConsoleApplication,
                },
                LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp9,
            }.RunAsync();
        }
 
        [Fact]
        public Task PublicClassType_NoDiagnostic_CS()
        {
            string source = $"public class C {{ protected class P {{ }} }}";
 
            return VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public Task AlreadySealedType_NoDiagnostic_CS()
        {
            string source = $"internal sealed class C {{ }}";
 
            return VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public Task InternalsVisibleTo_NoDiagnostic()
        {
            string source = @"[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""TestProject"")]
                              internal class C { }";
 
            return VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public Task ComImportAttributedType_NoDiagnostic_CS()
        {
            string source = $"[System.Runtime.InteropServices.ComImport] [System.Runtime.InteropServices.Guid(\"E8D59775-E821-4D6C-B63D-BB0D969361DA\")] internal class C {{ }}";
 
            return VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Theory]
        [InlineData("interface I { }")]
        [InlineData("struct S { }")]
        [InlineData("enum E { None }")]
        [InlineData("delegate void D();")]
        [InlineData("static class C { }")]
        public Task NonClassType_NoDiagnostic_CS(string declaration)
        {
            string source = $"internal {declaration}";
 
            return VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Theory]
        [InlineData("Interface I", "End Interface")]
        [InlineData("Structure S", "End Structure")]
        [InlineData(@"Enum E
    None", "End Enum")]
        [InlineData("Delegate Sub D()", "")]
        [InlineData("Module M", "End Module")]
        public Task NonClassType_NoDiagnostic_VB(string declaration, string endDeclaration)
        {
            string source = $@"
Friend {declaration}
{endDeclaration}";
 
            return VerifyVB.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public Task ClassWithDerivedType_NoDiagnostic_CS()
        {
            string source = @"
internal class B { }
internal sealed class D : B { }";
 
            return VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public Task ClassWithDerivedType_NoDiagnostic_VB()
        {
            string source = @"
Friend Class B
End Class
Friend NotInheritable Class D : Inherits B
End Class";
 
            return VerifyVB.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public Task AbstractClass_NoDiagnostic_CS()
        {
            string source = "internal abstract class C { }";
 
            return VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public Task AbstractClass_NoDiagnostic_VB()
        {
            string source = @"
Friend MustInherit Class C
End Class";
 
            return VerifyVB.VerifyCodeFixAsync(source, source);
        }
 
        [Theory]
        [InlineData("B<T> { }", "D : B<int> { }")]
        [InlineData("B<T> { }", "D<T> : B<T> { }")]
        [InlineData("B<T, U> { }", "D<T> : B<T, int> { }")]
        public Task GenericClass_WithSubclass_NoDiagnostic_CS(string baseClass, string derivedClass)
        {
            string source = $@"
internal class {baseClass}
internal sealed class {derivedClass}";
 
            return VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Theory]
        [InlineData("B(Of T)", "D : Inherits B(Of Integer)")]
        [InlineData("B(Of T)", "D(Of T) : Inherits B(Of T)")]
        [InlineData("B(Of T, U)", "D(Of T) : Inherits B(Of T, Integer)")]
        public Task GenericClass_WithSubclass_NoDiagnostic_VB(string baseClass, string derivedClass)
        {
            string source = $@"
Friend Class {baseClass}
End Class
 
Friend NotInheritable Class {derivedClass}
End Class";
 
            return VerifyVB.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public Task PartialClass_ReportedAndFixedAtAllLocations_CS()
        {
            var test = new VerifyCS.Test
            {
                CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck,
                TestState =
                {
                    Sources =
                    {
                        @"internal class Base { }",
                        @"internal partial class {|#0:Derived|} : Base { }",
                        @"internal partial class {|#1:Derived|} : Base { }"
                    },
                    ExpectedDiagnostics =
                    {
                        VerifyCS.Diagnostic(Rule).WithArguments("Derived").WithLocation(0).WithLocation(1)
                    }
                },
                FixedState =
                {
                    Sources =
                    {
                        @"internal class Base { }",
                        @"internal sealed partial class Derived : Base { }",
                        @"internal sealed partial class Derived : Base { }"
                    }
                }
            };
            return test.RunAsync();
        }
 
        [Fact(Skip = "Changes are being applied to .g.cs file")]
        public Task PartialClass_OneGenerated_ReportedAndFixedAtAllNonGeneratedLocations_CS()
        {
            var test = new VerifyCS.Test
            {
                TestState =
                {
                    Sources =
                    {
                        ("File1.cs", @"internal class Base { }"),
                        ("File2.cs", @"internal partial class {|#0:Derived|} : Base { }"),
                        ("File3.g.cs", @"internal partial class {|#1:Derived|} : Base { }")
                    },
                    ExpectedDiagnostics =
                    {
                        VerifyCS.Diagnostic(Rule).WithArguments("Derived").WithLocation(0).WithLocation(1)
                    }
                },
                FixedState =
                {
                    Sources =
                    {
                        ("File1.cs", @"internal class Base { }"),
                        ("File2.cs", @"internal sealed partial class {|#0:Derived|} : Base { }"),
                        ("File3.g.cs", @"internal partial class {|#1:Derived|} : Base { }")
                    }
                }
            };
            return test.RunAsync();
        }
 
        [Fact]
        public Task PartialClass_ReportedAndFixedAtAllLocations_VB()
        {
            var test = new VerifyVB.Test
            {
                CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck,
                TestState =
                {
                    Sources =
                    {
                        @"
Friend Class Base
End Class", @"
Partial Friend Class {|#0:Derived|} : Inherits Base
End Class", @"
Partial Friend Class {|#1:Derived|} : Inherits Base
End Class"
                    },
                    ExpectedDiagnostics =
                    {
                        VerifyVB.Diagnostic(Rule).WithArguments("Derived").WithLocation(0).WithLocation(1)
                    }
                },
                FixedState =
                {
                    Sources =
                    {
                        @"
Friend Class Base
End Class", @"
Partial Friend NotInheritable Class Derived : Inherits Base
End Class", @"
Partial Friend NotInheritable Class Derived : Inherits Base
End Class"
                    }
                }
            };
            return test.RunAsync();
        }
        #endregion
 
        private static DiagnosticDescriptor Rule => SealInternalTypes.Rule;
    }
}