File: PrivateProtected.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Emit\Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Emit.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.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using static Roslyn.Test.Utilities.SigningTestHelpers;
using Xunit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    [CompilerTrait(CompilerFeature.PrivateProtected)]
    public class PrivateProtected : CSharpTestBase
    {
        private static readonly string s_keyPairFile = SigningTestHelpers.KeyPairFile;
        private static readonly string s_publicKeyFile = SigningTestHelpers.PublicKeyFile;
        private static readonly ImmutableArray<byte> s_publicKey = SigningTestHelpers.PublicKey;
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void RejectIncompatibleModifiers()
        {
            string source =
@"public class Base
{
    private internal int Field1;
    internal private int Field2;
    private internal protected int Field3;
    internal protected private int Field4;
    private public protected int Field5;
    private readonly protected int Field6; // ok
}
";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                // (3,26): error CS0107: More than one protection modifier
                //     private internal int Field1;
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "Field1").WithLocation(3, 26),
                // (4,26): error CS0107: More than one protection modifier
                //     internal private int Field2;
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "Field2").WithLocation(4, 26),
                // (5,36): error CS0107: More than one protection modifier
                //     private internal protected int Field3;
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "Field3").WithLocation(5, 36),
                // (6,36): error CS0107: More than one protection modifier
                //     internal protected private int Field4;
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "Field4").WithLocation(6, 36),
                // (7,34): error CS0107: More than one protection modifier
                //     private public protected int Field5;
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "Field5").WithLocation(7, 34)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void AccessibleWhereRequired_01()
        {
            string source =
@"public class Base
{
    private protected int Field1;
    protected private int Field2;
}
 
public class Derived : Base
{
    void M()
    {
        Field1 = 1;
        Field2 = 2;
    }
}
";
            var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void AccessibleWhereRequired_02()
        {
            string source1 =
@"[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""WantsIVTAccess"")]
public class Base
{
    private protected const int Constant = 3;
    private protected int Field1;
    protected private int Field2;
    private protected void Method() { }
    private protected event System.Action Event1;
    private protected int Property1 { set {} }
    public int Property2 { private protected set {} get { return 4; } }
    private protected int this[int x] { set { } get { return 6; } }
    public int this[string x] { private protected set { } get { return 5; } }
    private protected Base() { Event1?.Invoke(); }
}";
            var baseCompilation = CreateCompilation(source1, parseOptions: TestOptions.Regular7_2,
                options: TestOptions.SigningReleaseDll,
                assemblyName: "Paul");
            var bb = (NamedTypeSymbol)baseCompilation.GlobalNamespace.GetMember("Base");
            foreach (var member in bb.GetMembers())
            {
                switch (member.Name)
                {
                    case "Property2":
                    case "get_Property2":
                    case "this[]":
                    case "get_Item":
                        break;
                    default:
                        Assert.Equal(Accessibility.ProtectedAndInternal, member.DeclaredAccessibility);
                        break;
                }
            }
 
            string source2 =
@"public class Derived : Base
{
    void M()
    {
        Field1 = Constant;
        Field2 = Constant;
        Method();
        Event1 += null;
        Property1 = Constant;
        Property2 = Constant;
        this[1] = 2;
        this[string.Empty] = 4;
    }
    Derived(int x) : base() {}
    Derived(long x) {} // implicit base()
}
";
            CreateCompilation(source2, parseOptions: TestOptions.Regular7_2,
                references: new[] { new CSharpCompilationReference(baseCompilation) },
                assemblyName: "WantsIVTAccessButCantHave",
                options: TestOptions.SigningReleaseDll)
            .VerifyDiagnostics(
                // (5,9): error CS0122: 'Base.Field1' is inaccessible due to its protection level
                //         Field1 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Field1").WithArguments("Base.Field1").WithLocation(5, 9),
                // (5,18): error CS0122: 'Base.Constant' is inaccessible due to its protection level
                //         Field1 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Constant").WithArguments("Base.Constant").WithLocation(5, 18),
                // (6,9): error CS0122: 'Base.Field2' is inaccessible due to its protection level
                //         Field2 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Field2").WithArguments("Base.Field2").WithLocation(6, 9),
                // (6,18): error CS0122: 'Base.Constant' is inaccessible due to its protection level
                //         Field2 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Constant").WithArguments("Base.Constant").WithLocation(6, 18),
                // (7,9): error CS0122: 'Base.Method()' is inaccessible due to its protection level
                //         Method();
                Diagnostic(ErrorCode.ERR_BadAccess, "Method").WithArguments("Base.Method()").WithLocation(7, 9),
                // (8,9): error CS0122: 'Base.Event1' is inaccessible due to its protection level
                //         Event1 += null;
                Diagnostic(ErrorCode.ERR_BadAccess, "Event1").WithArguments("Base.Event1").WithLocation(8, 9),
                // (8,9): error CS0122: 'Base.Event1.add' is inaccessible due to its protection level
                //         Event1 += null;
                Diagnostic(ErrorCode.ERR_BadAccess, "Event1 += null").WithArguments("Base.Event1.add").WithLocation(8, 9),
                // (9,9): error CS0122: 'Base.Property1' is inaccessible due to its protection level
                //         Property1 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Property1").WithArguments("Base.Property1").WithLocation(9, 9),
                // (9,21): error CS0122: 'Base.Constant' is inaccessible due to its protection level
                //         Property1 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Constant").WithArguments("Base.Constant").WithLocation(9, 21),
                // (10,9): error CS0272: The property or indexer 'Base.Property2' cannot be used in this context because the set accessor is inaccessible
                //         Property2 = Constant;
                Diagnostic(ErrorCode.ERR_InaccessibleSetter, "Property2").WithArguments("Base.Property2").WithLocation(10, 9),
                // (10,21): error CS0122: 'Base.Constant' is inaccessible due to its protection level
                //         Property2 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Constant").WithArguments("Base.Constant").WithLocation(10, 21),
                // (11,14): error CS1503: Argument 1: cannot convert from 'int' to 'string'
                //         this[1] = 2;
                Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "string").WithLocation(11, 14),
                // (12,9): error CS0272: The property or indexer 'Base.this[string]' cannot be used in this context because the set accessor is inaccessible
                //         this[string.Empty] = 4;
                Diagnostic(ErrorCode.ERR_InaccessibleSetter, "this[string.Empty]").WithArguments("Base.this[string]").WithLocation(12, 9),
                // (14,22): error CS0122: 'Base.Base()' is inaccessible due to its protection level
                //     Derived(int x) : base() {}
                Diagnostic(ErrorCode.ERR_BadAccess, "base").WithArguments("Base.Base()").WithLocation(14, 22),
                // (15,5): error CS0122: 'Base.Base()' is inaccessible due to its protection level
                //     Derived(long x) {} // implicit base()
                Diagnostic(ErrorCode.ERR_BadAccess, "Derived").WithArguments("Base.Base()").WithLocation(15, 5)
                );
            CreateCompilation(source2, parseOptions: TestOptions.Regular7_2,
                references: new[] { MetadataReference.CreateFromImage(baseCompilation.EmitToArray()) },
                assemblyName: "WantsIVTAccessButCantHave",
                options: TestOptions.SigningReleaseDll)
            .VerifyDiagnostics(
                // (5,9): error CS0122: 'Base.Field1' is inaccessible due to its protection level
                //         Field1 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Field1").WithArguments("Base.Field1").WithLocation(5, 9),
                // (5,18): error CS0122: 'Base.Constant' is inaccessible due to its protection level
                //         Field1 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Constant").WithArguments("Base.Constant").WithLocation(5, 18),
                // (6,9): error CS0122: 'Base.Field2' is inaccessible due to its protection level
                //         Field2 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Field2").WithArguments("Base.Field2").WithLocation(6, 9),
                // (6,18): error CS0122: 'Base.Constant' is inaccessible due to its protection level
                //         Field2 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Constant").WithArguments("Base.Constant").WithLocation(6, 18),
                // (7,9): error CS0122: 'Base.Method()' is inaccessible due to its protection level
                //         Method();
                Diagnostic(ErrorCode.ERR_BadAccess, "Method").WithArguments("Base.Method()").WithLocation(7, 9),
                // (8,9): error CS0122: 'Base.Event1' is inaccessible due to its protection level
                //         Event1 += null;
                Diagnostic(ErrorCode.ERR_BadAccess, "Event1").WithArguments("Base.Event1").WithLocation(8, 9),
                // (8,9): error CS0122: 'Base.Event1.add' is inaccessible due to its protection level
                //         Event1 += null;
                Diagnostic(ErrorCode.ERR_BadAccess, "Event1 += null").WithArguments("Base.Event1.add").WithLocation(8, 9),
                // (9,9): error CS0122: 'Base.Property1' is inaccessible due to its protection level
                //         Property1 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Property1").WithArguments("Base.Property1").WithLocation(9, 9),
                // (9,21): error CS0122: 'Base.Constant' is inaccessible due to its protection level
                //         Property1 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Constant").WithArguments("Base.Constant").WithLocation(9, 21),
                // (10,9): error CS0272: The property or indexer 'Base.Property2' cannot be used in this context because the set accessor is inaccessible
                //         Property2 = Constant;
                Diagnostic(ErrorCode.ERR_InaccessibleSetter, "Property2").WithArguments("Base.Property2").WithLocation(10, 9),
                // (10,21): error CS0122: 'Base.Constant' is inaccessible due to its protection level
                //         Property2 = Constant;
                Diagnostic(ErrorCode.ERR_BadAccess, "Constant").WithArguments("Base.Constant").WithLocation(10, 21),
                // (11,14): error CS1503: Argument 1: cannot convert from 'int' to 'string'
                //         this[1] = 2;
                Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "string").WithLocation(11, 14),
                // (12,9): error CS0272: The property or indexer 'Base.this[string]' cannot be used in this context because the set accessor is inaccessible
                //         this[string.Empty] = 4;
                Diagnostic(ErrorCode.ERR_InaccessibleSetter, "this[string.Empty]").WithArguments("Base.this[string]").WithLocation(12, 9),
                // (14,22): error CS0122: 'Base.Base()' is inaccessible due to its protection level
                //     Derived(int x) : base() {}
                Diagnostic(ErrorCode.ERR_BadAccess, "base").WithArguments("Base.Base()").WithLocation(14, 22),
                // (15,5): error CS0122: 'Base.Base()' is inaccessible due to its protection level
                //     Derived(long x) {} // implicit base()
                Diagnostic(ErrorCode.ERR_BadAccess, "Derived").WithArguments("Base.Base()").WithLocation(15, 5)
                );
 
            CreateCompilation(source2, parseOptions: TestOptions.Regular7_2,
                references: new[] { new CSharpCompilationReference(baseCompilation) },
                assemblyName: "WantsIVTAccess",
                options: TestOptions.SigningReleaseDll)
                .VerifyDiagnostics(
                );
            CreateCompilation(source2, parseOptions: TestOptions.Regular7_2,
                references: new[] { MetadataReference.CreateFromImage(baseCompilation.EmitToArray()) },
                assemblyName: "WantsIVTAccess",
                options: TestOptions.SigningReleaseDll)
                .VerifyDiagnostics(
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void NotAccessibleWhereRequired()
        {
            string source =
@"public class Base
{
    private protected int Field1;
    protected private int Field2;
}
 
public class Derived // : Base
{
    void M()
    {
        Base b = null;
        b.Field1 = 1;
        b.Field2 = 2;
    }
}
";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                // (12,11): error CS0122: 'Base.Field1' is inaccessible due to its protection level
                //         b.Field1 = 1;
                Diagnostic(ErrorCode.ERR_BadAccess, "Field1").WithArguments("Base.Field1").WithLocation(12, 11),
                // (13,11): error CS0122: 'Base.Field2' is inaccessible due to its protection level
                //         b.Field2 = 2;
                Diagnostic(ErrorCode.ERR_BadAccess, "Field2").WithArguments("Base.Field2").WithLocation(13, 11)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void NotInStructOrNamespace()
        {
            string source =
@"protected private struct Struct
{
    private protected int Field1;
}
";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                // (1,18): error CS1527: Elements defined in a namespace cannot be explicitly declared as private, protected, protected internal, or private protected
                // protected private struct Struct
                Diagnostic(ErrorCode.ERR_NoNamespacePrivate, "Struct").WithLocation(1, 26),
                // (3,27): error CS0666: 'Struct.Field1': new protected member declared in struct
                //     private protected int Field1;
                Diagnostic(ErrorCode.ERR_ProtectedInStruct, "Field1").WithArguments("Struct.Field1").WithLocation(3, 27)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void NotInStaticClass()
        {
            string source =
@"static class C
{
    static private protected int Field1 = 2;
}
sealed class D
{
    static private protected int Field2 = 2;
}
";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                // (7,34): warning CS0628: 'D.Field2': new protected member declared in sealed type
                //     static private protected int Field2 = 2;
                Diagnostic(ErrorCode.WRN_ProtectedInSealed, "Field2").WithArguments("D.Field2").WithLocation(7, 34),
                // (3,34): error CS1057: 'C.Field1': static classes cannot contain protected members
                //     static private protected int Field1 = 2;
                Diagnostic(ErrorCode.ERR_ProtectedInStatic, "Field1").WithArguments("C.Field1").WithLocation(3, 34)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void NestedTypes()
        {
            string source =
@"class Outer
{
    private protected class Inner
    {
    }
}
class Derived : Outer
{
    public void M()
    {
        Outer.Inner x = null;
    }
}
class NotDerived
{
    public void M()
    {
        Outer.Inner x = null; // error: Outer.Inner not accessible
    }
}
struct Struct
{
    private protected class Inner // error: protected not allowed in struct
    {
    }
}
";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                // (23,29): error CS0666: 'Struct.Inner': new protected member declared in struct
                //     private protected class Inner // error: protected not allowed in struct
                Diagnostic(ErrorCode.ERR_ProtectedInStruct, "Inner").WithArguments("Struct.Inner").WithLocation(23, 29),
                // (11,21): warning CS0219: The variable 'x' is assigned but its value is never used
                //         Outer.Inner x = null;
                Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(11, 21),
                // (18,15): error CS0122: 'Outer.Inner' is inaccessible due to its protection level
                //         Outer.Inner x = null; // error: Outer.Inner not accessible
                Diagnostic(ErrorCode.ERR_BadAccess, "Inner").WithArguments("Outer.Inner").WithLocation(18, 15)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void PermittedAccessorProtection()
        {
            string source =
@"class Class
{
    public int Prop1 { get; private protected set; }
    protected internal int Prop2 { get; private protected set; }
    protected int Prop3 { get; private protected set; }
    internal int Prop4 { get; private protected set; }
    private protected int Prop5 { get; private set; }
}";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void ForbiddenAccessorProtection_01()
        {
            string source =
@"class Class
{
    private protected int Prop1 { get; private protected set; }
    private int Prop2 { get; private protected set; }
}";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                // (3,58): error CS0273: The accessibility modifier of the 'Class.Prop1.set' accessor must be more restrictive than the property or indexer 'Class.Prop1'
                //     private protected int Prop1 { get; private protected set; }
                Diagnostic(ErrorCode.ERR_InvalidPropertyAccessMod, "set").WithArguments("Class.Prop1.set", "Class.Prop1").WithLocation(3, 58),
                // (4,48): error CS0273: The accessibility modifier of the 'Class.Prop2.set' accessor must be more restrictive than the property or indexer 'Class.Prop2'
                //     private int Prop2 { get; private protected set; }
                Diagnostic(ErrorCode.ERR_InvalidPropertyAccessMod, "set").WithArguments("Class.Prop2.set", "Class.Prop2").WithLocation(4, 48)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void ForbiddenAccessorProtection_02()
        {
            string source =
@"interface ISomething
{
    private protected int M();
}";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                // (3,27): error CS8503: The modifier 'private protected' is not valid for this item in C# 7.2. Please use language version '8.0' or greater.
                //     private protected int M();
                Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "M").WithArguments("private protected", "7.2", "8.0").WithLocation(3, 27),
                // (3,27): error CS8707: Target runtime doesn't support 'protected', 'protected internal', or 'private protected' accessibility for a member of an interface.
                //     private protected int M();
                Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportProtectedAccessForInterfaceMember, "M").WithLocation(3, 27)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void AtLeastAsRestrictivePositive_01()
        {
            string source =
@"
public class C
{
    internal class Internal {}
    protected class Protected {}
    private protected class PrivateProtected {}
    private protected void M(Internal x) {} // ok
    private protected void M(Protected x) {} // ok
    private protected void M(PrivateProtected x) {} // ok
    private protected class Nested
    {
        public void M(Internal x) {} // ok
        public void M(Protected x) {} // ok
        private protected void M(PrivateProtected x) {} // ok
    }
}
";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void AtLeastAsRestrictiveNegative_01()
        {
            string source =
@"
public class Container
{
    private protected class PrivateProtected {}
    internal void M1(PrivateProtected x) {} // error: conflicting access
    protected void M2(PrivateProtected x) {} // error: conflicting access
}
";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                // (6,20): error CS0051: Inconsistent accessibility: parameter type 'Container.PrivateProtected' is less accessible than method 'Container.M2(Container.PrivateProtected)'
                //     protected void M2(PrivateProtected x) {} // error: conflicting access
                Diagnostic(ErrorCode.ERR_BadVisParamType, "M2").WithArguments("Container.M2(Container.PrivateProtected)", "Container.PrivateProtected").WithLocation(6, 20),
                // (5,19): error CS0051: Inconsistent accessibility: parameter type 'Container.PrivateProtected' is less accessible than method 'Container.M1(Container.PrivateProtected)'
                //     internal void M1(PrivateProtected x) {} // error: conflicting access
                Diagnostic(ErrorCode.ERR_BadVisParamType, "M1").WithArguments("Container.M1(Container.PrivateProtected)", "Container.PrivateProtected").WithLocation(5, 19)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void DuplicateAccessInBinder()
        {
            string source =
@"
public class Container
{
    private public int Field;                           // 1
    private public int Property { get; set; }           // 2
    private public int M() => 1;                        // 3
    private public class C {}                           // 4
    private public struct S {}                          // 5
    private public enum E {}                            // 6
    private public event System.Action V;               // 7
    private public interface I {}                       // 8
    private public int this[int index] => 1;            // 9
    void Q() { V.Invoke(); V = null; }
}
";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                // (7,26): error CS0107: More than one protection modifier
                //     private public class C {}                           // 4
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "C").WithLocation(7, 26),
                // (8,27): error CS0107: More than one protection modifier
                //     private public struct S {}                          // 5
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "S").WithLocation(8, 27),
                // (9,25): error CS0107: More than one protection modifier
                //     private public enum E {}                            // 6
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "E").WithLocation(9, 25),
                // (11,30): error CS0107: More than one protection modifier
                //     private public interface I {}                       // 8
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "I").WithLocation(11, 30),
                // (4,24): error CS0107: More than one protection modifier
                //     private public int Field;                           // 1
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "Field").WithLocation(4, 24),
                // (5,24): error CS0107: More than one protection modifier
                //     private public int Property { get; set; }           // 2
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "Property").WithLocation(5, 24),
                // (6,24): error CS0107: More than one protection modifier
                //     private public int M() => 1;                        // 3
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "M").WithLocation(6, 24),
                // (10,40): error CS0107: More than one protection modifier
                //     private public event System.Action V;               // 7
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "V").WithLocation(10, 40),
                // (12,24): error CS0107: More than one protection modifier
                //     private public int this[int index] => 1;            // 9
                Diagnostic(ErrorCode.ERR_BadMemberProtection, "this").WithLocation(12, 24)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void NotInVersion71()
        {
            string source =
@"
public class Container
{
    private protected int Field;                           // 1
    private protected int Property { get; set; }           // 2
    private protected int M() => 1;                        // 3
    private protected class C {}                           // 4
    private protected struct S {}                          // 5
    private protected enum E {}                            // 6
    private protected event System.Action V;               // 7
    private protected interface I {}                       // 8
    private protected int this[int index] => 1;            // 9
    void Q() { V.Invoke(); V = null; }
}
";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_1)
                .VerifyDiagnostics(
                // (7,29): error CS8302: Feature 'private protected' is not available in C# 7.1. Please use language version 7.2 or greater.
                //     private protected class C {}                           // 4
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "C").WithArguments("private protected", "7.2").WithLocation(7, 29),
                // (8,30): error CS8302: Feature 'private protected' is not available in C# 7.1. Please use language version 7.2 or greater.
                //     private protected struct S {}                          // 5
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "S").WithArguments("private protected", "7.2").WithLocation(8, 30),
                // (9,28): error CS8302: Feature 'private protected' is not available in C# 7.1. Please use language version 7.2 or greater.
                //     private protected enum E {}                            // 6
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "E").WithArguments("private protected", "7.2").WithLocation(9, 28),
                // (11,33): error CS8302: Feature 'private protected' is not available in C# 7.1. Please use language version 7.2 or greater.
                //     private protected interface I {}                       // 8
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "I").WithArguments("private protected", "7.2").WithLocation(11, 33),
                // (4,27): error CS8302: Feature 'private protected' is not available in C# 7.1. Please use language version 7.2 or greater.
                //     private protected int Field;                           // 1
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "Field").WithArguments("private protected", "7.2").WithLocation(4, 27),
                // (5,27): error CS8302: Feature 'private protected' is not available in C# 7.1. Please use language version 7.2 or greater.
                //     private protected int Property { get; set; }           // 2
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "Property").WithArguments("private protected", "7.2").WithLocation(5, 27),
                // (6,27): error CS8302: Feature 'private protected' is not available in C# 7.1. Please use language version 7.2 or greater.
                //     private protected int M() => 1;                        // 3
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "M").WithArguments("private protected", "7.2").WithLocation(6, 27),
                // (10,43): error CS8302: Feature 'private protected' is not available in C# 7.1. Please use language version 7.2 or greater.
                //     private protected event System.Action V;               // 7
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "V").WithArguments("private protected", "7.2").WithLocation(10, 43),
                // (12,27): error CS8302: Feature 'private protected' is not available in C# 7.1. Please use language version 7.2 or greater.
                //     private protected int this[int index] => 1;            // 9
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "this").WithArguments("private protected", "7.2").WithLocation(12, 27)
                );
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void VerifyPrivateProtectedIL()
        {
            var text = @"
class Program
{
    private protected void M() {}
    private protected int F;
}
";
            var verifier = CompileAndVerify(
                text,
                parseOptions: TestOptions.Regular7_2,
                expectedSignatures: new[]
                {
                    Signature("Program", "M", ".method famandassem hidebysig instance System.Void M() cil managed"),
                    Signature("Program", "F", ".field famandassem instance System.Int32 F"),
                });
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void VerifyPartialPartsMatch()
        {
            var source =
@"class Outer
{
    private protected partial class Inner {}
    private           partial class Inner {}
}";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                // (3,37): error CS0262: Partial declarations of 'Outer.Inner' have conflicting accessibility modifiers
                //     private protected partial class Inner {}
                Diagnostic(ErrorCode.ERR_PartialModifierConflict, "Inner").WithArguments("Outer.Inner").WithLocation(3, 37)
                );
            source =
@"class Outer
{
    private protected partial class Inner {}
    private protected partial class Inner {}
}";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void VerifyProtectedSemantics()
        {
            var source =
@"class Base
{
    private protected void M()
    {
        System.Console.WriteLine(this.GetType().Name);
    }
}
 
class Derived : Base
{
    public void Main()
    {
        Derived derived = new Derived();
        derived.M();
        Base bb = new Base();
        bb.M(); // error 1
        Other other = new Other();
        other.M(); // error 2
    }
}
 
class Other : Base
{
}";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                // (16,12): error CS1540: Cannot access protected member 'Base.M()' via a qualifier of type 'Base'; the qualifier must be of type 'Derived' (or derived from it)
                //         bb.M(); // error 1
                Diagnostic(ErrorCode.ERR_BadProtectedAccess, "M").WithArguments("Base.M()", "Base", "Derived").WithLocation(16, 12),
                // (18,15): error CS1540: Cannot access protected member 'Base.M()' via a qualifier of type 'Other'; the qualifier must be of type 'Derived' (or derived from it)
                //         other.M(); // error 2
                Diagnostic(ErrorCode.ERR_BadProtectedAccess, "M").WithArguments("Base.M()", "Other", "Derived").WithLocation(18, 15)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void HidingAbstract()
        {
            var source =
@"abstract class A
{
    internal abstract void F();
}
abstract class B : A
{
    private protected new void F() { } // No CS0533
}";
            CreateCompilation(source, parseOptions: TestOptions.Regular7_2)
                .VerifyDiagnostics(
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void HidingInaccessible()
        {
            string source1 =
@"public class A
{
    private protected void F() { }
}
";
            var compilation1 = CreateCompilation(source1, parseOptions: TestOptions.Regular7_2);
            compilation1.VerifyDiagnostics();
 
            string source2 =
@"class B : A
{
    new void F() { } // CS0109
}
";
            CreateCompilation(source2, parseOptions: TestOptions.Regular7_2,
                references: new[] { new CSharpCompilationReference(compilation1) })
            .VerifyDiagnostics(
                // (3,14): warning CS0109: The member 'B.F()' does not hide an accessible member. The new keyword is not required.
                //     new void F() { } // CS0109
                Diagnostic(ErrorCode.WRN_NewNotRequired, "F").WithArguments("B.F()").WithLocation(3, 14)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void UnimplementedInaccessible()
        {
            string source1 =
@"public abstract class A
{
    private protected abstract void F();
}
";
            var compilation1 = CreateCompilation(source1, parseOptions: TestOptions.Regular7_2);
            compilation1.VerifyDiagnostics();
 
            string source2 =
@"class B : A // CS0534
{
}
";
            CreateCompilation(source2, parseOptions: TestOptions.Regular7_2,
                references: new[] { new CSharpCompilationReference(compilation1) })
            .VerifyDiagnostics(
                // (1,7): error CS0534: 'B' does not implement inherited abstract member 'A.F()'
                // class B : A // CS0534
                Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "B").WithArguments("B", "A.F()").WithLocation(1, 7)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void ImplementInaccessible()
        {
            string source1 =
@"public abstract class A
{
    private protected abstract void F();
}
";
            var compilation1 = CreateCompilation(source1, parseOptions: TestOptions.Regular7_2);
            compilation1.VerifyDiagnostics();
 
            string source2 =
@"class B : A // CS0534
{
    override private protected void F() {}
}
";
            CreateCompilation(source2, parseOptions: TestOptions.Regular7_2,
                references: new[] { new CSharpCompilationReference(compilation1) })
            .VerifyDiagnostics(
                // (3,37): error CS0115: 'B.F()': no suitable method found to override
                //     override private protected void F() {}
                Diagnostic(ErrorCode.ERR_OverrideNotExpected, "F").WithArguments("B.F()").WithLocation(3, 37),
                // (1,7): error CS0534: 'B' does not implement inherited abstract member 'A.F()'
                // class B : A // CS0534
                Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "B").WithArguments("B", "A.F()").WithLocation(1, 7)
                );
        }
 
        [ConditionalFact(typeof(DesktopOnly))]
        public void VerifyPPExtension()
        {
            string source = @"
static class Extensions
{
    static private protected void SomeExtension(this string s) { } // error: no pp in static class
}
 
class Client
{
    public static void M(string s)
    {
        s.SomeExtension(); // error: no accessible SomeExtension
    }
}
";
            CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Regular7_2)
            .VerifyDiagnostics(
                // (4,35): error CS1057: 'Extensions.SomeExtension(string)': static classes cannot contain protected members
                //     static private protected void SomeExtension(this string s) { } // error: no pp in static class
                Diagnostic(ErrorCode.ERR_ProtectedInStatic, "SomeExtension").WithArguments("Extensions.SomeExtension(string)").WithLocation(4, 35),
                // (11,11): error CS1061: 'string' does not contain a definition for 'SomeExtension' and no accessible extension method 'SomeExtension' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
                //         s.SomeExtension(); // error: no accessible SomeExtension
                Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "SomeExtension").WithArguments("string", "SomeExtension").WithLocation(11, 11)
                );
        }
    }
}