/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Symbols/Source/OverrideHidingHelper.vb
1 019 строк
61 KB
Cyrus Najmabadi
Update code to use GetFirstLocation() instead of Locations[0] or Locations.First() (#80709)
21 окт 2025, 00:00
Не верифицирован
21 окт 2025, 00:00
3a723bb
Код
Авторство
О чём код?
' 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.GetFirstLocation())) 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.GetFirstLocation())) 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.GetFirstLocation())) 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.GetFirstLocation())) 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.GetFirstLocation())) 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.GetFirstLocation())) 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.GetFirstLocation())) 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.GetFirstLocation())) 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.GetFirstLocation())) Else ' all other hiding scenarios. Debug.Assert(hidingMember.GetFirstLocation().IsInSource) diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.WRN_OverrideType5, hidingMember.GetKindText(), hidingMember.Name, hiddenMember.GetKindText(), hiddenMember.ContainingType.GetKindText(), CustomSymbolDisplayFormatter.ShortErrorName(hiddenMember.ContainingType)), hidingMember.GetFirstLocation())) 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.GetFirstLocation().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.GetFirstLocation())) Else ' Basic hiding case diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_CantShadowAMustOverride1, hidingMember), hidingMember.GetFirstLocation())) 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.GetFirstLocation())) 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.GetFirstLocation())) 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.GetFirstLocation())) End If Next Dim useSiteInfo = overriddenMember.GetUseSiteInfo() If Not diagnostics.Add(useSiteInfo, member.GetFirstLocation()) 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.GetFirstLocation()) 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.GetFirstLocation())) 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