File: Symbols\Source\OverrideHidingHelper.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' 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 System.Collections.Immutable
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
 
    ''' <summary>
    ''' Methods, Properties, and Events all can override or hide members. 
    ''' This class has helper methods and extensions for sharing by multiple symbol types.
    ''' </summary>
    Friend Class OverrideHidingHelper
        ''' <summary>
        ''' Check for overriding and hiding errors in container and report them via diagnostics.
        ''' </summary>
        ''' <param name="container">Containing type to check. Should be an original definition.</param>
        ''' <param name="diagnostics">Place diagnostics here.</param>
        Public Shared Sub CheckHidingAndOverridingForType(container As SourceMemberContainerTypeSymbol, diagnostics As BindingDiagnosticBag)
            Debug.Assert(container.IsDefinition) ' Don't do this on constructed types
 
            Select Case container.TypeKind
                Case TypeKind.Class, TypeKind.Interface, TypeKind.Structure
                    CheckMembersAgainstBaseType(container, diagnostics)
                    CheckAllAbstractsAreOverriddenAndNotHidden(container, diagnostics)
 
                Case Else
                    ' Modules, Enums and Delegates have nothing to do.
            End Select
        End Sub
 
        ' Determine if two method or property signatures match, using the rules in 4.1.1, i.e., ByRef mismatches, 
        ' differences in optional parameters, custom modifiers, return type are not considered. An optional parameter
        ' matches if the corresponding parameter in the other signature is not there, optional of any type, 
        ' or non-optional of matching type.
        '
        ' Note that this sense of matching is not transitive. I.e.
        '   A)  f(x as integer)
        '   B)  f(x as integer, optional y as String = "")
        '   C)  f(x as integer, y As String)
        ' A matches B, B matches C, but A doesn't match C
        '
        ' Note that (A) and (B) above do match in terms of Dev10 behavior when we look for overridden 
        ' methods/properties. We still keep this behavior in Roslyn to be able to generate the same 
        ' error in the following case:
        '
        '   Class Base
        '       Public Overridable Sub f(x As Integer)
        '       End Sub
        '   End Class
        '
        '   Class Derived
        '       Inherits Base
        '       Public Overrides Sub f(x As Integer, Optional y As String = "")
        '       End Sub
        '   End Class
        '
        ' >>> error BC30308: 'Public Overrides Sub f(x As Integer, [y As String = ""])' cannot override 
        '                    'Public Overridable Sub f(x As Integer)' because they differ by optional parameters.
        '
        ' In this sense the method returns True if signatures match enough to be 
        ' considered a candidate of overridden member.
        '
        ' But for new overloading rules (overloading based on optional parameters) introduced in Dev11 
        ' we also need more detailed info on the two members being compared, namely do their signatures 
        ' also match taking into account total parameter count and parameter optionality (optional/required)? 
        ' We return this information in 'exactMatch' parameter.
        '
        ' So when searching for overridden members we prefer exactly matched candidates in case we could 
        ' find them. This helps properly find overridden members in the following case:
        '
        '   Class Base
        '       Public Overridable Sub f(x As Integer)
        '       End Sub
        '       Public Overridable Sub f(x As Integer, Optional y As String = "")
        '       End Sub
        '   End Class
        '
        '   Class Derived
        '       Inherits Base
        '       Public Overrides Sub f(x As Integer) 
        '       End Sub
        '       Public Overrides Sub f(x As Integer, Optional y As String = "")  ' << Dev11 Beta reports BC30308
        '       End Sub
        '   End Class
        '
        ' Note that Dev11 Beta wrongly reports BC30308 on the last Sub in this case.
        '
        Public Shared Function SignaturesMatch(sym1 As Symbol, sym2 As Symbol, <Out()> ByRef exactMatch As Boolean, <Out()> ByRef exactMatchIgnoringCustomModifiers As Boolean) As Boolean
            ' NOTE: we should NOT ignore extra required parameters as for overloading
            Const mismatchesForOverriding As SymbolComparisonResults =
                (SymbolComparisonResults.AllMismatches And (Not SymbolComparisonResults.MismatchesForConflictingMethods)) Or
                SymbolComparisonResults.CustomModifierMismatch
 
            ' 'Exact match' means that the number of parameters and 
            ' parameter 'optionality' match on two symbol candidates.
            Const exactMatchIgnoringCustomModifiersMask As SymbolComparisonResults =
                SymbolComparisonResults.TotalParameterCountMismatch Or SymbolComparisonResults.OptionalParameterTypeMismatch
 
            ' Note that exact match doesn't care about tuple element names.
            Const exactMatchMask As SymbolComparisonResults =
                exactMatchIgnoringCustomModifiersMask Or SymbolComparisonResults.CustomModifierMismatch
 
            Dim results As SymbolComparisonResults = DetailedSignatureCompare(sym1, sym2, mismatchesForOverriding)
 
            ' no match
            If (results And Not exactMatchMask) <> 0 Then
                exactMatch = False
                exactMatchIgnoringCustomModifiers = False
                Return False
            End If
 
            ' match
            exactMatch = (results And exactMatchMask) = 0
            exactMatchIgnoringCustomModifiers = (results And exactMatchIgnoringCustomModifiersMask) = 0
 
            Debug.Assert(Not exactMatch OrElse exactMatchIgnoringCustomModifiers)
            Return True
        End Function
 
        Friend Shared Function DetailedSignatureCompare(
            sym1 As Symbol,
            sym2 As Symbol,
            comparisons As SymbolComparisonResults,
            Optional stopIfAny As SymbolComparisonResults = 0
        ) As SymbolComparisonResults
            If sym1.Kind = SymbolKind.Property Then
                Return PropertySignatureComparer.DetailedCompare(DirectCast(sym1, PropertySymbol), DirectCast(sym2, PropertySymbol), comparisons, stopIfAny)
            Else
                Return MethodSignatureComparer.DetailedCompare(DirectCast(sym1, MethodSymbol), DirectCast(sym2, MethodSymbol), comparisons, stopIfAny)
            End If
        End Function
 
        ''' <summary>
        ''' Check each member of container for constraints against the base type. For methods and properties and events,
        ''' checking overriding and hiding constraints. For other members, just check for hiding issues.
        ''' </summary>
        ''' <param name="container">Containing type to check. Should be an original definition.</param>
        ''' <param name="diagnostics">Place diagnostics here.</param>
        ''' <remarks></remarks>
        Private Shared Sub CheckMembersAgainstBaseType(container As SourceMemberContainerTypeSymbol, diagnostics As BindingDiagnosticBag)
            For Each member In container.GetMembers()
                If CanOverrideOrHide(member) Then
                    Select Case member.Kind
                        Case SymbolKind.Method
                            Dim methodMember = DirectCast(member, MethodSymbol)
                            If Not methodMember.IsAccessor Then
                                If methodMember.IsOverrides Then
                                    OverrideHidingHelper(Of MethodSymbol).CheckOverrideMember(methodMember, methodMember.OverriddenMembers, diagnostics)
                                ElseIf methodMember.IsNotOverridable Then
                                    'Method is not marked as Overrides but is marked as Not Overridable
                                    diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_NotOverridableRequiresOverrides), methodMember.Locations(0)))
                                End If
                            End If
                        Case SymbolKind.Property
                            Dim propMember = DirectCast(member, PropertySymbol)
                            If propMember.IsOverrides Then
                                OverrideHidingHelper(Of PropertySymbol).CheckOverrideMember(propMember, propMember.OverriddenMembers, diagnostics)
                            ElseIf propMember.IsNotOverridable Then
                                diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_NotOverridableRequiresOverrides), propMember.Locations(0)))
                            End If
                    End Select
 
                    ' TODO: only do this check if CheckOverrideMember didn't find an error?
                    CheckShadowing(container, member, diagnostics)
                End If
            Next
        End Sub
 
        ''' <summary>
        ''' If the "container" is a non-MustInherit, make sure it has no MustOverride Members
        ''' If "container" is a non-MustInherit inheriting from a MustInherit, make sure that all MustOverride members
        ''' have been overridden.
        ''' If "container" is a MustInherit inheriting from a MustInherit, make sure that no MustOverride members
        ''' have been shadowed.
        ''' </summary>
        Private Shared Sub CheckAllAbstractsAreOverriddenAndNotHidden(container As NamedTypeSymbol, diagnostics As BindingDiagnosticBag)
 
            ' Check that a non-MustInherit class doesn't have any MustOverride members
            If Not (container.IsMustInherit OrElse container.IsNotInheritable) Then
                For Each member In container.GetMembers()
                    If member.IsMustOverride Then
                        diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_MustOverridesInClass1, container.Name), container.Locations(0)))
                        Exit For
                    End If
                Next
            End If
 
            Dim baseType As NamedTypeSymbol = container.BaseTypeNoUseSiteDiagnostics
 
            If baseType IsNot Nothing AndAlso baseType.IsMustInherit Then
                ' Check that all MustOverride members in baseType or one of its bases are overridden/not shadowed somewhere along the chain.
                ' Do this by accumulating a set of all the methods that have been overridden, if we encounter a MustOverride
                ' method that is not in the set, then report it. We can do this in a single pass up the base chain.
 
                Dim overriddenMembers As HashSet(Of Symbol) = New HashSet(Of Symbol)()
                Dim unimplementedMembers As ArrayBuilder(Of Symbol) = ArrayBuilder(Of Symbol).GetInstance()
 
                Dim currType = container
                While currType IsNot Nothing
                    For Each member In currType.GetMembers()
                        If CanOverrideOrHide(member) AndAlso Not member.IsAccessor Then  ' accessors handled by their containing properties.
                            If member.IsOverrides Then
                                Dim overriddenMember = GetOverriddenMember(member)
                                If overriddenMember IsNot Nothing Then
                                    overriddenMembers.Add(GetOverriddenMember(member))
                                End If
                            End If
 
                            If member.IsMustOverride AndAlso currType IsNot container Then
                                If Not overriddenMembers.Contains(member) Then
                                    unimplementedMembers.Add(member)
                                End If
                            End If
 
                        End If
                    Next
 
                    currType = currType.BaseTypeNoUseSiteDiagnostics
                End While
 
                If unimplementedMembers.Any Then
                    If container.IsMustInherit Then
                        ' It is OK for a IsMustInherit type to have unimplemented abstract members. But, it is not allowed
                        ' to shadow them. Check each one to see if it is shadowed by a member of "container". Don't report for
                        ' accessor hiding accessor, because we'll report it on the property.
                        Dim hidingSymbols As New HashSet(Of Symbol) ' don't report more than once per hiding symbols
 
                        For Each mustOverrideMember In unimplementedMembers
                            For Each hidingMember In container.GetMembers(mustOverrideMember.Name)
                                If DoesHide(hidingMember, mustOverrideMember) AndAlso Not hidingSymbols.Contains(hidingMember) Then
                                    ReportShadowingMustOverrideError(hidingMember, mustOverrideMember, diagnostics)
                                    hidingSymbols.Add(hidingMember)
                                End If
                            Next
                        Next
                    Else
                        ' This is not a IsMustInherit type. Some members should be been overridden but weren't.
                        ' Create a single error that lists all of the unimplemented members.
                        Dim diagnosticInfos = ArrayBuilder(Of DiagnosticInfo).GetInstance(unimplementedMembers.Count)
 
                        For Each member In unimplementedMembers
                            If Not member.IsAccessor Then
                                If member.Kind = SymbolKind.Event Then
                                    diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_MustInheritEventNotOverridden,
                                                                                            member,
                                                                                            CustomSymbolDisplayFormatter.QualifiedName(member.ContainingType),
                                                                                            CustomSymbolDisplayFormatter.ShortErrorName(container)),
                                                    container.Locations(0)))
                                Else
                                    diagnosticInfos.Add(ErrorFactory.ErrorInfo(ERRID.ERR_UnimplementedMustOverride, member.ContainingType, member))
                                End If
                            Else
                                ' accessor is reported on the containing property.
                                Debug.Assert(unimplementedMembers.Contains(DirectCast(member, MethodSymbol).AssociatedSymbol))
                            End If
                        Next
 
                        If diagnosticInfos.Count > 0 Then
                            diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_BaseOnlyClassesMustBeExplicit2,
                                                                   CustomSymbolDisplayFormatter.ShortErrorName(container),
                                                                   New CompoundDiagnosticInfo(diagnosticInfos.ToArrayAndFree())),
                                                       container.Locations(0)))
                        Else
                            diagnosticInfos.Free()
                        End If
                    End If
                End If
 
                unimplementedMembers.Free()
            End If
        End Sub
 
        ' Compare two symbols of the same name to see if one actually does hide the other.
        Private Shared Function DoesHide(hidingMember As Symbol, hiddenMember As Symbol) As Boolean
            Debug.Assert(IdentifierComparison.Equals(hidingMember.Name, hiddenMember.Name))
 
            Select Case hidingMember.Kind
                Case SymbolKind.Method
                    If hidingMember.IsOverloads AndAlso hiddenMember.Kind = SymbolKind.Method Then
                        Dim hidingMethod = DirectCast(hidingMember, MethodSymbol)
                        If hidingMethod.IsOverrides Then
                            ' For Dev10 compatibility, an override is not considered as hiding (see bug 11728)
                            Return False
                        Else
                            Dim exactMatchIgnoringCustomModifiers As Boolean = False
                            Return OverrideHidingHelper(Of MethodSymbol).SignaturesMatch(hidingMethod, DirectCast(hiddenMember, MethodSymbol), Nothing, exactMatchIgnoringCustomModifiers) AndAlso exactMatchIgnoringCustomModifiers
                        End If
                    Else
                        Return True
                    End If
 
                Case SymbolKind.Property
                    If hidingMember.IsOverloads AndAlso hiddenMember.Kind = SymbolKind.Property Then
                        Dim hidingProperty = DirectCast(hidingMember, PropertySymbol)
                        If hidingProperty.IsOverrides Then
                            ' For Dev10 compatibility, an override is not considered as hiding (see bug 11728)
                            Return False
                        Else
                            Dim exactMatchIgnoringCustomModifiers As Boolean = False
                            Return OverrideHidingHelper(Of PropertySymbol).SignaturesMatch(hidingProperty, DirectCast(hiddenMember, PropertySymbol), Nothing, exactMatchIgnoringCustomModifiers) AndAlso exactMatchIgnoringCustomModifiers
                        End If
                    Else
                        Return True
                    End If
 
                Case Else
                    Return True
            End Select
        End Function
 
        ''' <summary>
        ''' Report any diagnostics related to shadowing for a member.
        ''' </summary>
        Protected Shared Sub CheckShadowing(container As SourceMemberContainerTypeSymbol,
                                            member As Symbol,
                                            diagnostics As BindingDiagnosticBag)
            Dim memberIsOverloads = member.IsOverloads()
            Dim warnForHiddenMember As Boolean = Not member.ShadowsExplicitly
 
            If Not warnForHiddenMember Then
                Return ' short circuit unnecessary checks.
            End If
 
            If container.IsInterfaceType() Then
                For Each currentBaseInterface In container.AllInterfacesNoUseSiteDiagnostics
                    CheckShadowingInBaseType(container, member, memberIsOverloads, currentBaseInterface, diagnostics, warnForHiddenMember)
                Next
            Else
                Dim currentBase As NamedTypeSymbol = container.BaseTypeNoUseSiteDiagnostics
                While currentBase IsNot Nothing
                    CheckShadowingInBaseType(container, member, memberIsOverloads, currentBase, diagnostics, warnForHiddenMember)
 
                    currentBase = currentBase.BaseTypeNoUseSiteDiagnostics
                End While
            End If
        End Sub
 
        ' Check shadowing against members in one base type.
        Private Shared Sub CheckShadowingInBaseType(container As SourceMemberContainerTypeSymbol,
                                                    member As Symbol,
                                                    memberIsOverloads As Boolean,
                                                    baseType As NamedTypeSymbol,
                                                    diagnostics As BindingDiagnosticBag,
                                                    ByRef warnForHiddenMember As Boolean)
            Debug.Assert(container.IsDefinition)
 
            If warnForHiddenMember Then
                For Each hiddenMember In baseType.GetMembers(member.Name)
                    If AccessCheck.IsSymbolAccessible(hiddenMember, container, Nothing, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded) AndAlso
                       (Not memberIsOverloads OrElse
                        hiddenMember.Kind <> member.Kind OrElse
                        hiddenMember.IsWithEventsProperty OrElse
                        (member.Kind = SymbolKind.Method AndAlso DirectCast(member, MethodSymbol).IsUserDefinedOperator() <> DirectCast(hiddenMember, MethodSymbol).IsUserDefinedOperator()) OrElse
                        member.IsAccessor() <> hiddenMember.IsAccessor) AndAlso
                       Not (member.IsAccessor() AndAlso hiddenMember.IsAccessor) Then
 
                        'special case for classes of different arity . Do not warn in such case
                        If member.Kind = SymbolKind.NamedType AndAlso
                            hiddenMember.Kind = SymbolKind.NamedType AndAlso
                            member.GetArity <> hiddenMember.GetArity Then
 
                            Continue For
                        End If
 
                        ' Found an accessible member we are hiding and not overloading.
                        ' We don't warn if accessor hides accessor, because we will warn on the containing properties instead.
 
                        ' Give warning for shadowing hidden member
                        ReportShadowingDiagnostic(member, hiddenMember, diagnostics)
                        warnForHiddenMember = False  ' don't warn for more than one hidden member.
                        Exit For
                    End If
                Next
            End If
        End Sub
 
        ' Report diagnostic for one member shadowing another, but no Shadows modifier was present.
        Private Shared Sub ReportShadowingDiagnostic(hidingMember As Symbol,
                                                     hiddenMember As Symbol,
                                                     diagnostics As BindingDiagnosticBag)
 
            Debug.Assert(Not (hidingMember.IsAccessor() AndAlso hiddenMember.IsAccessor))
 
            Dim associatedhiddenSymbol = hiddenMember.ImplicitlyDefinedBy
            If associatedhiddenSymbol Is Nothing AndAlso hiddenMember.IsUserDefinedOperator() AndAlso Not hidingMember.IsUserDefinedOperator() Then
                ' For the purpose of this check, operator methods are treated as implicitly defined by themselves.
                associatedhiddenSymbol = hiddenMember
            End If
 
            Dim associatedhidingSymbol = hidingMember.ImplicitlyDefinedBy
            If associatedhidingSymbol Is Nothing AndAlso hidingMember.IsUserDefinedOperator() AndAlso Not hiddenMember.IsUserDefinedOperator() Then
                ' For the purpose of this check, operator methods are treated as implicitly defined by themselves.
                associatedhidingSymbol = hidingMember
            End If
 
            If associatedhiddenSymbol IsNot Nothing Then
                If associatedhidingSymbol IsNot Nothing Then
                    If Not IdentifierComparison.Equals(associatedhiddenSymbol.Name,
                                                       associatedhidingSymbol.Name) Then
                        ' both members are defined implicitly by members of different names
                        diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.WRN_SynthMemberShadowsSynthMember7,
                                           associatedhidingSymbol.GetKindText(),
                                           AssociatedSymbolName(associatedhidingSymbol),
                                           hidingMember.Name,
                                           associatedhiddenSymbol.GetKindText(),
                                           AssociatedSymbolName(associatedhiddenSymbol),
                                           hiddenMember.ContainingType.GetKindText(),
                                           CustomSymbolDisplayFormatter.ShortErrorName(hiddenMember.ContainingType)),
                               hidingMember.Locations(0)))
                    End If
 
                    Return
                End If
                ' explicitly defined member hiding implicitly defined member
                diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.WRN_MemberShadowsSynthMember6,
                                                           hidingMember.GetKindText(), hidingMember.Name,
                                                           associatedhiddenSymbol.GetKindText(), AssociatedSymbolName(associatedhiddenSymbol), hiddenMember.ContainingType.GetKindText(),
                                                           CustomSymbolDisplayFormatter.ShortErrorName(hiddenMember.ContainingType)),
                                               hidingMember.Locations(0)))
            ElseIf associatedhidingSymbol IsNot Nothing Then
                ' implicitly defined member hiding explicitly defined member
                diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.WRN_SynthMemberShadowsMember5,
                                                           associatedhidingSymbol.GetKindText(), AssociatedSymbolName(associatedhidingSymbol),
                                                           hidingMember.Name, hiddenMember.ContainingType.GetKindText(),
                                                           CustomSymbolDisplayFormatter.ShortErrorName(hiddenMember.ContainingType)),
                                               associatedhidingSymbol.Locations(0)))
            ElseIf hidingMember.Kind = hiddenMember.Kind AndAlso
                (hidingMember.Kind = SymbolKind.Property OrElse hidingMember.Kind = SymbolKind.Method) AndAlso
                Not (hiddenMember.IsWithEventsProperty OrElse hidingMember.IsWithEventsProperty) Then
 
                ' method hiding method or property hiding property; message depends on if hidden symbol is overridable.
                Dim id As ERRID
                If hiddenMember.IsOverridable OrElse hiddenMember.IsOverrides OrElse (hiddenMember.IsMustOverride AndAlso Not hiddenMember.ContainingType.IsInterface) Then
                    id = ERRID.WRN_MustOverride2
                Else
                    id = ERRID.WRN_MustOverloadBase4
                End If
 
                diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(id,
                                                           hidingMember.GetKindText(), hidingMember.Name, hiddenMember.ContainingType.GetKindText(),
                                                           CustomSymbolDisplayFormatter.ShortErrorName(hiddenMember.ContainingType)),
                                               hidingMember.Locations(0)))
            Else
                ' all other hiding scenarios.
                Debug.Assert(hidingMember.Locations(0).IsInSource)
                diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.WRN_OverrideType5,
                                                           hidingMember.GetKindText(), hidingMember.Name, hiddenMember.GetKindText(), hiddenMember.ContainingType.GetKindText(),
                                                           CustomSymbolDisplayFormatter.ShortErrorName(hiddenMember.ContainingType)),
                                               hidingMember.Locations(0)))
            End If
 
        End Sub
 
        Public Shared Function AssociatedSymbolName(associatedSymbol As Symbol) As String
            Return If(associatedSymbol.IsUserDefinedOperator(),
                      SyntaxFacts.GetText(OverloadResolution.GetOperatorTokenKind(associatedSymbol.Name)),
                      associatedSymbol.Name)
        End Function
 
        ' Report diagnostic for a member shadowing a MustOverride.
        Private Shared Sub ReportShadowingMustOverrideError(hidingMember As Symbol,
                                                            hiddenMember As Symbol,
                                                            diagnostics As BindingDiagnosticBag)
            Debug.Assert(hidingMember.Locations(0).IsInSource)
 
            If hidingMember.IsAccessor() Then
                ' accessor hiding non-accessorTODO
                Dim associatedHidingSymbol = DirectCast(hidingMember, MethodSymbol).AssociatedSymbol
                diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_SynthMemberShadowsMustOverride5,
                                                                      hidingMember,
                                                                      associatedHidingSymbol.GetKindText(), associatedHidingSymbol.Name,
                                                                      hiddenMember.ContainingType.GetKindText(),
                                                                      CustomSymbolDisplayFormatter.ShortErrorName(hiddenMember.ContainingType)),
                                               hidingMember.Locations(0)))
            Else
                ' Basic hiding case
                diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_CantShadowAMustOverride1, hidingMember),
                                               hidingMember.Locations(0)))
            End If
        End Sub
 
        ''' <summary>
        ''' Some symbols do not participate in overriding/hiding (e.g. constructors). Accessors are consider
        ''' to override or hide.
        ''' </summary>
        Friend Shared Function CanOverrideOrHide(sym As Symbol) As Boolean
            If sym.Kind <> SymbolKind.Method Then
                Return True
            Else
 
                Select Case DirectCast(sym, MethodSymbol).MethodKind
                    Case MethodKind.LambdaMethod, MethodKind.Constructor, MethodKind.SharedConstructor
                        Return False
                    Case MethodKind.Conversion, MethodKind.DelegateInvoke, MethodKind.UserDefinedOperator, MethodKind.Ordinary, MethodKind.DeclareMethod,
                        MethodKind.EventAdd, MethodKind.EventRaise, MethodKind.EventRemove,
                        MethodKind.PropertyGet, MethodKind.PropertySet
                        Return True
                    Case Else
                        Debug.Assert(False, String.Format("Unexpected method kind '{0}'", DirectCast(sym, MethodSymbol).MethodKind))
                        Return False
                End Select
            End If
        End Function
 
        ' If this member overrides another member, return that overridden member, else return Nothing.
        Protected Shared Function GetOverriddenMember(sym As Symbol) As Symbol
            Select Case sym.Kind
                Case SymbolKind.Method
                    Return DirectCast(sym, MethodSymbol).OverriddenMethod
                Case SymbolKind.Property
                    Return DirectCast(sym, PropertySymbol).OverriddenProperty
                Case SymbolKind.Event
                    Return DirectCast(sym, EventSymbol).OverriddenEvent
            End Select
 
            Return Nothing
        End Function
 
        ''' <summary>
        ''' If a method had a virtual inaccessible override, then an explicit override in metadata is needed
        ''' to make it really override what it intends to override, and "skip" the inaccessible virtual
        ''' method.
        ''' </summary>
        Public Shared Function RequiresExplicitOverride(method As MethodSymbol) As Boolean
            If method.IsAccessor Then
                If TypeOf method.AssociatedSymbol Is EventSymbol Then
                    ' VB does not override events
                    Return False
                End If
 
                Return RequiresExplicitOverride(DirectCast(method.AssociatedSymbol, PropertySymbol))
            End If
 
            If method.OverriddenMethod IsNot Nothing Then
                For Each inaccessibleOverride In method.OverriddenMembers.InaccessibleMembers
                    If inaccessibleOverride.IsOverridable OrElse inaccessibleOverride.IsMustOverride OrElse inaccessibleOverride.IsOverrides Then
                        Return True
                    End If
                Next
            End If
 
            Return False
        End Function
 
        Private Shared Function RequiresExplicitOverride(prop As PropertySymbol) As Boolean
            If prop.OverriddenProperty IsNot Nothing Then
                For Each inaccessibleOverride In prop.OverriddenMembers.InaccessibleMembers
                    If inaccessibleOverride.IsOverridable OrElse inaccessibleOverride.IsMustOverride OrElse inaccessibleOverride.IsOverrides Then
                        Return True
                    End If
                Next
            End If
 
            Return False
        End Function
    End Class
 
    ''' <summary>
    ''' Many of the methods want to generically work on properties, methods (and maybe events) as TSymbol. We put all these
    ''' methods into a generic class for convenience.
    ''' </summary>
    Friend Class OverrideHidingHelper(Of TSymbol As Symbol)
        Inherits OverrideHidingHelper
 
        ' Comparer for comparing signatures of TSymbols in a runtime-equivalent way.
        ' It is not ReadOnly because it is initialized by a Shared Sub New of another instance of this class.
#Disable Warning IDE0044 ' Add readonly modifier - Adding readonly generates compile error in the constructor. - see https://github.com/dotnet/roslyn/issues/47197
        Private Shared s_runtimeSignatureComparer As IEqualityComparer(Of TSymbol)
#Enable Warning IDE0044 ' Add readonly modifier
 
        ' Initialize the various kinds of comparers.
        Shared Sub New()
            OverrideHidingHelper(Of MethodSymbol).s_runtimeSignatureComparer = MethodSignatureComparer.RuntimeMethodSignatureComparer
            OverrideHidingHelper(Of PropertySymbol).s_runtimeSignatureComparer = PropertySignatureComparer.RuntimePropertySignatureComparer
            OverrideHidingHelper(Of EventSymbol).s_runtimeSignatureComparer = EventSignatureComparer.RuntimeEventSignatureComparer
        End Sub
 
        ''' <summary>
        ''' Walk up the type hierarchy from ContainingType and list members that this
        ''' method overrides (accessible methods/properties with the same signature, if this
        ''' method is declared "override").
        ''' 
        ''' Methods in the overridden list may not be virtual or may have different
        ''' accessibilities, types, accessors, etc.  They are really candidates to be
        ''' overridden.
        ''' 
        ''' All found accessible candidates of overridden members are collected in two 
        ''' builders, those with 'exactly' matching signatures and those with 'generally'
        ''' or 'inexactly' matching signatures. 'Exact' signature match is a 'general' 
        ''' signature match which also does not have mismatches in total number of parameters
        ''' and/or types of optional parameters. See also comments on correspondent 
        ''' OverriddenMembersResult(Of TSymbol) properties.
        ''' 
        ''' 'Inexactly' matching candidates are only collected for reporting Dev10/Dev11
        ''' errors like BC30697 and others. We collect 'inexact' matching candidates until 
        ''' we find any 'exact' match.
        ''' 
        ''' Also remembers inaccessible members that are found, but these do not prevent
        ''' continuing to search for accessible members.
        ''' 
        ''' </summary>
        ''' <remarks>
        ''' In the presence of non-VB types, the meaning of "same signature" is rather
        ''' complicated.  If this method isn't from source, then it refers to the runtime's
        ''' notion of signature (i.e. including return type, custom modifiers, etc).
        ''' If this method is from source, use the VB version of signature. Note that 
        ''' Dev10 C# has a rule that prefers members with less custom modifiers. Dev 10 VB has no
        ''' such rule, so I'm not adding such a rule here.
        ''' </remarks>
        Friend Shared Function MakeOverriddenMembers(overridingSym As TSymbol) As OverriddenMembersResult(Of TSymbol)
            If Not overridingSym.IsOverrides OrElse Not CanOverrideOrHide(overridingSym) Then
                Return OverriddenMembersResult(Of TSymbol).Empty
            End If
 
            ' We should not be here for constructed methods, since overriding/hiding doesn't really make sense for them.
            Debug.Assert(Not (TypeOf overridingSym Is MethodSymbol AndAlso DirectCast(DirectCast(overridingSym, Symbol), MethodSymbol).ConstructedFrom <> overridingSym))
 
            ' We should not be here for property accessors (but ok for event accessors).
            ' TODO: When we support virtual events, that might change.
            Debug.Assert(Not (TypeOf overridingSym Is MethodSymbol AndAlso
                              (DirectCast(DirectCast(overridingSym, Symbol), MethodSymbol).MethodKind = MethodKind.PropertyGet OrElse
                                DirectCast(DirectCast(overridingSym, Symbol), MethodSymbol).MethodKind = MethodKind.PropertySet)))
 
            ' NOTE: If our goal is to make source references and metadata references indistinguishable, then we should really
            ' distinguish between the "current" compilation and other compilations, rather than between source and metadata.
            ' However, doing so would require adding a new parameter to the public API (i.e. which compilation to consider
            ' "current") and that extra complexity does not seem to provide significant benefit.  Our fallback goal is:
            ' if a source assembly builds successfully, then compilations referencing that assembly should build against
            ' both source and metadata or fail to build against both source and metadata.  Our expectation is that an exact
            ' match (which is required for successful compilation) should roundtrip through metadata, so this requirement
            ' should be met.
            Dim overridingIsFromSomeCompilation As Boolean = overridingSym.Dangerous_IsFromSomeCompilationIncludingRetargeting
 
            Dim containingType As NamedTypeSymbol = overridingSym.ContainingType
            Dim overriddenBuilder As ArrayBuilder(Of TSymbol) = ArrayBuilder(Of TSymbol).GetInstance()
            Dim inexactOverriddenMembers As ArrayBuilder(Of TSymbol) = ArrayBuilder(Of TSymbol).GetInstance()
            Dim inaccessibleBuilder As ArrayBuilder(Of TSymbol) = ArrayBuilder(Of TSymbol).GetInstance()
 
            Debug.Assert(Not containingType.IsInterface, "An interface member can't be marked overrides")
 
            Dim currType As NamedTypeSymbol = containingType.BaseTypeNoUseSiteDiagnostics
 
            While currType IsNot Nothing
                If FindOverriddenMembersInType(overridingSym, overridingIsFromSomeCompilation, containingType, currType, overriddenBuilder, inexactOverriddenMembers, inaccessibleBuilder) Then
                    Exit While ' Once we hit an overriding or hiding member, we're done.
                End If
 
                currType = currType.BaseTypeNoUseSiteDiagnostics
            End While
 
            Return OverriddenMembersResult(Of TSymbol).Create(overriddenBuilder.ToImmutableAndFree(),
                                                              inexactOverriddenMembers.ToImmutableAndFree(),
                                                              inaccessibleBuilder.ToImmutableAndFree())
        End Function
 
        ''' <summary>
        ''' Look for overridden members in a specific type. Return true if we find an overridden member candidate 
        ''' with 'exact' signature match, or we hit a member that hides. See comments on MakeOverriddenMembers(...)
        ''' for description of 'exact' and 'inexact' signature matches.
        ''' 
        ''' Also remember any inaccessible members that we see.
        ''' </summary>
        ''' <param name="overridingSym">Syntax that overriding or hiding.</param>
        ''' <param name="overridingIsFromSomeCompilation">True if "overridingSym" is from source (this.IsFromSomeCompilation).</param>
        ''' <param name="overridingContainingType">The type that contains this method (this.ContainingType).</param>
        ''' <param name="currType">The type to search.</param>
        ''' <param name="overriddenBuilder">Builder to place exactly-matched overridden member candidates in. </param>
        ''' <param name="inexactOverriddenMembers">Builder to place inexactly-matched overridden member candidates in. </param>
        ''' <param name="inaccessibleBuilder">Builder to place exactly-matched inaccessible overridden member candidates in. </param>
        Private Shared Function FindOverriddenMembersInType(overridingSym As TSymbol,
                                                            overridingIsFromSomeCompilation As Boolean,
                                                            overridingContainingType As NamedTypeSymbol,
                                                            currType As NamedTypeSymbol,
                                                            overriddenBuilder As ArrayBuilder(Of TSymbol),
                                                            inexactOverriddenMembers As ArrayBuilder(Of TSymbol),
                                                            inaccessibleBuilder As ArrayBuilder(Of TSymbol)) As Boolean
            ' Note that overriddenBuilder may contain some non-exact 
            ' matched symbols found in previous iterations
 
            ' We should not be here for property accessors (but ok for event accessors).
            ' TODO: When we support virtual events, that might change.
            Debug.Assert(Not (TypeOf overridingSym Is MethodSymbol AndAlso
                              (DirectCast(DirectCast(overridingSym, Symbol), MethodSymbol).MethodKind = MethodKind.PropertyGet OrElse
                                DirectCast(DirectCast(overridingSym, Symbol), MethodSymbol).MethodKind = MethodKind.PropertySet)))
 
            Dim stopLookup As Boolean = False
            Dim haveExactMatch As Boolean = False
            Dim overriddenInThisType As ArrayBuilder(Of TSymbol) = ArrayBuilder(Of TSymbol).GetInstance()
 
            For Each sym In currType.GetMembers(overridingSym.Name)
                ProcessMemberWithMatchingName(sym, overridingSym, overridingIsFromSomeCompilation, overridingContainingType, inexactOverriddenMembers,
                                              inaccessibleBuilder, overriddenInThisType, stopLookup, haveExactMatch)
            Next
 
            If overridingSym.Kind = SymbolKind.Property Then
                Dim prop = DirectCast(DirectCast(overridingSym, Object), PropertySymbol)
 
                If prop.IsImplicitlyDeclared AndAlso prop.IsWithEvents Then
                    For Each sym In currType.GetSynthesizedWithEventsOverrides()
                        If sym.Name.Equals(prop.Name) Then
                            ProcessMemberWithMatchingName(sym, overridingSym, overridingIsFromSomeCompilation, overridingContainingType, inexactOverriddenMembers,
                                              inaccessibleBuilder, overriddenInThisType, stopLookup, haveExactMatch)
                        End If
                    Next
                End If
            End If
 
            If overriddenInThisType.Count > 1 Then
                RemoveMembersWithConflictingAccessibility(overriddenInThisType)
            End If
 
            If overriddenInThisType.Count > 0 Then
                If haveExactMatch Then
                    Debug.Assert(stopLookup)
                    overriddenBuilder.Clear()
                End If
 
                If overriddenBuilder.Count = 0 Then
                    overriddenBuilder.AddRange(overriddenInThisType)
                End If
            End If
 
            overriddenInThisType.Free()
            Return stopLookup
        End Function
 
        Private Shared Sub ProcessMemberWithMatchingName(
            sym As Symbol,
            overridingSym As TSymbol,
            overridingIsFromSomeCompilation As Boolean,
            overridingContainingType As NamedTypeSymbol,
            inexactOverriddenMembers As ArrayBuilder(Of TSymbol),
            inaccessibleBuilder As ArrayBuilder(Of TSymbol),
            overriddenInThisType As ArrayBuilder(Of TSymbol),
            ByRef stopLookup As Boolean,
            ByRef haveExactMatch As Boolean
        )
            ' Use original definition for accessibility check, because substitutions can cause
            ' reductions in accessibility that aren't appropriate (see bug #12038 for example).
            Dim accessible = AccessCheck.IsSymbolAccessible(sym.OriginalDefinition, overridingContainingType.OriginalDefinition, Nothing, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
 
            If sym.Kind = overridingSym.Kind AndAlso
                CanOverrideOrHide(sym) Then
 
                Dim member As TSymbol = DirectCast(sym, TSymbol)
                Dim exactMatch As Boolean = True ' considered to be True for all runtime signature comparisons
                Dim exactMatchIgnoringCustomModifiers As Boolean = True ' considered to be True for all runtime signature comparisons
 
                If If(overridingIsFromSomeCompilation,
                    sym.IsWithEventsProperty = overridingSym.IsWithEventsProperty AndAlso
                        SignaturesMatch(overridingSym, member, exactMatch, exactMatchIgnoringCustomModifiers),
                    s_runtimeSignatureComparer.Equals(overridingSym, member)) Then
 
                    If accessible Then
                        If exactMatchIgnoringCustomModifiers Then
                            If exactMatch Then
                                If Not haveExactMatch Then
                                    haveExactMatch = True
                                    stopLookup = True
                                    overriddenInThisType.Clear()
                                End If
 
                                overriddenInThisType.Add(member)
                            ElseIf Not haveExactMatch Then
                                overriddenInThisType.Add(member)
                            End If
                        Else
                            ' Add only if not hidden by signature
                            AddMemberToABuilder(member, inexactOverriddenMembers)
                        End If
                    Else
                        If exactMatchIgnoringCustomModifiers Then
                            ' only exact matched methods are to be added 
                            inaccessibleBuilder.Add(member)
                        End If
                    End If
                ElseIf Not member.IsOverloads() AndAlso accessible Then
                    ' hiding symbol by name
                    stopLookup = True
                End If
            ElseIf accessible Then
                ' Any accessible symbol of different kind stops further lookup
                stopLookup = True
            End If
        End Sub
 
        Private Shared Sub AddMemberToABuilder(member As TSymbol,
                                               builder As ArrayBuilder(Of TSymbol))
 
            ' We should only add a member to a builder if it does not match any 
            ' symbols from previously processed (derived) classes 
 
            ' This is supposed to help avoid adding multiple symbols one of 
            ' which overrides another one, in the following case
            '    C1
            '       Overridable Sub S(x As Integer, Optional y As Integer = 1)
            '
            '    C2: C1
            '       Overridable Sub S(x As Integer)
            '
            '    C3: C2
            '       Overrides Sub S(x As Integer)
            '
            '    C4: C3
            '       Overrides Sub S(x As Integer, Optional y As Integer = 1)
 
            ' In the case above we should not add 'S(x As Integer)' twice
 
            ' We don't use 'OverriddenMethod' property on MethodSymbol because
            ' right now it does not cache the result, so we want to avoid 
            ' unnecessary nested calls to 'MakeOverriddenMembers'
 
            Dim memberContainingType As NamedTypeSymbol = member.ContainingType
            For i = 0 To builder.Count - 1
                Dim exactMatchIgnoringCustomModifiers As Boolean = False
                If Not TypeSymbol.Equals(builder(i).ContainingType, memberContainingType, TypeCompareKind.ConsiderEverything) AndAlso
                        SignaturesMatch(builder(i), member, Nothing, exactMatchIgnoringCustomModifiers) AndAlso exactMatchIgnoringCustomModifiers Then
                    ' Do NOT add
                    Exit Sub
                End If
            Next
            builder.Add(member)
        End Sub
 
        ' Check a member that is marked Override against it's base and report any necessary diagnostics. The already computed
        ' overridden members are passed in.
        Friend Shared Sub CheckOverrideMember(member As TSymbol,
                                              overriddenMembersResult As OverriddenMembersResult(Of TSymbol),
                                              diagnostics As BindingDiagnosticBag)
            Debug.Assert(overriddenMembersResult IsNot Nothing)
 
            Dim memberIsShadows As Boolean = member.ShadowsExplicitly
            Dim memberIsOverloads As Boolean = member.IsOverloads()
            Dim overriddenMembers As ImmutableArray(Of TSymbol) = overriddenMembersResult.OverriddenMembers
 
            ' If there are no overridden members (those with 'exactly' matching signature)
            ' analyze overridden member candidates with 'generally' matching signature
            If overriddenMembers.IsEmpty Then
                overriddenMembers = overriddenMembersResult.InexactOverriddenMembers
            End If
 
            If overriddenMembers.Length = 0 Then
                ' Did not have member to override. But there might have been an inaccessible one.
                If overriddenMembersResult.InaccessibleMembers.Length > 0 Then
                    ReportBadOverriding(ERRID.ERR_CannotOverrideInAccessibleMember, member, overriddenMembersResult.InaccessibleMembers(0), diagnostics)
                Else
                    diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_OverrideNotNeeded3, member.GetKindText(), member.Name),
                                                   member.Locations(0)))
                End If
            ElseIf overriddenMembers.Length > 1 Then
                ' Multiple members we could be overriding. Create a single error message that lists them all.
                Dim diagnosticInfos = ArrayBuilder(Of DiagnosticInfo).GetInstance(overriddenMembers.Length)
 
                For Each overriddenMemb In overriddenMembers
                    diagnosticInfos.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverriddenCandidate1, overriddenMemb.OriginalDefinition))
                Next
 
                diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_AmbiguousOverrides3,
                                                           overriddenMembers(0),
                                                           CustomSymbolDisplayFormatter.ShortErrorName(overriddenMembers(0).ContainingType),
                                                           New CompoundDiagnosticInfo(diagnosticInfos.ToArrayAndFree())),
                                               member.Locations(0)))
            Else
                ' overriding exactly one member.
                Dim overriddenMember As TSymbol = overriddenMembers(0)
                Dim comparisonResults As SymbolComparisonResults = DetailedSignatureCompare(member, overriddenMember, SymbolComparisonResults.AllMismatches)
                Dim errorId As ERRID
 
                If overriddenMember.IsNotOverridable Then
                    ReportBadOverriding(ERRID.ERR_CantOverrideNotOverridable2, member, overriddenMember, diagnostics)
                ElseIf Not (overriddenMember.IsOverridable Or overriddenMember.IsMustOverride Or overriddenMember.IsOverrides) Then
                    ReportBadOverriding(ERRID.ERR_CantOverride4, member, overriddenMember, diagnostics)
                ElseIf (comparisonResults And SymbolComparisonResults.ParameterByrefMismatch) <> 0 Then
                    ReportBadOverriding(ERRID.ERR_OverrideWithByref2, member, overriddenMember, diagnostics)
                ElseIf (comparisonResults And SymbolComparisonResults.OptionalParameterMismatch) <> 0 Then
                    ReportBadOverriding(ERRID.ERR_OverrideWithOptional2, member, overriddenMember, diagnostics)
                ElseIf (comparisonResults And SymbolComparisonResults.ReturnTypeMismatch) <> 0 Then
                    ReportBadOverriding(ERRID.ERR_InvalidOverrideDueToReturn2, member, overriddenMember, diagnostics)
                ElseIf (comparisonResults And SymbolComparisonResults.PropertyAccessorMismatch) <> 0 Then
                    ReportBadOverriding(ERRID.ERR_OverridingPropertyKind2, member, overriddenMember, diagnostics)
                ElseIf (comparisonResults And SymbolComparisonResults.PropertyInitOnlyMismatch) <> 0 Then
                    ReportBadOverriding(ERRID.ERR_OverridingInitOnlyProperty, member, overriddenMember, diagnostics)
                ElseIf (comparisonResults And SymbolComparisonResults.ParamArrayMismatch) <> 0 Then
                    ReportBadOverriding(ERRID.ERR_OverrideWithArrayVsParamArray2, member, overriddenMember, diagnostics)
                ElseIf (comparisonResults And SymbolComparisonResults.OptionalParameterTypeMismatch) <> 0 Then
                    ReportBadOverriding(ERRID.ERR_OverrideWithOptionalTypes2, member, overriddenMember, diagnostics)
                ElseIf (comparisonResults And SymbolComparisonResults.OptionalParameterValueMismatch) <> 0 Then
                    ReportBadOverriding(ERRID.ERR_OverrideWithDefault2, member, overriddenMember, diagnostics)
                ElseIf (comparisonResults And SymbolComparisonResults.ConstraintMismatch) <> 0 Then
                    ReportBadOverriding(ERRID.ERR_OverrideWithConstraintMismatch2, member, overriddenMember, diagnostics)
                ElseIf Not ConsistentAccessibility(member, overriddenMember, errorId) Then
                    ReportBadOverriding(errorId, member, overriddenMember, diagnostics)
                ElseIf member.ContainsTupleNames() AndAlso (comparisonResults And SymbolComparisonResults.TupleNamesMismatch) <> 0 Then
                    ' it is ok to override with no tuple names, for compatibility with VB 14, but otherwise names should match
                    ReportBadOverriding(ERRID.WRN_InvalidOverrideDueToTupleNames2, member, overriddenMember, diagnostics)
                Else
                    For Each inaccessibleMember In overriddenMembersResult.InaccessibleMembers
                        If inaccessibleMember.DeclaredAccessibility = Accessibility.Friend AndAlso
                            inaccessibleMember.OverriddenMember = overriddenMember Then
                            ' We have an inaccessible friend member that overrides the member we're trying to override.
                            ' We can't do that, so issue an error.
                            diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_InAccessibleOverridingMethod5,
                                                                                  member, member.ContainingType, overriddenMember, overriddenMember.ContainingType, inaccessibleMember.ContainingType),
                                                            member.Locations(0)))
 
                        End If
 
                    Next
 
                    Dim useSiteInfo = overriddenMember.GetUseSiteInfo()
                    If Not diagnostics.Add(useSiteInfo, member.Locations(0)) AndAlso
                       member.Kind = SymbolKind.Property Then
 
                        ' No overriding errors found in member. If its a property, its accessors might have issues.
                        Dim overridingProperty As PropertySymbol = DirectCast(DirectCast(member, Symbol), PropertySymbol)
                        Dim overriddenProperty As PropertySymbol = DirectCast(DirectCast(overriddenMember, Symbol), PropertySymbol)
 
                        CheckOverridePropertyAccessor(overridingProperty.GetMethod, overriddenProperty.GetMethod, diagnostics)
                        CheckOverridePropertyAccessor(overridingProperty.SetMethod, overriddenProperty.SetMethod, diagnostics)
                    End If
                End If
            End If
        End Sub
 
        ' Imported types can have multiple members with the same signature (case insensitive) and different accessibilities. VB prefers
        ' members that are "more accessible". This is a very rare code path if members has > 1 element so we don't worry about performance.
        Private Shared Sub RemoveMembersWithConflictingAccessibility(members As ArrayBuilder(Of TSymbol))
            If members.Count < 2 Then
                Return
            End If
 
            Const significantDifferences As SymbolComparisonResults = SymbolComparisonResults.AllMismatches And
                                                                      Not SymbolComparisonResults.MismatchesForConflictingMethods
 
            Dim nonConflicting As ArrayBuilder(Of TSymbol) = ArrayBuilder(Of TSymbol).GetInstance()
 
            For Each sym In members
                Dim isWorseThanAnother As Boolean = False
                For Each otherSym In members
                    If sym IsNot otherSym Then
                        Dim originalSym = sym.OriginalDefinition
                        Dim originalOther = otherSym.OriginalDefinition
 
                        ' Two original definitions with identical signatures in same containing types are compared by accessibility, and
                        ' more accessible wins.
                        If TypeSymbol.Equals(originalSym.ContainingType, originalOther.ContainingType, TypeCompareKind.ConsiderEverything) AndAlso
                           DetailedSignatureCompare(originalSym, originalOther, significantDifferences) = 0 AndAlso
                           LookupResult.CompareAccessibilityOfSymbolsConflictingInSameContainer(originalSym, originalOther) < 0 Then
                            ' sym is worse than otherSym
                            isWorseThanAnother = True
                            Exit For
                        End If
                    End If
                Next
 
                If Not isWorseThanAnother Then
                    nonConflicting.Add(sym)
                End If
            Next
 
            If nonConflicting.Count <> members.Count Then
                members.Clear()
                members.AddRange(nonConflicting)
            End If
 
            nonConflicting.Free()
        End Sub
 
        ' Check an accessor with respect to its overridden accessor and report any diagnostics
        Friend Shared Sub CheckOverridePropertyAccessor(overridingAccessor As MethodSymbol,
                                                        overriddenAccessor As MethodSymbol,
                                                        diagnostics As BindingDiagnosticBag)
            ' CONSIDER: it is possible for an accessor to have a use site error even when the property
            ' does not but, in general, we have not been handling cases where property and accessor
            ' signatures are mismatched (e.g. different modopts).
            If overridingAccessor IsNot Nothing AndAlso overriddenAccessor IsNot Nothing Then
                ' Use original definition for accessibility check, because substitutions can cause
                ' reductions in accessibility that aren't appropriate (see bug #12038 for example).
                If Not AccessCheck.IsSymbolAccessible(overriddenAccessor.OriginalDefinition, overridingAccessor.ContainingType, Nothing, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded) Then
                    ReportBadOverriding(ERRID.ERR_CannotOverrideInAccessibleMember, overridingAccessor, overriddenAccessor, diagnostics)
                Else
                    Dim errorId As ERRID
 
                    If Not ConsistentAccessibility(overridingAccessor, overriddenAccessor, errorId) Then
                        ReportBadOverriding(errorId, overridingAccessor, overriddenAccessor, diagnostics)
                    End If
                End If
 
                diagnostics.Add(overriddenAccessor.GetUseSiteInfo(), overridingAccessor.Locations(0))
            End If
        End Sub
 
        ' Report an error with overriding
        Private Shared Sub ReportBadOverriding(id As ERRID,
                                               overridingMember As Symbol,
                                               overriddenMember As Symbol,
                                               diagnostics As BindingDiagnosticBag)
            diagnostics.Add(New VBDiagnostic(New BadSymbolDiagnostic(overriddenMember, id, overridingMember, overriddenMember),
                                            overridingMember.Locations(0)))
        End Sub
 
        ' Are the declared accessibility of the two symbols consistent? If not, return the error code to use.
        Private Shared Function ConsistentAccessibility(overriding As Symbol, overridden As Symbol, ByRef errorId As ERRID) As Boolean
            If overridden.DeclaredAccessibility = Accessibility.ProtectedOrFriend And Not overriding.ContainingAssembly = overridden.ContainingAssembly Then
                errorId = ERRID.ERR_FriendAssemblyBadAccessOverride2
                Return overriding.DeclaredAccessibility = Accessibility.Protected
            Else
                errorId = ERRID.ERR_BadOverrideAccess2
                Return overridden.DeclaredAccessibility = overriding.DeclaredAccessibility
            End If
        End Function
 
    End Class
End Namespace