' 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.

Imports Microsoft.CodeAnalysis.Test.Utilities
Imports Roslyn.Test.Utilities

Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests

    Public Class InaccessibleOverridingTests
        Inherits BasicTestBase

        <WorkItem(541742, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541742")>
        <Fact>
        Public Sub EmitExplicitOverride()
            ' In order for Class3.f to override Class1.f (which it can because Class2.f is not
            ' accessible to Class3, since there is no IVT Proj2->Proj3), a explicit override must
            ' be emitted to metadata. 
            Dim proj1 = CompilationUtils.CreateCompilationWithMscorlib40(
                <compilation name="Proj1">
                    <file name="Class1.vb">
                        <![CDATA[
Imports System

<Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Proj2")> 
<Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Proj3")> 

Public Class Class1
    Friend Overridable Sub f()
        Console.WriteLine("Class1.f")
    End Sub
End Class
]]>
                    </file>
                </compilation>)
            Dim proj1ref = New VisualBasicCompilationReference(proj1)

            Dim proj2 = CompilationUtils.CreateCompilationWithMscorlib40AndReferences(
                <compilation name="Proj2">
                    <file name="Class2.vb">
                        <![CDATA[
Imports System

Public Class Class2
    Inherits Class1
    Friend Overridable Shadows Sub f()
        Console.WriteLine("Class2.f")
    End Sub
End Class
]]>
                    </file>
                </compilation>, {proj1ref})
            Dim proj2ref = New VisualBasicCompilationReference(proj2)

            Dim proj3 = CompileAndVerify(
                <compilation name="Proj3">
                    <file name="Class3.vb">
                        <![CDATA[
Imports System

Public Class Class3
    Inherits Class2

    Friend Overrides Sub f()
        Console.WriteLine("Class3.f")
    End Sub
End Class

Public Module Module1
    Public Sub Main()
        Dim x As Class1 = New Class3()
        x.f()
    End Sub
End Module
]]>
                    </file>
                </compilation>, references:={proj1ref, proj2ref}, expectedOutput:="Class3.f")

            proj3.VerifyDiagnostics()   ' no errors.
        End Sub

        <WorkItem(541742, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541742")>
        <Fact>
        Public Sub EmitExplicitOverrideOnProperty()
            ' In order for Class3.p to override Class1.p (which it can because Class2.p is not
            ' accessible to Class3, since there is no IVT Proj2->Proj3), a explicit override must
            ' be emitted to metadata. 
            Dim proj1 = CompilationUtils.CreateCompilationWithMscorlib40(
                <compilation name="Proj1">
                    <file name="Class1.vb">
                        <![CDATA[
<Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Proj2")> 
<Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Proj3")> 

Public Class Class1
    Friend Overridable ReadOnly Property P() As String
      Get
        Return "Class1.P"
      End Get
    End Property
End Class
]]>
                    </file>
                </compilation>, OutputKind.DynamicallyLinkedLibrary)
            Dim proj1ref = New VisualBasicCompilationReference(proj1)

            Dim proj2 = CompilationUtils.CreateCompilationWithMscorlib40AndVBRuntimeAndReferences(
                <compilation name="Proj2">
                    <file name="Class2.vb">
                        <![CDATA[
Public Class Class2
    Inherits Class1
    Friend Overridable Shadows ReadOnly Property P() As String
      Get
        Return "Class2.P"
      End Get
    End Property
End Class
]]>
                    </file>
                </compilation>, references:={proj1ref})
            Dim proj2ref = New VisualBasicCompilationReference(proj2)

            Dim proj3 = CompileAndVerify(
                <compilation name="Proj3">
                    <file name="Class3.vb">
                        <![CDATA[
Imports System

Public Class Class3
    Inherits Class2

    Friend Overrides ReadOnly Property P() As String
      Get
        Return "Class3.P"
      End Get
    End Property
End Class

Public Module Module1
    Public Sub Main()
        Dim x As Class1 = New Class3()
        Console.WriteLine(x.P)
    End Sub
End Module
]]>
                    </file>
                </compilation>, references:={proj1ref, proj2ref}, expectedOutput:="Class3.P")

            proj3.VerifyDiagnostics()   ' no errors.
        End Sub

        <WorkItem(541742, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541742")>
        <Fact>
        Public Sub OverrideWithInterveningFriendOverride()
            Dim proj1 = CompilationUtils.CreateCompilationWithMscorlib40(
                <compilation name="Proj1">
                    <file name="Class1.vb">
                        <![CDATA[
Imports System

<Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Proj2")> 
<Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Proj3")> 

Public Class Class1
    Friend Overridable Sub f()
        Console.WriteLine("Class1.f")
    End Sub
End Class
]]>
                    </file>
                </compilation>)
            Dim proj1ref = New VisualBasicCompilationReference(proj1)

            Dim proj2 = CompilationUtils.CreateCompilationWithMscorlib40AndReferences(
                <compilation name="Proj2">
                    <file name="Class2.vb">
                        <![CDATA[
Imports System

Public Class Class2
    Inherits Class1
    Friend Overrides Sub f()
        Console.WriteLine("Class2.f")
    End Sub
End Class
]]>
                    </file>
                </compilation>, {proj1ref})
            Dim proj2ref = New VisualBasicCompilationReference(proj2)

            Dim proj3 = CompilationUtils.CreateCompilationWithMscorlib40AndVBRuntimeAndReferences(
                <compilation name="Proj3">
                    <file name="Class3.vb">
                        <![CDATA[
Imports System

Public Class Class3
    Inherits Class2

    Friend Overrides Sub f()
        Console.WriteLine("Class3.f")
    End Sub
End Class

Public Module Module1
    Public Sub Main()
        Dim x As Class1 = New Class3()
        x.f()
    End Sub
End Module
]]>
                    </file>
                </compilation>, references:={proj1ref, proj2ref})

            CompilationUtils.AssertTheseDiagnostics(proj3,
<expected>
BC30981: 'Friend Overrides Sub f()' in class 'Class3' cannot override 'Friend Overridable Sub f()' in class 'Class1' because an intermediate class 'Class2' overrides 'Friend Overridable Sub f()' in class 'Class1' but is not accessible.
    Friend Overrides Sub f()
                         ~
</expected>)
        End Sub

        <WorkItem(541742, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541742")>
        <Fact>
        Public Sub OverridePropertyWithInterveningFriendOverride()
            Dim proj1 = CompilationUtils.CreateCompilationWithMscorlib40(
                <compilation name="Proj1">
                    <file name="Class1.vb">
                        <![CDATA[
Imports System

<Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Proj2")> 
<Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Proj3")> 

Public Class Class1
    Friend Overridable ReadOnly Property P() As String
      Get
        Return "Class1.P"
      End Get
    End Property
End Class
]]>
                    </file>
                </compilation>, OutputKind.DynamicallyLinkedLibrary)
            Dim proj1ref = New VisualBasicCompilationReference(proj1)

            Dim proj2 = CompilationUtils.CreateCompilationWithMscorlib40AndVBRuntimeAndReferences(
                <compilation name="Proj2">
                    <file name="Class2.vb">
                        <![CDATA[
Imports System

Public Class Class2
    Inherits Class1
    Friend Overrides ReadOnly Property P() As String
      Get
        Return "Class2.P"
      End Get
    End Property
End Class
]]>
                    </file>
                </compilation>, references:={proj1ref})
            Dim proj2ref = New VisualBasicCompilationReference(proj2)

            Dim proj3 = CompilationUtils.CreateCompilationWithMscorlib40AndVBRuntimeAndReferences(
                <compilation name="Proj3">
                    <file name="Class3.vb">
                        <![CDATA[
Imports System

Public Class Class3
    Inherits Class2

    Friend Overrides ReadOnly Property P() As String
      Get
        Return "Class3.P"
      End Get
    End Property
End Class

Public Module Module1
    Public Sub Main()
        Dim x As Class1 = New Class3()
        Console.WriteLine(x.P)
    End Sub
End Module
]]>
                    </file>
                </compilation>, references:={proj1ref, proj2ref})

            CompilationUtils.AssertTheseDiagnostics(proj3,
<expected>
BC30981: 'Friend Overrides ReadOnly Property P As String' in class 'Class3' cannot override 'Friend Overridable ReadOnly Property P As String' in class 'Class1' because an intermediate class 'Class2' overrides 'Friend Overridable ReadOnly Property P As String' in class 'Class1' but is not accessible.
    Friend Overrides ReadOnly Property P() As String
                                       ~
</expected>)
        End Sub

        <WorkItem(541742, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541742")>
        <Fact>
        Public Sub EmitExplicitOverrideMetadata()
            Dim p1AssemblyName = "P1" + Guid.NewGuid().ToString().Replace("-", "")
            Dim proj2ILText = <![CDATA[
.assembly extern <<P1Name>>
{
}
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                      
  .ver 4:0:0:0
}
.assembly '<<GeneratedFileName>>'
{
}
.module '<<GeneratedFileName>>.dll'

// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit Class2
       extends [<<P1Name>>]Class1
{
  .method assembly hidebysig newslot strict virtual 
          instance void  f() cil managed
  {
    // Code size       11 (0xb)
    .maxstack  8
    IL_0000:  ldstr      "Class2.P2"
    IL_0005:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000a:  ret
  } // end of method Class2::f

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [<<P1Name>>]Class1::.ctor()
    IL_0006:  ret
  } // end of method Class2::.ctor

} // end of class Class2

// =============================================================
]]>.Value.Replace("<<P1Name>>", p1AssemblyName)

            Using proj2ILFile = IlasmUtilities.CreateTempAssembly(proj2ILText, prependDefaultHeader:=False)
                Dim proj2AssemblyName = IO.Path.GetFileNameWithoutExtension(proj2ILFile.Path)
                Dim proj2Ref = MetadataReference.CreateFromImage(ReadFromFile(proj2ILFile.Path))
                Dim proj2AssemblyNameBytes As New System.Text.StringBuilder()
                proj2AssemblyNameBytes.Append(proj2AssemblyName.Length.ToString("X") + " ")
                For Each c In proj2AssemblyName
                    proj2AssemblyNameBytes.Append(AscW(c).ToString("X") & " ")
                Next

                Dim proj1ILText = <![CDATA[
    .assembly extern mscorlib
    {
      .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                      
      .ver 4:0:0:0
    }
    .assembly '<<P1Name>>'
    {
      .custom instance void [mscorlib]System.Runtime.CompilerServices.InternalsVisibleToAttribute::.ctor(string) = ( 01 00 ]]>.Value & proj2AssemblyNameBytes.ToString() &
                <![CDATA[ 00 00 )                            
      .custom instance void [mscorlib]System.Runtime.CompilerServices.InternalsVisibleToAttribute::.ctor(string) = ( 01 00 02 50 33 00 00 )                            // ...P3..
    }
    .module '<<P1Name>>.dll'

    // =============== CLASS MEMBERS DECLARATION ===================

    .class public auto ansi beforefieldinit Class1
           extends [mscorlib]System.Object
    {
      .method assembly hidebysig newslot strict virtual 
              instance void  f() cil managed
      {
        // Code size       11 (0xb)
        .maxstack  8
        IL_0000:  ldstr      "Class1.f"
        IL_0005:  call       void [mscorlib]System.Console::WriteLine(string)
        IL_000a:  ret
      } // end of method Class1::f

      .method public hidebysig specialname rtspecialname 
              instance void  .ctor() cil managed
      {
        // Code size       7 (0x7)
        .maxstack  8
        IL_0000:  ldarg.0
        IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
        IL_0006:  ret
      } // end of method Class1::.ctor

    } // end of class Class1    ]]>.Value
                proj1ILText = proj1ILText.Replace("<<P1Name>>", p1AssemblyName)

                Using proj1ILFile = IlasmUtilities.CreateTempAssembly(proj1ILText, prependDefaultHeader:=False)
                    Dim proj1Ref = MetadataReference.CreateFromImage(ReadFromFile(proj1ILFile.Path))

                    Dim proj3 = CompileAndVerify(
                        <compilation name="P3">
                            <file name="Class3.vb">
                                <![CDATA[
Imports System

Public Class Class3
    Inherits Class2

    Friend Overrides Sub f()
        Console.WriteLine("Class3.f")
    End Sub
End Class

Public Module Module1
    Public Sub Main()
        Dim x As Class1 = New Class3()
        x.f()
    End Sub
End Module
]]>
                            </file>
                        </compilation>, references:={proj1Ref, proj2Ref}, expectedOutput:="Class3.f")

                    proj3.VerifyDiagnostics()   ' no errors.

                End Using
            End Using
        End Sub

        <WorkItem(541742, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541742")>
        <Fact>
        Public Sub OverrideWithInterveningFriendMetadata()
            Dim proj2ILText = <![CDATA[
.assembly extern P1
{
}
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                      
  .ver 4:0:0:0
}
.assembly P2
{
}
.module P2.dll

// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit Class2
       extends [P1]Class1
{
  .method assembly hidebysig strict virtual 
          instance void  f() cil managed
  {
    // Code size       11 (0xb)
    .maxstack  8
    IL_0000:  ldstr      "Class2.P2"
    IL_0005:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000a:  ret
  } // end of method Class2::f

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [P1]Class1::.ctor()
    IL_0006:  ret
  } // end of method Class2::.ctor

} // end of class Class2

// =============================================================
]]>.Value

            Using proj2ILFile = IlasmUtilities.CreateTempAssembly(proj2ILText, prependDefaultHeader:=False)
                Dim proj2AssemblyName = IO.Path.GetFileNameWithoutExtension(proj2ILFile.Path)
                Dim proj2Ref = MetadataReference.CreateFromImage(ReadFromFile(proj2ILFile.Path))

                Dim proj1ILText = <![CDATA[
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                      
  .ver 4:0:0:0
}
.assembly P1
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.InternalsVisibleToAttribute::.ctor(string) = ( 01 00 02 50 32 00 00 )                            // ...P2..
  .custom instance void [mscorlib]System.Runtime.CompilerServices.InternalsVisibleToAttribute::.ctor(string) = ( 01 00 02 50 33 00 00 )                            // ...P3..
}
.module P1.dll

// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit Class1
       extends [mscorlib]System.Object
{
  .method assembly newslot strict virtual 
          instance void  f() cil managed
  {
    // Code size       11 (0xb)
    .maxstack  8
    IL_0000:  ldstr      "Class1.f"
    IL_0005:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000a:  ret
  } // end of method Class1::f

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Class1::.ctor

} // end of class Class1    ]]>.Value

                Using proj1ILFile = IlasmUtilities.CreateTempAssembly(proj1ILText, prependDefaultHeader:=False)
                    Dim proj1Ref = MetadataReference.CreateFromImage(ReadFromFile(proj1ILFile.Path))

                    Dim proj3 = CompilationUtils.CreateCompilationWithMscorlib40AndVBRuntimeAndReferences(
                        <compilation name="P3">
                            <file name="Class3.vb">
                                <![CDATA[
Imports System

Public Class Class3
    Inherits Class2

    Friend Overrides Sub f()
        Console.WriteLine("Class3.f")
    End Sub
End Class

Public Module Module1
    Public Sub Main()
        Dim x As Class1 = New Class3()
        x.f()
    End Sub
End Module
]]>
                            </file>
                        </compilation>, references:={proj1Ref, proj2Ref})

                    CompilationUtils.AssertTheseDiagnostics(proj3,
        <expected>
BC30981: 'Friend Overrides Sub f()' in class 'Class3' cannot override 'Friend Overridable Sub f()' in class 'Class1' because an intermediate class 'Class2' overrides 'Friend Overridable Sub f()' in class 'Class1' but is not accessible.
    Friend Overrides Sub f()
                         ~
</expected>)

                End Using
            End Using
        End Sub

        <WorkItem(541742, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541742")>
        <Fact>
        Public Sub CannotOverrideInAccessibleMemberInMetadata()
            Dim customIL = <![CDATA[

//  Microsoft (R) .NET Framework IL Disassembler.  Version 4.0.30319.1



// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                      
  .ver 4:0:0:0
}
.assembly extern Microsoft.VisualBasic
{
  .ver 10:0:0:0
  .publickeytoken = (b0 3f 5f 7f 11 d5 0a 3a)
}
.assembly extern System
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                      
  .ver 4:0:0:0
}
.assembly '<<GeneratedFileName>>'
{
}
.module '<<GeneratedFileName>>.dll'


// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi Cls1
       extends [mscorlib]System.Object
{
  .method public specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Cls1::.ctor

  .method private instance void  goo() cil managed
  {
    // Code size       1 (0x1)
    .maxstack  8
    IL_0000:  ret
  } // end of method Cls1::goo

} // end of class Cls1
]]>

            ' Because the private is defined in another assembly, we don't import it.
            ' So BC30284 is reasonable, and Dev10 does the same.

            Using reference = IlasmUtilities.CreateTempAssembly(customIL.Value, prependDefaultHeader:=False)
                Dim ilRef = MetadataReference.CreateFromImage(ReadFromFile(reference.Path))
                Dim compilation1 = CompilationUtils.CreateCompilationWithMscorlib40AndReferences(
                    <compilation name="CannotOverrideInAccessibleMemberInMetadata">
                        <file name="a.vb">
                        Class Cls2
                            Inherits Cls1
                            Private Overrides Sub goo()
                            End Sub
                        End Class
                    </file>
                    </compilation>, references:={ilRef})

                Dim expectedErrors1 = <errors>
BC30284: sub 'goo' cannot be declared 'Overrides' because it does not override a sub in a base class.
                            Private Overrides Sub goo()
                                                  ~~~
                 </errors>

                CompilationUtils.AssertTheseDiagnostics(compilation1, expectedErrors1)
            End Using
        End Sub

        <WorkItem(541742, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541742")>
        <Fact>
        Public Sub Bug14346()
            Dim customIL = <![CDATA[
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                      
  .ver 4:0:0:0
}
.assembly '<<GeneratedFileName>>'
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.InternalsVisibleToAttribute::.ctor(string) = ( 01 00 08 42 75 67 31 34 33 34 36 00 00 )          // ...Bug14346..
}
.module '<<GeneratedFileName>>.dll'

.class public auto ansi beforefieldinit CaseMembers
       extends [mscorlib]System.Object
{
  .method family hidebysig newslot virtual 
          instance int32  goo() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  1
    .locals init (int32 V_0)
    IL_0000:  nop
    IL_0001:  ldc.i4.1
    IL_0002:  stloc.0
    IL_0003:  br.s       IL_0005

    IL_0005:  ldloc.0
    IL_0006:  ret
  } // end of method CaseMembers::goo

  .method assembly hidebysig instance int32 
          Goo() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  1
    .locals init (int32 V_0)
    IL_0000:  nop
    IL_0001:  ldc.i4.2
    IL_0002:  stloc.0
    IL_0003:  br.s       IL_0005

    IL_0005:  ldloc.0
    IL_0006:  ret
  } // end of method CaseMembers::Goo

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method CaseMembers::.ctor

} // end of class CaseMembers

]]>

            Using reference = IlasmUtilities.CreateTempAssembly(customIL.Value, prependDefaultHeader:=False)
                Dim ilRef = MetadataReference.CreateFromImage(ReadFromFile(reference.Path))
                Dim compilation1 = CompilationUtils.CreateCompilationWithMscorlib40AndVBRuntimeAndReferences(
                    <compilation name="Bug14346">
                        <file name="a.vb">
Option Strict On
Imports System
Module m1
    class Car : Inherits CaseMembers
        protected overrides function goo() as integer
            return MyBase.goo()
        end function
        public function bar() as integer
            return goo()
        end function
    end class
    sub Main()
        dim c as new Car
        Console.WriteLine( "running test..." )
        Console.WriteLine( c.bar() )
    end sub
End Module
                    </file>
                    </compilation>, references:={ilRef})

                CompilationUtils.AssertNoDiagnostics(compilation1)
            End Using
        End Sub

    End Class
End Namespace
