|
' 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.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Emit
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Roslyn.Utilities
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
''' <summary>
''' Represents a named type symbol whose members are declared in source.
''' </summary>
Friend MustInherit Class SourceMemberContainerTypeSymbol
Inherits InstanceTypeSymbol
''' <summary>
''' Holds information about a SourceType in a compact form.
''' </summary>
<Flags>
Friend Enum SourceTypeFlags As UShort
[Private] = CUShort(Accessibility.Private)
[Protected] = CUShort(Accessibility.Protected)
[Friend] = CUShort(Accessibility.Friend)
ProtectedFriend = CUShort(Accessibility.ProtectedOrFriend)
PrivateProtected = CUShort(Accessibility.ProtectedAndFriend)
[Public] = CUShort(Accessibility.Public)
AccessibilityMask = &H7
[Class] = CUShort(TypeKind.Class) << TypeKindShift
[Structure] = CUShort(TypeKind.Structure) << TypeKindShift
[Interface] = CUShort(TypeKind.Interface) << TypeKindShift
[Enum] = CUShort(TypeKind.Enum) << TypeKindShift
[Delegate] = CUShort(TypeKind.Delegate) << TypeKindShift
[Module] = CUShort(TypeKind.Module) << TypeKindShift
Submission = CUShort(TypeKind.Submission) << TypeKindShift
TypeKindMask = &HF0
TypeKindShift = 4
[MustInherit] = 1 << 8
[NotInheritable] = 1 << 9
[Shadows] = 1 << 10
[Partial] = 1 << 11
End Enum
' Flags about the type
Private ReadOnly _flags As SourceTypeFlags
' Misc flags defining the state of this symbol (StateFlags)
Protected m_lazyState As Integer
<Flags>
Protected Enum StateFlags As Integer
FlattenedMembersIsSortedMask = &H1 ' Set if "m_lazyMembersFlattened" is sorted.
ReportedVarianceDiagnostics = &H2 ' Set if variance diagnostics have been reported.
ReportedBaseClassConstraintsDiagnostics = &H4 ' Set if base class constraints diagnostics have been reported.
ReportedInterfacesConstraintsDiagnostics = &H8 ' Set if constraints diagnostics for base/implemented interfaces have been reported.
End Enum
' Containing symbol
Private ReadOnly _containingSymbol As NamespaceOrTypeSymbol
' Containing source module
Protected ReadOnly m_containingModule As SourceModuleSymbol
' The declaration for this type.
Private ReadOnly _declaration As MergedTypeDeclaration
' The name of the type, might be different than m_decl.Name depending on lexical sort order.
Private ReadOnly _name As String
' The name of the default property if any.
' GetMembersAndInitializers must be called before accessing field.
Private _defaultPropertyName As String
' The different kinds of members of this type
Private _lazyMembersAndInitializers As MembersAndInitializers
' Maps names to nested type symbols.
Private Shared ReadOnly s_emptyTypeMembers As New Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol))(IdentifierComparison.Comparer)
Private _lazyTypeMembers As Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol))
' An array of members in declaration order.
Private _lazyMembersFlattened As ImmutableArray(Of Symbol)
Private _lazyEmitExtensionAttribute As ThreeState = ThreeState.Unknown
Private _lazyContainsExtensionMethods As ThreeState = ThreeState.Unknown
Private _lazyAnyMemberHasAttributes As ThreeState = ThreeState.Unknown
Private _lazyStructureCycle As Integer = ThreeState.Unknown ' Interlocked
Private _lazyLexicalSortKey As LexicalSortKey = LexicalSortKey.NotInitialized
#Region "Construction"
' Create the type symbol and associated type parameter symbols. Most information
' is deferred until later.
Protected Sub New(declaration As MergedTypeDeclaration,
containingSymbol As NamespaceOrTypeSymbol,
containingModule As SourceModuleSymbol)
m_containingModule = containingModule
_containingSymbol = containingSymbol
_declaration = declaration
_name = GetBestName(declaration, containingModule.ContainingSourceAssembly.DeclaringCompilation)
_flags = ComputeTypeFlags(declaration, containingSymbol.IsNamespace)
End Sub
' Figure out the "right" name spelling, it should come from lexically first declaration.
Private Shared Function GetBestName(declaration As MergedTypeDeclaration, compilation As VisualBasicCompilation) As String
Dim declarations As ImmutableArray(Of SingleTypeDeclaration) = declaration.Declarations
Dim best As SingleTypeDeclaration = declarations(0)
For i As Integer = 1 To declarations.Length - 1
Dim bestLocation As Location = best.Location
If compilation.FirstSourceLocation(bestLocation, declarations(i).Location) IsNot bestLocation Then
best = declarations(i)
End If
Next
Return best.Name
End Function
''' <summary>
''' Compute the type flags from the declaration.
''' This function DOES NOT diagnose errors in the modifiers. Given the set of modifiers,
''' it produces the flags, even in the case of potentially conflicting modifiers. We have to
''' return some answer even in the case of errors.
''' </summary>
Private Function ComputeTypeFlags(declaration As MergedTypeDeclaration, isTopLevel As Boolean) As SourceTypeFlags
Dim mergedModifiers As DeclarationModifiers = DeclarationModifiers.None
For i = 0 To declaration.Declarations.Length - 1
mergedModifiers = mergedModifiers Or declaration.Declarations(i).Modifiers
Next
Dim modifiers = mergedModifiers
Dim flags As SourceTypeFlags = 0
' compute type kind, inheritability
Select Case declaration.Kind
Case DeclarationKind.Class
flags = SourceTypeFlags.Class
If (modifiers And DeclarationModifiers.NotInheritable) <> 0 Then
flags = flags Or SourceTypeFlags.NotInheritable
ElseIf (modifiers And DeclarationModifiers.MustInherit) <> 0 Then
flags = flags Or SourceTypeFlags.MustInherit
End If
Case DeclarationKind.Script, DeclarationKind.ImplicitClass
flags = SourceTypeFlags.Class Or SourceTypeFlags.NotInheritable
Case DeclarationKind.Submission
flags = SourceTypeFlags.Submission Or SourceTypeFlags.NotInheritable
Case DeclarationKind.Structure
flags = SourceTypeFlags.Structure Or SourceTypeFlags.NotInheritable
Case DeclarationKind.Interface
flags = SourceTypeFlags.Interface Or SourceTypeFlags.MustInherit
Case DeclarationKind.Enum
flags = SourceTypeFlags.Enum Or SourceTypeFlags.NotInheritable
Case DeclarationKind.Delegate,
DeclarationKind.EventSyntheticDelegate
flags = SourceTypeFlags.Delegate Or SourceTypeFlags.NotInheritable
Case DeclarationKind.Module
flags = SourceTypeFlags.Module Or SourceTypeFlags.NotInheritable
Case Else
Throw ExceptionUtilities.UnexpectedValue(declaration.Kind)
End Select
' compute accessibility
If isTopLevel Then
' top-level types (types in namespaces) can only be Friend or Public, and the default is friend
If (modifiers And DeclarationModifiers.Friend) <> 0 Then
flags = flags Or SourceTypeFlags.Friend
ElseIf (modifiers And DeclarationModifiers.Public) <> 0 Then
flags = flags Or SourceTypeFlags.Public
Else
flags = flags Or SourceTypeFlags.Friend
End If
Else
' Nested types (including types in modules) can be any accessibility, and the default is public
If (modifiers And (DeclarationModifiers.Private Or DeclarationModifiers.Protected)) =
(DeclarationModifiers.Private Or DeclarationModifiers.Protected) Then
flags = flags Or SourceTypeFlags.PrivateProtected
ElseIf (modifiers And DeclarationModifiers.Private) <> 0 Then
flags = flags Or SourceTypeFlags.Private
ElseIf (modifiers And (DeclarationModifiers.Protected Or DeclarationModifiers.Friend)) =
(DeclarationModifiers.Protected Or DeclarationModifiers.Friend) Then
flags = flags Or SourceTypeFlags.ProtectedFriend
ElseIf (modifiers And DeclarationModifiers.Protected) <> 0 Then
flags = flags Or SourceTypeFlags.Protected
ElseIf (modifiers And DeclarationModifiers.Friend) <> 0 Then
flags = flags Or SourceTypeFlags.Friend
Else
flags = flags Or SourceTypeFlags.Public
End If
End If
' Compute partial
If (modifiers And DeclarationModifiers.Partial) <> 0 Then
flags = flags Or SourceTypeFlags.Partial
End If
' Compute Shadows
If (modifiers And DeclarationModifiers.Shadows) <> 0 Then
flags = flags Or SourceTypeFlags.Shadows
End If
Return flags
End Function
Public Shared Function Create(declaration As MergedTypeDeclaration,
containingSymbol As NamespaceOrTypeSymbol,
containingModule As SourceModuleSymbol) As SourceMemberContainerTypeSymbol
Dim kind = declaration.SyntaxReferences.First.SyntaxTree.GetEmbeddedKind()
If kind <> EmbeddedSymbolKind.None Then
Return New EmbeddedSymbolManager.EmbeddedNamedTypeSymbol(declaration, containingSymbol, containingModule, kind)
End If
Select Case declaration.Kind
Case DeclarationKind.ImplicitClass,
DeclarationKind.Script,
DeclarationKind.Submission
Return New ImplicitNamedTypeSymbol(declaration, containingSymbol, containingModule)
Case Else
Dim type = New SourceNamedTypeSymbol(declaration, containingSymbol, containingModule)
' In case Vb Core Runtime is being embedded, we should mark attribute
' 'Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute'
' as being referenced if the named type just created is a module
If type.TypeKind = TypeKind.Module Then
type.DeclaringCompilation.EmbeddedSymbolManager.RegisterModuleDeclaration()
End If
Return type
End Select
End Function
' Create a nested type with the given declaration.
Private Function CreateNestedType(declaration As MergedTypeDeclaration) As NamedTypeSymbol
#If DEBUG Then
' Ensure that the type declaration is either from user code or embedded
' code, but not merged across embedded code/user code boundary.
Dim embedded = EmbeddedSymbolKind.Unset
For Each ref In declaration.SyntaxReferences
Dim refKind = ref.SyntaxTree.GetEmbeddedKind()
If embedded <> EmbeddedSymbolKind.Unset Then
Debug.Assert(embedded = refKind)
Else
embedded = refKind
End If
Next
Debug.Assert(embedded <> EmbeddedSymbolKind.Unset)
#End If
If declaration.Kind = DeclarationKind.Delegate Then
Debug.Assert(Not declaration.SyntaxReferences.First.SyntaxTree.IsEmbeddedSyntaxTree)
Return New SourceNamedTypeSymbol(declaration, Me, m_containingModule)
ElseIf declaration.Kind = DeclarationKind.EventSyntheticDelegate Then
Debug.Assert(Not declaration.SyntaxReferences.First.SyntaxTree.IsEmbeddedSyntaxTree)
Return New SynthesizedEventDelegateSymbol(declaration.SyntaxReferences(0), Me)
Else
Return Create(declaration, Me, m_containingModule)
End If
End Function
#End Region
#Region "Completion"
''' <summary>
''' Force all declaration errors to be generated.
''' </summary>
Friend NotOverridable Overrides Sub GenerateDeclarationErrors(cancellationToken As CancellationToken)
GenerateAllDeclarationErrorsImpl(cancellationToken)
End Sub
Protected Overridable Sub GenerateAllDeclarationErrorsImpl(cancellationToken As CancellationToken)
cancellationToken.ThrowIfCancellationRequested()
Dim membersAndInitializers = GetMembersAndInitializers()
cancellationToken.ThrowIfCancellationRequested()
For Each member In Me.GetMembers()
' we already visited types
If member.Kind <> SymbolKind.NamedType Then
member.GenerateDeclarationErrors(cancellationToken)
End If
Next
cancellationToken.ThrowIfCancellationRequested()
Dim unused1 = BaseTypeNoUseSiteDiagnostics
cancellationToken.ThrowIfCancellationRequested()
Dim unused2 = InterfacesNoUseSiteDiagnostics
cancellationToken.ThrowIfCancellationRequested()
Dim unused3 = ExplicitInterfaceImplementationMap
cancellationToken.ThrowIfCancellationRequested()
Dim typeParams = TypeParameters
If Not typeParams.IsEmpty Then
TypeParameterSymbol.EnsureAllConstraintsAreResolved(typeParams)
End If
cancellationToken.ThrowIfCancellationRequested()
Dim unused4 = GetAttributes()
cancellationToken.ThrowIfCancellationRequested()
BindAllMemberAttributes(cancellationToken)
cancellationToken.ThrowIfCancellationRequested()
GenerateVarianceDiagnostics()
End Sub
Private Sub GenerateVarianceDiagnostics()
If (m_lazyState And StateFlags.ReportedVarianceDiagnostics) <> 0 Then
Return
End If
Dim diagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, withDependencies:=False)
Dim infosBuffer As ArrayBuilder(Of DiagnosticInfo) = Nothing
Select Case Me.TypeKind
Case TypeKind.Interface
GenerateVarianceDiagnosticsForInterface(diagnostics, infosBuffer)
Case TypeKind.Delegate
GenerateVarianceDiagnosticsForDelegate(diagnostics, infosBuffer)
Case TypeKind.Class, TypeKind.Enum, TypeKind.Structure
ReportNestingIntoVariantInterface(diagnostics)
Case TypeKind.Module, TypeKind.Submission
Case Else
Throw ExceptionUtilities.UnexpectedValue(Me.TypeKind)
End Select
m_containingModule.AtomicSetFlagAndStoreDiagnostics(m_lazyState,
StateFlags.ReportedVarianceDiagnostics,
0,
diagnostics)
diagnostics.Free()
If infosBuffer IsNot Nothing Then
' all diagnostics were reported to diagnostic bag:
Debug.Assert(infosBuffer.Count = 0)
infosBuffer.Free()
End If
End Sub
Private Sub ReportNestingIntoVariantInterface(diagnostics As BindingDiagnosticBag)
If Not _containingSymbol.IsType Then
Return
End If
' Check for illegal nesting into variant interface.
Dim container = DirectCast(_containingSymbol, NamedTypeSymbol)
Do
If Not container.IsInterfaceType() Then
Debug.Assert(Not container.IsDelegateType())
' The same validation will be performed for the container and
' there is no reason to duplicate the same errors, if any, on this type.
container = Nothing
Exit Do
End If
If container.TypeParameters.HaveVariance() Then
' We are inside of a variant interface
Exit Do
End If
' This interface isn't variant, but its containing interface might be.
container = container.ContainingType
Loop While container IsNot Nothing
If container IsNot Nothing Then
Debug.Assert(container.IsInterfaceType() AndAlso container.HasVariance())
diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_VarianceInterfaceNesting), Locations(0)))
End If
End Sub
Private Sub GenerateVarianceDiagnosticsForInterface(
diagnostics As BindingDiagnosticBag,
<[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
)
' Dev10 didn't do this shortcut, but I and Lucian believe that the checks below
' can be safely skipped for an invariant interface.
If Not Me.HasVariance() Then
Return
End If
' Variance spec $2:
' An interface I is valid if and only if
' * every method signature in I is valid and has no variant generic parameters (no variant generic
' parameters part is handled by SourceMethodSymbol.BindTypeParameterConstraints), and
' * every property in I is valid, and
' * every event in I is valid, and
' * every immediate base interface type of I is valid covariantly, and
' * the interface is either invariant or it lacks nested classes and structs, and
' * every nested type is valid.
'
' A property "Property Goo as T" is valid if and only if either
' * The property is read-only and T is valid covariantly, or
' * The property is write-only and T is valid invariantly, or
' * The property is readable and writable and T is invariant.
'
' An event "Event e as D" is valid if and only if
' * the delegate type D is valid contravariantly
'
' An event "Event e(Signature)" is valid if and only if
' * it is not contained (not even nested) in a variant interface,
' this is handled by SynthesizedEventDelegateSymbol.
'
' The test that nested types are valid isn't needed, since GenerateVarianceDiagnostics
' will anyways be called on them all. (and for any invalid nested type, we report the
' error on it, rather than on this container.)
'
' The check that an interface lacks nested classes and structs is done inside
' ReportNestingIntoVariantInterface. Why? Because we have to look for indirectly
' nested classes/structs, not just immediate ones. And it seemed nicer for classes/structs
' to look UP for variant containers, rather than for interfaces to look DOWN for class/struct contents.
For Each batch As ImmutableArray(Of Symbol) In GetMembersAndInitializers().Members.Values
For Each member As Symbol In batch
If Not member.IsImplicitlyDeclared Then
Select Case member.Kind
Case SymbolKind.Method
GenerateVarianceDiagnosticsForMethod(DirectCast(member, MethodSymbol), diagnostics, infosBuffer)
Debug.Assert(Not HaveDiagnostics(infosBuffer))
Case SymbolKind.Property
GenerateVarianceDiagnosticsForProperty(DirectCast(member, PropertySymbol), diagnostics, infosBuffer)
Debug.Assert(Not HaveDiagnostics(infosBuffer))
Case SymbolKind.Event
GenerateVarianceDiagnosticsForEvent(DirectCast(member, EventSymbol), diagnostics, infosBuffer)
Debug.Assert(Not HaveDiagnostics(infosBuffer))
End Select
End If
Next
Next
' 3. every immediate base interface is valid covariantly.
' Actually, the only type that's invalid covariantly and allowable as a base interface,
' is a generic instantiation X(T1,...) where we've instantiated it wrongly (e.g. given it "Out Ti"
' for a generic parameter that was declared as an "In"). Look what happens:
' Interface IZoo(Of In T) | Dim x as IZoo(Of Animal)
' Inherits IReadOnly(Of T) | Dim y as IZoo(Of Mammal) = x ' through contravariance of IZoo
' End Interface | Dim z as IReadOnly(Of Mammal) = y ' through inheritance from IZoo
' Now we might give "z" to someone who's expecting to read only Mammals, even though we know the zoo
' contains all kinds of animals.
For Each implemented As NamedTypeSymbol In Me.InterfacesNoUseSiteDiagnostics
If Not implemented.IsErrorType() Then
Debug.Assert(Not HaveDiagnostics(infosBuffer))
GenerateVarianceDiagnosticsForType(implemented, VarianceKind.Out, VarianceContext.Complex, infosBuffer)
If HaveDiagnostics(infosBuffer) Then
ReportDiagnostics(diagnostics, GetInheritsOrImplementsLocation(implemented, getInherits:=True), infosBuffer)
End If
End If
Next
End Sub
' Gets the implements location for a particular interface, which must be implemented but might be indirectly implemented.
' Also gets the direct interface it was inherited through
Private Function GetImplementsLocation(implementedInterface As NamedTypeSymbol, ByRef directInterface As NamedTypeSymbol) As Location
Debug.Assert(Me.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics(implementedInterface).Contains(implementedInterface))
' Find the directly implemented interface that "implementedIface" was inherited through.
directInterface = Nothing
For Each iface In Me.InterfacesNoUseSiteDiagnostics
If TypeSymbol.Equals(iface, implementedInterface, TypeCompareKind.ConsiderEverything) Then
directInterface = iface
Exit For
ElseIf directInterface Is Nothing AndAlso iface.ImplementsInterface(implementedInterface, comparer:=Nothing, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded) Then
directInterface = iface
End If
Next
Debug.Assert(directInterface IsNot Nothing)
Return GetInheritsOrImplementsLocation(directInterface, Me.IsInterfaceType())
End Function
Private Function GetImplementsLocation(implementedInterface As NamedTypeSymbol) As Location
Dim dummy As NamedTypeSymbol = Nothing
Return GetImplementsLocation(implementedInterface, dummy)
End Function
Protected MustOverride Function GetInheritsOrImplementsLocation(base As NamedTypeSymbol, getInherits As Boolean) As Location
Private Sub GenerateVarianceDiagnosticsForDelegate(
diagnostics As BindingDiagnosticBag,
<[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
)
' Dev10 didn't do this shortcut, but I and Lucian believe that the checks below
' can be safely skipped for an invariant interface.
If Not Me.HasVariance() Then
Return
End If
' Variance spec $2
' A delegate "Delegate Function/Sub Goo(Of T1, ... Tn)Signature" is valid if and only if
' * the signature is valid.
'
' That delegate becomes "Class Goo(Of T1, ... Tn) : Function Invoke(...) As ... : End Class
' So we just need to pick up the "Invoke" method and check that it's valid.
' NB. that delegates can have variance in their generic params, and hence so can e.g. "Class Goo(Of Out T1)"
' This is the only place in the CLI where a class can have variant generic params.
'
' Note: delegates that are synthesized from events are already dealt with in
' SynthesizedEventDelegateSymbol.GenerateAllDeclarationErrors, so we won't run into them here.
Dim invoke As MethodSymbol = Me.DelegateInvokeMethod
If invoke IsNot Nothing Then
GenerateVarianceDiagnosticsForMethod(invoke, diagnostics, infosBuffer)
End If
End Sub
Private Shared Sub ReportDiagnostics(
diagnostics As BindingDiagnosticBag,
location As Location,
infos As ArrayBuilder(Of DiagnosticInfo)
)
For Each info In infos
diagnostics.Add(info, location)
Next
infos.Clear()
End Sub
Private Shared Function HaveDiagnostics(diagnostics As ArrayBuilder(Of DiagnosticInfo)) As Boolean
Return diagnostics IsNot Nothing AndAlso diagnostics.Count > 0
End Function
''' <summary>
''' Following enum is used just to help give more specific error messages.
''' </summary>
Private Enum VarianceContext
' We'll give specific error messages in these simple contexts:
[ByVal]
[ByRef]
[Return]
[Constraint]
Nullable
ReadOnlyProperty
WriteOnlyProperty
[Property]
' Otherwise (e.g. nested inside a generic) we use the following as a catch-all:
Complex
End Enum
Private Sub GenerateVarianceDiagnosticsForType(
type As TypeSymbol,
requiredVariance As VarianceKind,
context As VarianceContext,
<[In], Out> ByRef diagnostics As ArrayBuilder(Of DiagnosticInfo)
)
GenerateVarianceDiagnosticsForTypeRecursively(type, requiredVariance, context, Nothing, 0, diagnostics)
End Sub
Private Shared Sub AppendVarianceDiagnosticInfo(
<[In], Out> ByRef diagnostics As ArrayBuilder(Of DiagnosticInfo),
info As DiagnosticInfo
)
If diagnostics Is Nothing Then
diagnostics = ArrayBuilder(Of DiagnosticInfo).GetInstance()
End If
diagnostics.Add(info)
End Sub
Private Structure VarianceDiagnosticsTargetTypeParameter
Public ReadOnly ConstructedType As NamedTypeSymbol
Private ReadOnly _typeParameterIndex As Integer
Public ReadOnly Property TypeParameter As TypeParameterSymbol
Get
Return ConstructedType.TypeParameters(_typeParameterIndex)
End Get
End Property
Public Sub New(constructedType As NamedTypeSymbol, typeParameterIndex As Integer)
Debug.Assert(typeParameterIndex >= 0 AndAlso typeParameterIndex < constructedType.Arity)
Me.ConstructedType = constructedType
_typeParameterIndex = typeParameterIndex
End Sub
End Structure
Private Sub GenerateVarianceDiagnosticsForTypeRecursively(
type As TypeSymbol,
requiredVariance As VarianceKind,
context As VarianceContext,
typeParameterInfo As VarianceDiagnosticsTargetTypeParameter,
constructionDepth As Integer,
<[In], Out> ByRef diagnostics As ArrayBuilder(Of DiagnosticInfo)
)
' Variance spec $2:
'
' A type T is valid invariantly if and only if:
' * it is valid covariantly, and
' * it is valid contravariantly.
'
' A type T is valid covariantly if and only if one of the following hold: either
' * T is a generic parameter which was not declared contravariant, or
' * T is an array type U() where U is valid covariantly, or
' * T is a construction X1(Of T11...)...Xn(Of Tn1...) of some generic struct/class/interface/delegate Xn
' declared as X1(Of X11...)...Xn(Of Xn1...) such that for each i and j,
' - if Xij was declared covariant then Tij is valid covariantly
' - if Xij was declared contravariant then Tij is valid contravariantly
' - if Xij was declared invariant then Tij is valid invariantly
' * or T is a non-generic struct/class/interface/delegate/enum.
'
' A type T is valid contravariantly if and only if one of the following hold: either
' * T is a generic parameter which was not declared covariant, or
' * T is an array type U() where U is valid contravariantly, or
' * T is a construction X1(Of T11...)...Xn(Of Tn1...) of some generic struct/class/interface/delegate Xn
' declared as X1(Of X11...)...Xn(Of Xn1...) such that for each i and j,
' - if Xij was declared covariant then Tij is valid contravariantly
' - if Xij was declared contravariant then Tij is valid covariantly
' - if Xij was declared invariant then Tij is valid invariantly
' * or T is a non-generic struct/class/interface/delegate/enum.
'
'
' In all cases, if a type fails a variance validity check, then it ultimately failed
' because somewhere there were one or more generic parameters "T" which were declared with
' the wrong kind of variance. In particular, they were either declared In when they'd have
' to be Out or InOut, or they were declared Out when they'd have to be In or InOut.
' We mark all these as errors.
'
' BUT... CLS restrictions say that in any generic type, all nested types first copy their
' containers's generic parameters. This restriction is embodied in the BCSYM structure.
' SOURCE: BCSYM: IL:
' Interface I(Of Out T1) Interface"I"/genericparams=T1 .interface I(Of Out T1)
' Interface J : End Interface Interface"J"/no genericparams .interface J(Of Out T1)
' Sub f(ByVal x as J) ... GenericTypeBinding(J,args=[], .proc f(x As J(Of T1))
' End Interface parentargs=I[T1])
' Observe that, by construction, any time we use a nested type like J in a contravariant position
' then it's bound to be invalid. If we simply applied the previous paragraph then we'd emit a
' confusing error to the user like "J is invalid because T1 is an Out parameter". So we want
' to do a better job of reporting errors. In particular,
' * If we are checking a GenericTypeBinding (e.g. x as J(Of T1)) for contravariant validity, look up
' to find the outermost ancestor binding (e.g. parentargs=I[T1]) which is of a variant interface.
' If this is also the outermost variant container of the current context, then it's an error.
Select Case type.Kind
Case SymbolKind.TypeParameter
' 1. if T is a generic parameter which was declared wrongly
Dim typeParam = DirectCast(type, TypeParameterSymbol)
If (typeParam.Variance = VarianceKind.Out AndAlso requiredVariance <> VarianceKind.Out) OrElse
(typeParam.Variance = VarianceKind.In AndAlso requiredVariance <> VarianceKind.In) Then
' The error is either because we have an "Out" param and Out is inappropriate here,
' or we used an "In" param and In is inappropriate here. This flag says which:
Dim inappropriateOut As Boolean = (typeParam.Variance = VarianceKind.Out)
' OKAY, so now we need to report an error. Simple enough, but we've tried to give helpful
' context-specific error messages to the user, and so the code has to work through a lot
' of special cases.
Select Case context
Case VarianceContext.ByVal
' "Type '|1' cannot be used as a ByVal parameter type because '|1' is an 'Out' type parameter."
Debug.Assert(inappropriateOut, "unexpected: a variance error in ByVal must be due to an inappropriate out")
AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceOutByValDisallowed1, type.Name))
Case VarianceContext.ByRef
' "Type '|1' cannot be used in this context because 'In' and 'Out' type parameters cannot be used for ByRef parameter types, and '|1' is an 'Out/In' type parameter."
AppendVarianceDiagnosticInfo(diagnostics,
ErrorFactory.ErrorInfo(If(inappropriateOut,
ERRID.ERR_VarianceOutByRefDisallowed1,
ERRID.ERR_VarianceInByRefDisallowed1),
type.Name))
Case VarianceContext.Return
' "Type '|1' cannot be used as a return type because '|1' is an 'In' type parameter."
Debug.Assert(Not inappropriateOut, "unexpected: a variance error in Return Type must be due to an inappropriate in")
AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceInReturnDisallowed1, type.Name))
Case VarianceContext.Constraint
' "Type '|1' cannot be used as a generic type constraint because '|1' is an 'Out' type parameter."
Debug.Assert(inappropriateOut, "unexpected: a variance error in Constraint must be due to an inappropriate out")
AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceOutConstraintDisallowed1, type.Name))
Case VarianceContext.Nullable
' "Type '|1' cannot be used in '|2' because 'In' and 'Out' type parameters cannot be made nullable, and '|1' is an 'In/Out' type parameter."
AppendVarianceDiagnosticInfo(diagnostics,
ErrorFactory.ErrorInfo(If(inappropriateOut,
ERRID.ERR_VarianceOutNullableDisallowed2,
ERRID.ERR_VarianceInNullableDisallowed2),
type.Name,
CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType)))
Case VarianceContext.ReadOnlyProperty
' "Type '|1' cannot be used as a ReadOnly property type because '|1' is an 'In' type parameter."
Debug.Assert(Not inappropriateOut, "unexpected: a variance error in ReadOnlyProperty must be due to an inappropriate in")
AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceInReadOnlyPropertyDisallowed1, type.Name))
Case VarianceContext.WriteOnlyProperty
' "Type '|1' cannot be used as a WriteOnly property type because '|1' is an 'Out' type parameter."
Debug.Assert(inappropriateOut, "unexpected: a variance error in WriteOnlyProperty must be due to an inappropriate out")
AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceOutWriteOnlyPropertyDisallowed1, type.Name))
Case VarianceContext.Property
' "Type '|1' cannot be used as a property type in this context because '|1' is an 'Out/In' type parameter and the property is not marked ReadOnly/WriteOnly.")
AppendVarianceDiagnosticInfo(diagnostics,
ErrorFactory.ErrorInfo(If(inappropriateOut,
ERRID.ERR_VarianceOutPropertyDisallowed1,
ERRID.ERR_VarianceInPropertyDisallowed1),
type.Name))
Case VarianceContext.Complex
' Otherwise, we're in "VarianceContextComplex" property. And so the error message needs
' to spell out precisely where in the context we are:
' "Type '|1' cannot be used in this context because '|1' is an 'Out|In' type parameter."
' "Type '|1' cannot be used for the '|2' in '|3' in this context because '|1' is an 'Out|In' type parameter."
' "Type '|1' cannot be used in '|2' in this context because '|1' is an 'Out|In' type parameter."
' "Type '|1' cannot be used in '|2' for the '|3' in '|4' in this context because '|1' is an 'Out' type parameter."
' We need the "in '|2' here" clause when ErrorBindingIsNested, to show which instantiation we're talking about.
' We need the "for the '|3' in '|4'" when ErrorBinding->GetGenericParamCount()>1
If typeParameterInfo.ConstructedType Is Nothing Then
' "Type '|1' cannot be used in this context because '|1' is an 'Out|In' type parameter."
' Used for simple errors where the erroneous generic-param is NOT inside a generic binding:
' e.g. "Sub f(ByVal a as O)" for some parameter declared as "Out O"
' gives the error "An 'Out' parameter like 'O' cannot be user here".
AppendVarianceDiagnosticInfo(diagnostics,
ErrorFactory.ErrorInfo(If(inappropriateOut,
ERRID.ERR_VarianceOutParamDisallowed1,
ERRID.ERR_VarianceInParamDisallowed1),
type.Name))
ElseIf constructionDepth <= 1 Then
If typeParameterInfo.ConstructedType.Arity <= 1 Then
' "Type '|1' cannot be used in this context because '|1' is an 'Out|In' type parameter."
' e.g. "Sub f(ByVal a As IEnumerable(Of O))" yields
' "An 'Out' parameter like 'O' cannot be used here."
AppendVarianceDiagnosticInfo(diagnostics,
ErrorFactory.ErrorInfo(If(inappropriateOut,
ERRID.ERR_VarianceOutParamDisallowed1,
ERRID.ERR_VarianceInParamDisallowed1),
type.Name))
Else
Debug.Assert(typeParameterInfo.ConstructedType.Arity > 1)
' "Type '|1' cannot be used for the '|2' in '|3' in this context because '|1' is an 'Out|In' type parameter."
' e.g. "Sub f(ByVal a As IDoubleEnumerable(Of O,I)) yields
' "An 'Out' parameter like 'O' cannot be used for type parameter 'T1' of 'IDoubleEnumerable(Of T1,T2)'."
AppendVarianceDiagnosticInfo(diagnostics,
ErrorFactory.ErrorInfo(If(inappropriateOut,
ERRID.ERR_VarianceOutParamDisallowedForGeneric3,
ERRID.ERR_VarianceInParamDisallowedForGeneric3),
type.Name,
typeParameterInfo.TypeParameter.Name,
CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType.OriginalDefinition)))
End If
Else
Debug.Assert(constructionDepth > 1)
If typeParameterInfo.ConstructedType.Arity <= 1 Then
' "Type '|1' cannot be used in '|2' in this context because '|1' is an 'Out|In' type parameter."
' e.g. "Sub f(ByVal a as Func(Of IEnumerable(Of O), IEnumerable(Of O))" yields
' "In 'IEnumerable(Of O)' here, an 'Out' parameter like 'O' cannot be used."
AppendVarianceDiagnosticInfo(diagnostics,
ErrorFactory.ErrorInfo(If(inappropriateOut,
ERRID.ERR_VarianceOutParamDisallowedHere2,
ERRID.ERR_VarianceInParamDisallowedHere2),
type.Name,
CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType)))
Else
Debug.Assert(typeParameterInfo.ConstructedType.Arity > 1)
' "Type '|1' cannot be used in '|2' for the '|3' in '|4' in this context because '|1' is an 'Out' type parameter."
' e.g. "Sub f(ByVal a as IEnumerable(Of Func(Of O,O))" yields
' "In 'Func(Of O,O)' here, an 'Out' parameter like 'O' cannot be used for type parameter 'Tresult' of 'Func(Of Tresult,T)'."
AppendVarianceDiagnosticInfo(diagnostics,
ErrorFactory.ErrorInfo(If(inappropriateOut,
ERRID.ERR_VarianceOutParamDisallowedHereForGeneric4,
ERRID.ERR_VarianceInParamDisallowedHereForGeneric4),
type.Name,
CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType),
typeParameterInfo.TypeParameter.Name,
CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType.OriginalDefinition)))
End If
End If
Case Else
Throw ExceptionUtilities.UnexpectedValue(context)
End Select
End If
Case SymbolKind.ArrayType
' 2. if T is an array U():
GenerateVarianceDiagnosticsForTypeRecursively(DirectCast(type, ArrayTypeSymbol).ElementType,
requiredVariance,
context,
typeParameterInfo,
constructionDepth,
diagnostics)
Case SymbolKind.NamedType
Dim namedType = DirectCast(type.GetTupleUnderlyingTypeOrSelf(), NamedTypeSymbol)
If Not namedType.IsGenericType Then
Return
End If
' 3. T is a construction X1(Of T11...)...Xn(Of Tn1...) of some generic struct/class/interface/delegate X1(Of X11...)...Xn(Of Xn1...)
' Special check, discussed above, for better error-reporting when we find a generic binding in an
' illegal contravariant position
If requiredVariance <> VarianceKind.Out Then
Dim outermostVarianceContainerOfType As NamedTypeSymbol = Nothing
Dim container As NamedTypeSymbol = type.ContainingType
While container IsNot Nothing
If container.TypeParameters.HaveVariance() Then
outermostVarianceContainerOfType = container.OriginalDefinition
End If
container = container.ContainingType
End While
Dim outermostVarianceContainerOfContext As NamedTypeSymbol = Nothing
container = Me
Do
If container.TypeParameters.HaveVariance() Then
outermostVarianceContainerOfContext = container
End If
container = container.ContainingType
Loop While container IsNot Nothing
If outermostVarianceContainerOfType IsNot Nothing AndAlso outermostVarianceContainerOfType Is outermostVarianceContainerOfContext Then
' ERRID_VarianceTypeDisallowed2. "Type '|1' cannot be used in this context because both the context and the definition of '|1' are nested within type '|2', and '|2' has 'In' or 'Out' type parameters. Consider moving '|1' outside of '|2'."
' ERRID_VarianceTypeDisallowedForGeneric4. "Type '|1' cannot be used for the '|3' in '|4' in this context because both the context and the definition of '|1' are nested within type '|2', and '|2' has 'In' or 'Out' type parameters. Consider moving '|1' outside of '|2'."
' ERRID_VarianceTypeDisallowedHere3. "Type '|1' cannot be used in '|3' in this context because both the context and the definition of '|1' are nested within type '|2', and '|2' has 'In' or 'Out' type parameters. Consider moving '|1' outside of '|2'."
' ERRID_VarianceTypeDisallowedHereForGeneric5. "Type '|1' cannot be used for the '|4' of '|5' in '|3' in this context because both the context and the definition of '|1' are nested within type '|2', and '|2' has 'In' or 'Out' type parameters. Consider moving '|1' outside of '|2'."
If typeParameterInfo.ConstructedType Is Nothing Then
AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceTypeDisallowed2,
CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(type.OriginalDefinition),
CustomSymbolDisplayFormatter.QualifiedName(outermostVarianceContainerOfType)))
ElseIf constructionDepth <= 1 Then
If typeParameterInfo.ConstructedType.Arity <= 1 Then
AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceTypeDisallowed2,
CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(type.OriginalDefinition),
CustomSymbolDisplayFormatter.QualifiedName(outermostVarianceContainerOfType)))
Else
Debug.Assert(typeParameterInfo.ConstructedType.Arity > 1)
AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceTypeDisallowedForGeneric4,
CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(type.OriginalDefinition),
CustomSymbolDisplayFormatter.QualifiedName(outermostVarianceContainerOfType),
typeParameterInfo.TypeParameter.Name,
CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType.OriginalDefinition)))
End If
Else
Debug.Assert(constructionDepth > 1)
If typeParameterInfo.ConstructedType.Arity <= 1 Then
AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceTypeDisallowedHere3,
CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(type.OriginalDefinition),
CustomSymbolDisplayFormatter.QualifiedName(outermostVarianceContainerOfType),
CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType)))
Else
Debug.Assert(typeParameterInfo.ConstructedType.Arity > 1)
AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceTypeDisallowedHereForGeneric5,
CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(type.OriginalDefinition),
CustomSymbolDisplayFormatter.QualifiedName(outermostVarianceContainerOfType),
CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType),
typeParameterInfo.TypeParameter.Name,
CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType.OriginalDefinition)))
End If
End If
Return
End If
End If
' The general code below will catch the case of nullables "T?" or "Nullable(Of T)", which require T to
' be invariant. But we want more specific error reporting for this case, so we check for it first.
If namedType.IsNullableType() Then
Debug.Assert(namedType.TypeParameters(0).Variance = VarianceKind.None, "unexpected: a nullable type should have one generic parameter with no variance")
If namedType.TypeArgumentsNoUseSiteDiagnostics(0).IsValueType Then
GenerateVarianceDiagnosticsForTypeRecursively(namedType.TypeArgumentsNoUseSiteDiagnostics(0),
VarianceKind.None,
VarianceContext.Nullable,
New VarianceDiagnosticsTargetTypeParameter(namedType, 0),
constructionDepth,
diagnostics)
End If
Return
End If
' "Type" will refer to the last generic binding, Xn(Of Tn1...). So we have to check all the way up to X1.
Do
For argumentIndex As Integer = 0 To namedType.Arity - 1
' nb. the InvertVariance() here is the only difference between covariantly-valid and contravariantly-valid
' for generic constructions.
Dim argumentRequiredVariance As VarianceKind
Select Case requiredVariance
Case VarianceKind.In
Select Case namedType.TypeParameters(argumentIndex).Variance
Case VarianceKind.In
argumentRequiredVariance = VarianceKind.Out
Case VarianceKind.Out
argumentRequiredVariance = VarianceKind.In
Case Else
argumentRequiredVariance = VarianceKind.None
End Select
Case VarianceKind.Out
argumentRequiredVariance = namedType.TypeParameters(argumentIndex).Variance
Case Else
argumentRequiredVariance = VarianceKind.None
End Select
GenerateVarianceDiagnosticsForTypeRecursively(namedType.TypeArgumentsNoUseSiteDiagnostics(argumentIndex),
argumentRequiredVariance,
VarianceContext.Complex,
New VarianceDiagnosticsTargetTypeParameter(namedType, argumentIndex),
constructionDepth + 1,
diagnostics)
Next
namedType = namedType.ContainingType
Loop While namedType IsNot Nothing
Case SymbolKind.ErrorType
Case Else
Throw ExceptionUtilities.UnexpectedValue(type.Kind)
End Select
End Sub
Private Sub GenerateVarianceDiagnosticsForMethod(
method As MethodSymbol,
diagnostics As BindingDiagnosticBag,
<[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
)
Debug.Assert(Not HaveDiagnostics(infosBuffer))
Select Case method.MethodKind
Case MethodKind.EventAdd, MethodKind.EventRemove, MethodKind.PropertyGet, MethodKind.PropertySet
Return
End Select
GenerateVarianceDiagnosticsForParameters(method.Parameters, diagnostics, infosBuffer)
' Return type is valid covariantly
Debug.Assert(Not HaveDiagnostics(infosBuffer))
GenerateVarianceDiagnosticsForType(method.ReturnType, VarianceKind.Out, VarianceContext.Return, infosBuffer)
If HaveDiagnostics(infosBuffer) Then
Dim location As Location
Dim syntax As MethodBaseSyntax = method.GetDeclaringSyntaxNode(Of MethodBaseSyntax)()
If syntax Is Nothing AndAlso method.MethodKind = MethodKind.DelegateInvoke Then
syntax = method.ContainingType.GetDeclaringSyntaxNode(Of MethodBaseSyntax)()
End If
Dim asClause As AsClauseSyntax = If(syntax IsNot Nothing, syntax.AsClauseInternal, Nothing)
If asClause IsNot Nothing Then
location = asClause.Type.GetLocation()
Else
location = method.Locations(0)
End If
ReportDiagnostics(diagnostics, location, infosBuffer)
End If
GenerateVarianceDiagnosticsForConstraints(method.TypeParameters, diagnostics, infosBuffer)
End Sub
Private Sub GenerateVarianceDiagnosticsForParameters(
parameters As ImmutableArray(Of ParameterSymbol),
diagnostics As BindingDiagnosticBag,
<[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
)
Debug.Assert(Not HaveDiagnostics(infosBuffer))
' Each ByVal Pi is valid contravariantly, and each ByRef Pi is valid invariantly
For Each param As ParameterSymbol In parameters
Dim requiredVariance As VarianceKind
Dim context As VarianceContext
If param.IsByRef Then
requiredVariance = VarianceKind.None
context = VarianceContext.ByRef
Else
requiredVariance = VarianceKind.In
context = VarianceContext.ByVal
End If
GenerateVarianceDiagnosticsForType(param.Type, requiredVariance, context, infosBuffer)
If HaveDiagnostics(infosBuffer) Then
Dim location As Location
Dim syntax As ParameterSyntax = param.GetDeclaringSyntaxNode(Of ParameterSyntax)()
If syntax IsNot Nothing AndAlso syntax.AsClause IsNot Nothing Then
location = syntax.AsClause.Type.GetLocation()
Else
location = param.Locations(0)
End If
ReportDiagnostics(diagnostics, location, infosBuffer)
End If
Next
End Sub
Private Sub GenerateVarianceDiagnosticsForConstraints(
parameters As ImmutableArray(Of TypeParameterSymbol),
diagnostics As BindingDiagnosticBag,
<[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
)
Debug.Assert(Not HaveDiagnostics(infosBuffer))
' Each constraint on U1...Un is valid contravariantly
' "It is character-building to consider why this is required" [Eric Lippert, 2008]
' Interface IReadOnly(Of Out T) | Class Zoo(Of T) | Dim m As IReadOnly(Of Mammal) = new Zoo(Of Mammal) ' OK through inheritance
' Sub Fun(Of U As T)() | Implements IReadOnly(Of T) | Dim a as IReadOnly(Of Animal) = m ' OK through covariance
' End Interface | End Class | a.Fun(Of Fish)() ' BAD: Fish is an Animal (satisfies "U as T"), but fun is expecting a mammal!
For Each param As TypeParameterSymbol In parameters
For Each constraint As TypeSymbol In param.ConstraintTypesNoUseSiteDiagnostics
GenerateVarianceDiagnosticsForType(constraint, VarianceKind.In, VarianceContext.Constraint, infosBuffer)
If HaveDiagnostics(infosBuffer) Then
Dim location As Location = param.Locations(0)
For Each constraintInfo As TypeParameterConstraint In param.GetConstraints()
If constraintInfo.TypeConstraint IsNot Nothing AndAlso
constraintInfo.TypeConstraint.IsSameTypeIgnoringAll(constraint) Then
location = constraintInfo.LocationOpt
Exit For
End If
Next
ReportDiagnostics(diagnostics, location, infosBuffer)
End If
Next
Next
End Sub
Private Sub GenerateVarianceDiagnosticsForProperty(
[property] As PropertySymbol,
diagnostics As BindingDiagnosticBag,
<[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
)
Debug.Assert(Not HaveDiagnostics(infosBuffer))
' Gettable: requires covariance. Settable: requires contravariance. Gettable and settable: requires invariance.
Dim requiredVariance As VarianceKind
Dim context As VarianceContext
If [property].IsReadOnly Then
requiredVariance = VarianceKind.Out
context = VarianceContext.ReadOnlyProperty
ElseIf [property].IsWriteOnly Then
requiredVariance = VarianceKind.In
context = VarianceContext.WriteOnlyProperty
Else
requiredVariance = VarianceKind.None
context = VarianceContext.Property
End If
GenerateVarianceDiagnosticsForType([property].Type, requiredVariance, context, infosBuffer)
If HaveDiagnostics(infosBuffer) Then
Dim location As Location
Dim syntax As PropertyStatementSyntax = [property].GetDeclaringSyntaxNode(Of PropertyStatementSyntax)()
If syntax IsNot Nothing AndAlso syntax.AsClause IsNot Nothing Then
location = syntax.AsClause.Type.GetLocation()
Else
location = [property].Locations(0)
End If
ReportDiagnostics(diagnostics, location, infosBuffer)
End If
' A property might be declared with extra parameters, so we have to check that these are variance-valid.
GenerateVarianceDiagnosticsForParameters([property].Parameters, diagnostics, infosBuffer)
End Sub
Private Sub GenerateVarianceDiagnosticsForEvent(
[event] As EventSymbol,
diagnostics As BindingDiagnosticBag,
<[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
)
Debug.Assert(Not HaveDiagnostics(infosBuffer))
Dim type As TypeSymbol = [event].Type
If Not type.IsDelegateType() Then
Return
End If
If type.ImplicitlyDefinedBy() Is [event] Then
Return
End If
GenerateVarianceDiagnosticsForType(type, VarianceKind.In, VarianceContext.Complex, infosBuffer)
If HaveDiagnostics(infosBuffer) Then
Dim location As Location
Dim syntax As EventStatementSyntax = [event].GetDeclaringSyntaxNode(Of EventStatementSyntax)()
If syntax IsNot Nothing AndAlso syntax.AsClause IsNot Nothing Then
location = syntax.AsClause.Type.GetLocation()
Else
location = [event].Locations(0)
End If
ReportDiagnostics(diagnostics, location, infosBuffer)
End If
End Sub
''' <summary>
''' Ensure all attributes on all members in the named type are bound.
''' </summary>
Private Sub BindAllMemberAttributes(cancellationToken As CancellationToken)
' Ensure all members are declared
Dim lookup = Me.MemberAndInitializerLookup
Dim haveExtensionMethods As Boolean = False
' Now bind all attributes on all members. This must be done after the members are declared to avoid
' infinite recursion.
For Each syms In lookup.Members.Values
For Each sym In syms
sym.GetAttributes()
' Make a note of extension methods
If Not haveExtensionMethods Then
haveExtensionMethods = (sym.Kind = SymbolKind.Method AndAlso DirectCast(sym, MethodSymbol).IsExtensionMethod)
End If
cancellationToken.ThrowIfCancellationRequested()
Next
Next
If haveExtensionMethods Then
Debug.Assert(Me.MightContainExtensionMethods)
m_containingModule.RecordPresenceOfExtensionMethods()
Debug.Assert(_lazyContainsExtensionMethods <> ThreeState.False)
_lazyContainsExtensionMethods = ThreeState.True
' At this point we already processed all the attributes on the type.
' and should know whether there is an explicit Extension attribute on it.
' If there is an explicit attribute, or we passed through this code before,
' m_lazyEmitExtensionAttribute should have known value.
If _lazyEmitExtensionAttribute = ThreeState.Unknown Then
' We need to emit an Extension attribute on the type.
' Can we locate it?
Dim useSiteInfo As UseSiteInfo(Of AssemblySymbol) = Nothing
m_containingModule.ContainingSourceAssembly.DeclaringCompilation.GetExtensionAttributeConstructor(useSiteInfo:=useSiteInfo)
If useSiteInfo.DiagnosticInfo IsNot Nothing Then
' Note, we are storing false because, even though we should emit the attribute,
' we can't do that due to the use site error.
_lazyEmitExtensionAttribute = ThreeState.False
' also notify the containing assembly to not use the extension attribute
m_containingModule.ContainingSourceAssembly.AnErrorHasBeenReportedAboutExtensionAttribute()
Else
' We have extension methods, we don't have explicit Extension attribute
' on the type, which we were able to locate. Should emit it.
Debug.Assert(_lazyEmitExtensionAttribute <> ThreeState.False)
_lazyEmitExtensionAttribute = ThreeState.True
End If
End If
Else
Debug.Assert(_lazyContainsExtensionMethods <> ThreeState.True)
_lazyContainsExtensionMethods = ThreeState.False
Debug.Assert(_lazyEmitExtensionAttribute <> ThreeState.True)
_lazyEmitExtensionAttribute = ThreeState.False
End If
Debug.Assert(_lazyEmitExtensionAttribute <> ThreeState.Unknown)
Debug.Assert(_lazyContainsExtensionMethods <> ThreeState.Unknown)
Debug.Assert(_lazyEmitExtensionAttribute = ThreeState.False OrElse _lazyContainsExtensionMethods = ThreeState.True)
End Sub
#End Region
#Region "Containers"
Public Overrides ReadOnly Property ContainingSymbol As Symbol
Get
Return _containingSymbol
End Get
End Property
Public Overrides ReadOnly Property ContainingType As NamedTypeSymbol
Get
Return TryCast(_containingSymbol, NamedTypeSymbol)
End Get
End Property
Public Overrides ReadOnly Property ContainingModule As ModuleSymbol
Get
Return m_containingModule
End Get
End Property
Public ReadOnly Property ContainingSourceModule As SourceModuleSymbol
Get
Return m_containingModule
End Get
End Property
#End Region
#Region "Flags Encoded Properties"
Public Overrides ReadOnly Property DeclaredAccessibility As Accessibility
Get
Return CType((_flags And SourceTypeFlags.AccessibilityMask), Accessibility)
End Get
End Property
Public Overrides ReadOnly Property IsMustInherit As Boolean
Get
Return (_flags And SourceTypeFlags.MustInherit) <> 0
End Get
End Property
Public Overrides ReadOnly Property IsNotInheritable As Boolean
Get
Return (_flags And SourceTypeFlags.NotInheritable) <> 0
End Get
End Property
Friend Overrides ReadOnly Property ShadowsExplicitly As Boolean
Get
Return (_flags And SourceTypeFlags.Shadows) <> 0
End Get
End Property
Public Overrides ReadOnly Property TypeKind As TypeKind
Get
Return CType((_flags And SourceTypeFlags.TypeKindMask) >> CUInt(SourceTypeFlags.TypeKindShift), TypeKind)
End Get
End Property
Friend Overrides ReadOnly Property IsInterface As Boolean
Get
Return Me.TypeKind = TypeKind.Interface
End Get
End Property
Friend ReadOnly Property IsPartial As Boolean
Get
Return (_flags And SourceTypeFlags.Partial) <> 0
End Get
End Property
#End Region
#Region "Syntax"
Friend ReadOnly Property TypeDeclaration As MergedTypeDeclaration
Get
Return _declaration
End Get
End Property
Public Overrides ReadOnly Property IsImplicitlyDeclared As Boolean
Get
Return False
End Get
End Property
Public NotOverridable Overrides ReadOnly Property IsScriptClass As Boolean
Get
Dim kind = _declaration.Declarations(0).Kind
Return kind = DeclarationKind.Script OrElse kind = DeclarationKind.Submission
End Get
End Property
Public NotOverridable Overrides ReadOnly Property IsImplicitClass As Boolean
Get
Return _declaration.Declarations(0).Kind = DeclarationKind.ImplicitClass
End Get
End Property
Public NotOverridable Overrides ReadOnly Property Arity As Integer
Get
Return _declaration.Arity
End Get
End Property
' Get the declaration kind. Used to match up symbols with declarations in the BinderCache.
' Name, Arity, and DeclarationKind must match to exactly one source symbol, even in the presence
' of errors.
Friend ReadOnly Property DeclarationKind As DeclarationKind
Get
Return _declaration.Kind
End Get
End Property
Public NotOverridable Overrides ReadOnly Property Name As String
Get
Return _name
End Get
End Property
Friend NotOverridable Overrides ReadOnly Property MangleName As Boolean
Get
Return Arity > 0
End Get
End Property
''' <summary>
''' Should return full emitted namespace name for a top level type if the name
''' might be different in case from containing namespace symbol full name, Nothing otherwise.
'''
''' Although namespaces unify based on case-insensitive name, VB uses the casing the namespace
''' declaration surround the class definition for the name emitted to metadata.
'''
''' Namespace GOO
''' Class X
''' End Class
''' ENd Namespace
''' Namespace goo
''' Class Y
''' End Class
''' ENd Namespace
'''
''' In metadata, these are classes "GOO.X" and "goo.Y" (and thus appear in different namespaces
''' when imported into C#.) This function determines the casing of the namespace part of a class, if needed
''' to override the namespace name.
''' </summary>
Friend Overrides Function GetEmittedNamespaceName() As String
Dim containingSourceNamespace = TryCast(_containingSymbol, SourceNamespaceSymbol)
If containingSourceNamespace IsNot Nothing AndAlso containingSourceNamespace.HasMultipleSpellings Then
' Find the namespace spelling surrounding the first declaration.
Debug.Assert(Locations.Length > 0)
Dim firstLocation = Me.DeclaringCompilation.FirstSourceLocation(Locations)
Debug.Assert(firstLocation.IsInSource)
Return containingSourceNamespace.GetDeclarationSpelling(firstLocation.SourceTree, firstLocation.SourceSpan.Start)
End If
Return Nothing
End Function
Friend NotOverridable Overrides Function GetLexicalSortKey() As LexicalSortKey
' WARNING: this should not allocate memory!
If Not _lazyLexicalSortKey.IsInitialized Then
_lazyLexicalSortKey.SetFrom(_declaration.GetLexicalSortKey(DeclaringCompilation))
End If
Return _lazyLexicalSortKey
End Function
Public NotOverridable Overrides ReadOnly Property Locations As ImmutableArray(Of Location)
Get
Dim result = _declaration.NameLocations
Return result
End Get
End Property
''' <summary>
''' Syntax references of all parts of the type declaration.
''' Submission and script classes are represented by their containing <see cref="CompilationUnitSyntax"/>,
''' implicit class can be represented by <see cref="CompilationUnitSyntax"/> or <see cref="NamespaceBlockSyntax"/>.
''' </summary>
Public ReadOnly Property SyntaxReferences As ImmutableArray(Of SyntaxReference)
Get
Return _declaration.SyntaxReferences
End Get
End Property
Public NotOverridable Overrides ReadOnly Property DeclaringSyntaxReferences As ImmutableArray(Of SyntaxReference)
Get
Return GetDeclaringSyntaxReferenceHelper(SyntaxReferences)
End Get
End Property
#End Region
#Region "Member from Syntax"
' Types defined in source code go through the following states, described here.
' The data that is computed by each state is stored in a contained immutable object, for storage efficiency
' and ease of lock-free implementation.
' 1) Created. Only the name, accessibility, arity, typekind, type parameters (but not their names),
' and references to the source code declarations are known.
' No access to the syntax tree is needed for this state, only the declaration.
' 2) Nested types created. No errors are diagnosed yet. Still no access to the syntax tree is
' needed for this state.
' 3) Type parameters names, variance, and constraints are known. Errors relating to type parameters
' are diagnosed. This needs to be done before resolving inheritance, because the base type can
' involve the type parameters.
' 4) Inheritance resolved. The base type, interfaces, type parameter names, variance, and
' constraints are known. Errors relating to partial types are reported.
' 5) Members declared. All members of the type are created, and errors with them reported.
' Errors relating to nested type are reported here also.
'
' Which phases have been completed is tracked by whether the state data for that
' that phase has been initialized. This allows us to update phases in a lock-free
' manner, which avoids many deadlock possibilities. It also makes sure that diagnostics are
' reported once and only once.
'
' The states are not visible outside the class, the class moves to the given
' state on demand. Care must be given in implementing the transitions to avoid
' deadlock or infinite recursion.
' Given the syntax declaration, and a container, get the symbol relating to that syntax.
' This is done by looking up the name from the declaration in the container, handling duplicates, arity, and
' so forth correctly.
Friend Shared Function FindSymbolFromSyntax(declarationSyntax As TypeStatementSyntax,
container As NamespaceOrTypeSymbol,
sourceModule As ModuleSymbol) As SourceNamedTypeSymbol
Dim childName As String = declarationSyntax.Identifier.ValueText
Dim childArity As Integer = DeclarationTreeBuilder.GetArity(declarationSyntax.TypeParameterList)
Dim childDeclKind As DeclarationKind = DeclarationTreeBuilder.GetKind(declarationSyntax.Kind)
Return FindSymbolInContainer(childName, childArity, childDeclKind, container, sourceModule)
End Function
' Given the syntax declaration, and a container, get the symbol relating to that syntax.
' This is done by lookup up the name from the declaration in the container, handling duplicates, arity, and
' so forth correctly.
Friend Shared Function FindSymbolFromSyntax(declarationSyntax As EnumStatementSyntax,
container As NamespaceOrTypeSymbol,
sourceModule As ModuleSymbol) As SourceNamedTypeSymbol
Dim childName As String = declarationSyntax.Identifier.ValueText
Dim childArity As Integer = 0
Dim childDeclKind As DeclarationKind = DeclarationTreeBuilder.GetKind(declarationSyntax.Kind)
Return FindSymbolInContainer(childName, childArity, childDeclKind, container, sourceModule)
End Function
' Given the syntax declaration, and a container, get the symbol relating to that syntax.
' This is done by lookup up the name from the declaration in the container, handling duplicates, arity, and
' so forth correctly.
Friend Shared Function FindSymbolFromSyntax(declarationSyntax As DelegateStatementSyntax,
container As NamespaceOrTypeSymbol,
sourceModule As ModuleSymbol) As SourceNamedTypeSymbol
Dim childName As String = declarationSyntax.Identifier.ValueText
Dim childArity As Integer = DeclarationTreeBuilder.GetArity(declarationSyntax.TypeParameterList)
Dim childDeclKind As DeclarationKind = VisualBasic.Symbols.DeclarationKind.Delegate
Return FindSymbolInContainer(childName, childArity, childDeclKind, container, sourceModule)
End Function
' Helper for FindSymbolFromSyntax. Finds a child source type based on name, arity, DeclarationKind.
Private Shared Function FindSymbolInContainer(childName As String,
childArity As Integer,
childDeclKind As DeclarationKind,
container As NamespaceOrTypeSymbol,
sourceModule As ModuleSymbol) As SourceNamedTypeSymbol
' We need to find the correct symbol, even in error cases. There must be only one
' symbol that is a source symbol, defined in our module, with the given name, arity,
' and declaration kind. The declaration table merges together symbols with the same
' arity, name, and declaration kind (regardless of the Partial modifier).
For Each child In container.GetTypeMembers(childName, childArity)
Dim sourceType = TryCast(child, SourceNamedTypeSymbol)
If sourceType IsNot Nothing Then
If (sourceType.ContainingModule Is sourceModule AndAlso
sourceType.DeclarationKind = childDeclKind) Then
Return sourceType
End If
End If
Next
Return Nothing
End Function
#End Region
#Region "Members (phase 5)"
''' <summary>
''' Structure to wrap the different arrays of members.
''' </summary>
Friend Class MembersAndInitializers
Friend ReadOnly Members As Dictionary(Of String, ImmutableArray(Of Symbol))
Friend ReadOnly StaticInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer))
Friend ReadOnly InstanceInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer))
Friend ReadOnly StaticInitializersSyntaxLength As Integer
Friend ReadOnly InstanceInitializersSyntaxLength As Integer
''' <summary>
''' Initializes a new instance of the <see cref="MembersAndInitializers" /> class.
''' </summary>
''' <param name="members">The members.</param>
''' <param name="staticInitializers">The static initializers.</param>
''' <param name="instanceInitializers">The instance initializers.</param>
Friend Sub New(
members As Dictionary(Of String, ImmutableArray(Of Symbol)),
staticInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer)),
instanceInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer)),
staticInitializersSyntaxLength As Integer,
instanceInitializersSyntaxLength As Integer)
Me.Members = members
Me.StaticInitializers = staticInitializers
Me.InstanceInitializers = instanceInitializers
Debug.Assert(staticInitializersSyntaxLength = If(staticInitializers.IsDefaultOrEmpty, 0, staticInitializers.Sum(Function(s) s.Sum(Function(i) If(Not i.IsMetadataConstant, i.Syntax.Span.Length, 0)))))
Debug.Assert(instanceInitializersSyntaxLength = If(instanceInitializers.IsDefaultOrEmpty, 0, instanceInitializers.Sum(Function(s) s.Sum(Function(i) i.Syntax.Span.Length))))
Me.StaticInitializersSyntaxLength = staticInitializersSyntaxLength
Me.InstanceInitializersSyntaxLength = instanceInitializersSyntaxLength
End Sub
End Class
''' <summary>
''' Accumulates different members kinds used while building the members.
''' </summary>
Friend NotInheritable Class MembersAndInitializersBuilder
Friend ReadOnly Members As Dictionary(Of String, ArrayBuilder(Of Symbol)) = New Dictionary(Of String, ArrayBuilder(Of Symbol))(IdentifierComparison.Comparer)
Friend Property StaticInitializers As ArrayBuilder(Of ImmutableArray(Of FieldOrPropertyInitializer))
Friend Property InstanceInitializers As ArrayBuilder(Of ImmutableArray(Of FieldOrPropertyInitializer))
Friend ReadOnly DeferredMemberDiagnostic As ArrayBuilder(Of Symbol) = ArrayBuilder(Of Symbol).GetInstance()
Friend StaticSyntaxLength As Integer = 0
Friend InstanceSyntaxLength As Integer = 0
Friend Function ToReadOnlyAndFree() As MembersAndInitializers
DeferredMemberDiagnostic.Free()
Dim readonlyMembers = New Dictionary(Of String, ImmutableArray(Of Symbol))(IdentifierComparison.Comparer)
For Each memberList In Members.Values
readonlyMembers.Add(memberList(0).Name, memberList.ToImmutableAndFree())
Next
Return New MembersAndInitializers(
readonlyMembers,
If(StaticInitializers IsNot Nothing, StaticInitializers.ToImmutableAndFree(), Nothing),
If(InstanceInitializers IsNot Nothing, InstanceInitializers.ToImmutableAndFree(), Nothing),
StaticSyntaxLength,
InstanceSyntaxLength)
End Function
End Class
''' <summary>
''' Adds a field initializer for the field to list of field initializers
''' </summary>
''' <param name="initializers">All initializers.</param>
''' <param name="computeInitializer">Compute the field initializer to add to the list of initializers.</param>
Friend Shared Sub AddInitializer(ByRef initializers As ArrayBuilder(Of FieldOrPropertyInitializer), computeInitializer As Func(Of Integer, FieldOrPropertyInitializer), ByRef aggregateSyntaxLength As Integer)
Dim initializer = computeInitializer(aggregateSyntaxLength)
If initializers Is Nothing Then
initializers = ArrayBuilder(Of FieldOrPropertyInitializer).GetInstance()
Else
' initializers should be added in syntax order
Debug.Assert(initializer.Syntax.SyntaxTree Is initializers.Last().Syntax.SyntaxTree)
Debug.Assert(initializer.Syntax.Span.Start > initializers.Last().Syntax.Span.Start)
End If
initializers.Add(initializer)
' A constant field of type decimal needs a field initializer, so
' check if it is a metadata constant, not just a constant to exclude
' decimals. Other constants do not need field initializers.
If Not initializer.IsMetadataConstant Then
' ignore leading and trailing trivia of the node
aggregateSyntaxLength += initializer.Syntax.Span.Length
End If
End Sub
''' <summary>
''' Adds an array of initializers to the member collections structure
''' </summary>
''' <param name="allInitializers">All initializers.</param>
''' <param name="siblings">The siblings.</param>
Friend Shared Sub AddInitializers(ByRef allInitializers As ArrayBuilder(Of ImmutableArray(Of FieldOrPropertyInitializer)), siblings As ArrayBuilder(Of FieldOrPropertyInitializer))
If siblings IsNot Nothing Then
If allInitializers Is Nothing Then
allInitializers = New ArrayBuilder(Of ImmutableArray(Of FieldOrPropertyInitializer))()
End If
allInitializers.Add(siblings.ToImmutableAndFree())
End If
End Sub
Protected Function GetTypeMembersDictionary() As Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol))
If _lazyTypeMembers Is Nothing Then
Interlocked.CompareExchange(_lazyTypeMembers, MakeTypeMembers(), Nothing)
Debug.Assert(_lazyTypeMembers IsNot Nothing)
End If
Return _lazyTypeMembers
End Function
' Create symbols for all the nested types, and put them in a lookup indexed by (case-insensitive) name.
Private Function MakeTypeMembers() As Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol))
Dim children As ImmutableArray(Of MergedTypeDeclaration) = _declaration.Children
Debug.Assert(s_emptyTypeMembers.Count = 0)
If children.IsEmpty Then
Return s_emptyTypeMembers
End If
Return children.Select(Function(decl) CreateNestedType(decl)).ToMultiDictionary(
Function(decl) decl.Name,
IdentifierComparison.Comparer)
End Function
Friend Overrides Function GetTypeMembersUnordered() As ImmutableArray(Of NamedTypeSymbol)
Return GetTypeMembersDictionary().Flatten()
End Function
Public Overloads Overrides Function GetTypeMembers() As ImmutableArray(Of NamedTypeSymbol)
Return GetTypeMembersDictionary().Flatten(LexicalOrderSymbolComparer.Instance)
End Function
Public Overloads Overrides Function GetTypeMembers(name As String) As ImmutableArray(Of NamedTypeSymbol)
Dim members As ImmutableArray(Of NamedTypeSymbol) = Nothing
If GetTypeMembersDictionary().TryGetValue(name, members) Then
Return members
End If
Return ImmutableArray(Of NamedTypeSymbol).Empty
End Function
Public Overrides Function GetTypeMembers(name As String, arity As Integer) As ImmutableArray(Of NamedTypeSymbol)
Return GetTypeMembers(name).WhereAsArray(Function(t, arity_) t.Arity = arity_, arity)
End Function
Friend Overrides ReadOnly Property DefaultPropertyName As String
Get
If TypeKind <> TypeKind.Delegate Then
GetMembersAndInitializers() ' Ensure m_defaultPropertyName is set.
Else
Debug.Assert(_defaultPropertyName Is Nothing)
End If
Return _defaultPropertyName
End Get
End Property
Private ReadOnly Property MemberAndInitializerLookup As MembersAndInitializers
Get
Return GetMembersAndInitializers()
End Get
End Property
Private Function GetMembersAndInitializers() As MembersAndInitializers
If _lazyMembersAndInitializers Is Nothing Then
Dim diagBag = BindingDiagnosticBag.GetInstance()
Dim membersAndInitializers = BuildMembersAndInitializers(diagBag)
m_containingModule.AtomicStoreReferenceAndDiagnostics(_lazyMembersAndInitializers, membersAndInitializers, diagBag)
Debug.Assert(_lazyMembersAndInitializers IsNot Nothing)
diagBag.Free()
Dim unused = Me.KnownCircularStruct
#If DEBUG Then
VerifyMembers()
#End If
End If
Return _lazyMembersAndInitializers
End Function
#If DEBUG Then
Protected Overridable Sub VerifyMembers()
End Sub
#End If
Friend ReadOnly Property MembersHaveBeenCreated As Boolean
Get
Return _lazyMembersAndInitializers IsNot Nothing
End Get
End Property
#If DEBUG Then
' A thread local hash table to catch cases when BuildMembersAndInitializers
' is called recursively for the same symbol.
<ThreadStatic>
Private Shared s_SymbolsBuildingMembersAndInitializers As HashSet(Of SourceMemberContainerTypeSymbol)
#End If
Private Function BuildMembersAndInitializers(diagBag As BindingDiagnosticBag) As MembersAndInitializers
Dim membersAndInitializers As MembersAndInitializers
#If DEBUG Then
If s_SymbolsBuildingMembersAndInitializers Is Nothing Then
s_SymbolsBuildingMembersAndInitializers = New HashSet(Of SourceMemberContainerTypeSymbol)(ReferenceEqualityComparer.Instance)
End If
Dim added As Boolean = s_SymbolsBuildingMembersAndInitializers.Add(Me)
Debug.Assert(added)
#End If
Try
' Get type members
Dim typeMembers = GetTypeMembersDictionary()
' Get non-type members
membersAndInitializers = BuildNonTypeMembers(diagBag)
_defaultPropertyName = DetermineDefaultPropertyName(membersAndInitializers.Members, diagBag)
' Find/process partial methods
ProcessPartialMethodsIfAny(membersAndInitializers.Members, diagBag)
' Merge types with non-types
For Each typeSymbols In typeMembers.Values
Dim nontypeSymbols As ImmutableArray(Of Symbol) = Nothing
Dim name = typeSymbols(0).Name
If Not membersAndInitializers.Members.TryGetValue(name, nontypeSymbols) Then
membersAndInitializers.Members.Add(name, StaticCast(Of Symbol).From(typeSymbols))
Else
membersAndInitializers.Members(name) = nontypeSymbols.Concat(StaticCast(Of Symbol).From(typeSymbols))
End If
Next
Finally
#If DEBUG Then
If added Then
s_SymbolsBuildingMembersAndInitializers.Remove(Me)
End If
#End If
End Try
Return membersAndInitializers
End Function
''' <summary> Examines the members collection and builds a set of partial methods if any, otherwise returns nothing </summary>
Private Function FindPartialMethodDeclarations(diagnostics As BindingDiagnosticBag, members As Dictionary(Of String, ImmutableArray(Of Symbol))) As HashSet(Of SourceMemberMethodSymbol)
Dim partialMethods As HashSet(Of SourceMemberMethodSymbol) = Nothing
For Each memberGroup In members
For Each member In memberGroup.Value
Dim method = TryCast(member, SourceMemberMethodSymbol)
If method IsNot Nothing AndAlso method.IsPartial AndAlso method.MethodKind = MethodKind.Ordinary Then
If Not method.IsSub Then
Debug.Assert(method.Locations.Length = 1)
diagnostics.Add(ERRID.ERR_PartialMethodsMustBeSub1, method.NonMergedLocation, method.Name)
Else
If partialMethods Is Nothing Then
partialMethods = New HashSet(Of SourceMemberMethodSymbol)(ReferenceEqualityComparer.Instance)
End If
partialMethods.Add(method)
End If
End If
Next
Next
Return partialMethods
End Function
Private Sub ProcessPartialMethodsIfAny(members As Dictionary(Of String, ImmutableArray(Of Symbol)), diagnostics As BindingDiagnosticBag)
' Detect all partial method declarations
Dim partialMethods As HashSet(Of SourceMemberMethodSymbol) = FindPartialMethodDeclarations(diagnostics, members)
If partialMethods Is Nothing Then
Return
End If
' we have at least one partial method, note that the methods we
' found may have the same names and/or signatures
' NOTE: we process partial methods one-by-one which is not optimal for the
' case where we have a bunch of partial methods with the same name.
' TODO: revise
While partialMethods.Count > 0
Dim originalPartialMethod As SourceMemberMethodSymbol = partialMethods.First()
partialMethods.Remove(originalPartialMethod)
' The best partial method
Dim bestPartialMethod As SourceMemberMethodSymbol = originalPartialMethod
Dim bestPartialMethodLocation As Location = bestPartialMethod.NonMergedLocation
Debug.Assert(bestPartialMethodLocation IsNot Nothing)
' The best partial method implementation
Dim bestImplMethod As SourceMemberMethodSymbol = Nothing
Dim bestImplLocation As Location = Nothing
' Process the members group by name
Dim memberGroup As ImmutableArray(Of Symbol) = members(originalPartialMethod.Name)
For Each member In memberGroup
Dim candidate As SourceMemberMethodSymbol = TryCast(member, SourceMemberMethodSymbol)
If candidate IsNot Nothing AndAlso candidate IsNot originalPartialMethod AndAlso candidate.MethodKind = MethodKind.Ordinary Then
If ComparePartialMethodSignatures(originalPartialMethod, candidate) Then
' candidate location
Dim candidateLocation As Location = candidate.NonMergedLocation
Debug.Assert(candidateLocation IsNot Nothing)
If partialMethods.Remove(candidate) Then
' partial-partial conflict
' the 'best' partial method is the one with the 'smallest'
' location, we should report errors on the other
Dim candidateIsBigger As Boolean = Me.DeclaringCompilation.CompareSourceLocations(bestPartialMethodLocation, candidateLocation) < 0
Dim reportOnMethod As SourceMemberMethodSymbol = If(candidateIsBigger, candidate, bestPartialMethod)
Dim nameToReport As String = reportOnMethod.Name
diagnostics.Add(ERRID.ERR_OnlyOnePartialMethodAllowed2,
If(candidateIsBigger, candidateLocation, bestPartialMethodLocation),
nameToReport, nameToReport)
reportOnMethod.SuppressDuplicateProcDefDiagnostics = True
' change the best partial method if needed
If Not candidateIsBigger Then
bestPartialMethod = candidate
bestPartialMethodLocation = candidateLocation
End If
ElseIf Not candidate.IsPartial Then
' if there are more than one method
If bestImplMethod Is Nothing Then
bestImplMethod = candidate
bestImplLocation = candidateLocation
Else
' the 'best' implementation method is the one with the 'smallest'
' location, we should report errors on the others
Dim candidateIsBigger = Me.DeclaringCompilation.CompareSourceLocations(bestImplLocation, candidateLocation) < 0
Dim reportOnMethod As SourceMemberMethodSymbol = If(candidateIsBigger, candidate, bestImplMethod)
Dim reportedName As String = reportOnMethod.Name
diagnostics.Add(ERRID.ERR_OnlyOneImplementingMethodAllowed3,
If(candidateIsBigger, candidateLocation, bestImplLocation),
reportedName, reportedName, reportedName)
reportOnMethod.SuppressDuplicateProcDefDiagnostics = True
' change the best implementation if needed
If Not candidateIsBigger Then
bestImplMethod = candidate
bestImplLocation = candidateLocation
End If
End If
End If
' NOTE: the rest of partial methods are already processed, those can be safely ignored
End If
End If
Next
' Report ERR_PartialMethodMustBeEmpty
If bestPartialMethod.BlockSyntax IsNot Nothing AndAlso bestPartialMethod.BlockSyntax.Statements.Count > 0 Then
diagnostics.Add(ERRID.ERR_PartialMethodMustBeEmpty, bestPartialMethodLocation)
End If
If bestImplMethod IsNot Nothing Then
' We found the partial method implementation
' Remove the best implementation from members
' NOTE: conflicting partial method declarations and implementations are NOT removed
Dim newMembers = ArrayBuilder(Of Symbol).GetInstance()
For i = 0 To memberGroup.Length - 1
Dim member As Symbol = memberGroup(i)
If bestImplMethod IsNot member Then
newMembers.Add(member)
End If
Next
members(originalPartialMethod.Name) = newMembers.ToImmutableAndFree()
' Assign implementation to best partial method
SourceMemberMethodSymbol.InitializePartialMethodParts(bestPartialMethod, bestImplMethod)
' Report errors on partial method implementation
ReportErrorsOnPartialMethodImplementation(bestPartialMethod, bestImplMethod, bestImplLocation, diagnostics)
Else
' There is no implementation
SourceMemberMethodSymbol.InitializePartialMethodParts(bestPartialMethod, Nothing)
End If
End While
End Sub
Private Sub ReportErrorsOnPartialMethodImplementation(partialMethod As SourceMethodSymbol,
implMethod As SourceMethodSymbol,
implMethodLocation As Location,
diagnostics As BindingDiagnosticBag)
' Report 'Method '...' must be declared 'Private' in order to implement partial method '...'
If implMethod.DeclaredAccessibility <> Accessibility.Private Then
diagnostics.Add(ERRID.ERR_ImplementationMustBePrivate2,
implMethodLocation,
implMethod.Name, partialMethod.Name)
End If
' Check method parameters' names
If partialMethod.ParameterCount > 0 Then
Debug.Assert(partialMethod.ParameterCount = implMethod.ParameterCount)
Dim declMethodParams As ImmutableArray(Of ParameterSymbol) = partialMethod.Parameters
Dim implMethodParams As ImmutableArray(Of ParameterSymbol) = implMethod.Parameters
For index = 0 To declMethodParams.Length - 1
Dim declParameter As ParameterSymbol = declMethodParams(index)
Dim implParameter As ParameterSymbol = implMethodParams(index)
' Check type parameter name
If Not CaseInsensitiveComparison.Equals(declParameter.Name, implParameter.Name) Then
Debug.Assert(implParameter.Locations.Length = 1)
diagnostics.Add(ERRID.ERR_PartialMethodParamNamesMustMatch3,
implParameter.Locations(0),
implParameter.Name, declParameter.Name, implMethod.Name)
End If
Next
End If
' Generic type names/constraints
If implMethod.Arity > 0 Then
Dim declTypeParams As ImmutableArray(Of TypeParameterSymbol) = partialMethod.TypeParameters
Dim implTypeParams As ImmutableArray(Of TypeParameterSymbol) = implMethod.TypeParameters
Debug.Assert(declTypeParams.Length = implTypeParams.Length)
For index = 0 To declTypeParams.Length - 1
Dim declParameter As TypeParameterSymbol = declTypeParams(index)
Dim implParameter As TypeParameterSymbol = implTypeParams(index)
' Check parameter name
If Not CaseInsensitiveComparison.Equals(declParameter.Name, implParameter.Name) Then
Debug.Assert(implParameter.Locations.Length = 1)
diagnostics.Add(ERRID.ERR_PartialMethodTypeParamNameMismatch3,
implParameter.Locations(0),
implParameter.Name, declParameter.Name, implMethod.Name)
End If
Next
' If type parameters constraints don't match at least on one of type
' parameters, report an error on the implementation method
Dim options = SymbolComparisonResults.ArityMismatch Or SymbolComparisonResults.ConstraintMismatch
If MethodSignatureComparer.DetailedCompare(partialMethod, implMethod, options) <> Nothing Then
diagnostics.Add(ERRID.ERR_PartialMethodGenericConstraints2,
implMethodLocation,
implMethod.Name, partialMethod.Name)
End If
End If
End Sub
''' <summary>
''' Compares two methods to check if the 'candidate' can be an implementation of the 'partialDeclaration'.
''' </summary>
Private Function ComparePartialMethodSignatures(partialDeclaration As SourceMethodSymbol, candidate As SourceMethodSymbol) As Boolean
' Don't check values of optional parameters yet, this might cause an infinite cycle.
' Don't check ParamArray mismatch either, might cause us to bind attributes too early.
Dim comparisons = SymbolComparisonResults.AllMismatches And
Not (SymbolComparisonResults.CallingConventionMismatch Or
SymbolComparisonResults.ConstraintMismatch Or
SymbolComparisonResults.OptionalParameterValueMismatch Or
SymbolComparisonResults.ParamArrayMismatch)
Dim result As SymbolComparisonResults = MethodSignatureComparer.DetailedCompare(partialDeclaration, candidate, comparisons)
If result <> Nothing Then
Return False
End If
' Dev10 also compares EQ_Flags { Shared|Overrides|MustOverride|Overloads }, but ignores 'Overloads'
Return partialDeclaration.IsShared = candidate.IsShared AndAlso
partialDeclaration.IsOverrides = candidate.IsOverrides AndAlso
partialDeclaration.IsMustOverride = candidate.IsMustOverride
End Function
Friend Overrides ReadOnly Property KnownCircularStruct As Boolean
Get
If _lazyStructureCycle = ThreeState.Unknown Then
If Not Me.IsStructureType Then
_lazyStructureCycle = ThreeState.False
Else
Dim diagnostics = BindingDiagnosticBag.GetInstance()
Dim hasCycle = Me.CheckStructureCircularity(diagnostics)
' In either case we use AtomicStoreIntegerAndDiagnostics.
m_containingModule.AtomicStoreIntegerAndDiagnostics(_lazyStructureCycle,
If(hasCycle, ThreeState.True, ThreeState.False),
ThreeState.Unknown,
diagnostics)
diagnostics.Free()
End If
End If
Return _lazyStructureCycle = ThreeState.True
End Get
End Property
''' <summary>
''' Poolable data set to be used in structure circularity detection.
''' </summary>
Private Class StructureCircularityDetectionDataSet
''' <summary>
''' Following C# implementation we keep up to 32 data sets so that we do not need to allocate
''' them over and over. In this implementation though, circularity detection in one type can trigger
''' circularity detection in other types while it traverses the types tree. The traversal is being
''' performed breadth-first, so the number of data sets used by one thread is not longer than the
''' length of the longest structure-in-structure nesting chain.
''' </summary>
Private Shared ReadOnly s_pool As New ObjectPool(Of StructureCircularityDetectionDataSet)(
Function() New StructureCircularityDetectionDataSet(), 32)
''' <summary> Set of processed structure types </summary>
Public ReadOnly ProcessedTypes As HashSet(Of NamedTypeSymbol)
Public ReadOnly TypesWithCycle As HashSet(Of NamedTypeSymbol)
''' <summary> Queue element structure </summary>
Public Structure QueueElement
Public ReadOnly Type As NamedTypeSymbol
Public ReadOnly FieldPath As ConsList(Of FieldSymbol)
Public ReadOnly ContainingDefinitionsPath As ConsList(Of NamedTypeSymbol)
Public ReadOnly Report As Boolean
Public Sub New(type As NamedTypeSymbol, fieldPath As ConsList(Of FieldSymbol), containingDefinitionsPath As ConsList(Of NamedTypeSymbol), report As Boolean)
Debug.Assert(type IsNot Nothing)
Debug.Assert(fieldPath IsNot Nothing)
Debug.Assert(containingDefinitionsPath IsNot Nothing)
Me.Type = type
Me.FieldPath = fieldPath
Me.ContainingDefinitionsPath = containingDefinitionsPath
Me.Report = report
End Sub
End Structure
''' <summary> Queue of the types to be processed </summary>
Public ReadOnly Queue As Queue(Of QueueElement)
Private Sub New()
ProcessedTypes = New HashSet(Of NamedTypeSymbol)()
TypesWithCycle = New HashSet(Of NamedTypeSymbol)()
Queue = New Queue(Of QueueElement)
End Sub
Public Shared Function GetInstance() As StructureCircularityDetectionDataSet
Return s_pool.Allocate()
End Function
Public Sub Free()
Me.Queue.Clear()
Me.ProcessedTypes.Clear()
Me.TypesWithCycle.Clear()
s_pool.Free(Me)
End Sub
End Class
''' <summary>
''' Analyzes structure type for circularities. Reports only errors relevant for 'structBeingAnalyzed' type.
''' </summary>
''' <remarks>
''' When VB Dev10 detects circularity it reports the error only once for each cycle. Thus, if the cycle
''' is {S1 --> S2 --> S3 --> S1}, only one error will be reported, which one of S1/S2/S3 will have error
''' is non-deterministic (depends on the order of symbols in a hash table).
'''
''' Moreover, Dev10 analyzes the type graph and reports only one error in case S1 --> S2 --> S1 even if
''' there are two fields referencing S2 from S1.
'''
''' Example:
''' Structure S2
''' Dim s1 As S1
''' End Structure
'''
''' Structure S3
''' Dim s1 As S1
''' End Structure
'''
''' Structure S1
''' Dim s2 As S2 ' ERROR
''' Dim s2_ As S2 ' NO ERROR
''' Dim s3 As S3 ' ERROR
''' End Structure
'''
''' Dev10 also reports only one error for all the cycles starting with the same field, which one is reported
''' depends on the declaration order. Current implementation reports all of the cycles for consistency.
''' See testcases MultiplyCyclesInStructure03 and MultiplyCyclesInStructure04 (report different errors in Dev10).
''' </remarks>
Private Function CheckStructureCircularity(diagnostics As BindingDiagnosticBag) As Boolean
' Must be a structure
Debug.Assert(Me.IsValueType AndAlso Not Me.IsTypeParameter)
' Allocate data set
Dim data = StructureCircularityDetectionDataSet.GetInstance()
data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement(Me, ConsList(Of FieldSymbol).Empty, ConsList(Of NamedTypeSymbol).Empty.Prepend(Me), report:=True))
Dim hasCycle = False
Try
While data.Queue.Count > 0
Dim current = data.Queue.Dequeue()
If Not data.ProcessedTypes.Add(current.Type) Then
' In some cases the queue may contain two same types which are not processed yet
Continue While
End If
If data.TypesWithCycle.Contains(current.Type.OriginalDefinition) Then
Continue While
End If
Dim cycleReportedForCurrentType As Boolean = False
' iterate over non-static fields of structure data type
For Each member In current.Type.GetMembers()
Dim field = TryCast(member, FieldSymbol)
If field IsNot Nothing AndAlso Not field.IsShared Then
Dim fieldType = TryCast(field.Type, NamedTypeSymbol)
If fieldType IsNot Nothing AndAlso fieldType.IsValueType Then
' if the type is constructed from a generic structure, we should
' process ONLY fields which types are instantiated from type arguments
If Not field.IsDefinition AndAlso field.Type.Equals(field.OriginalDefinition.Type) Then
Continue For
End If
If current.ContainingDefinitionsPath.ContainsReference(fieldType.OriginalDefinition) Then
' a cycle detected
data.TypesWithCycle.Add(fieldType.OriginalDefinition)
If current.Report AndAlso Not cycleReportedForCurrentType AndAlso fieldType.OriginalDefinition.Equals(Me) Then
' the cycle includes 'current.Path' and ends with 'field'; the order is reversed in the list
Dim cycleFields = New ConsList(Of FieldSymbol)(field, current.FieldPath)
' generate a message info
Dim diagnosticInfos = ArrayBuilder(Of DiagnosticInfo).GetInstance()
Dim firstField As FieldSymbol = Nothing ' after the cycle is processed this will hold the last element in the list
While Not cycleFields.IsEmpty
firstField = cycleFields.Head
' generate next field description
diagnosticInfos.Add(ErrorFactory.ErrorInfo(ERRID.ERR_RecordEmbeds2,
firstField.ContainingType,
firstField.Type,
firstField.Name))
cycleFields = cycleFields.Tail
End While
diagnosticInfos.ReverseContents()
Debug.Assert(firstField IsNot Nothing)
' Report an error
Dim symbolToReportErrorOn As Symbol = If(firstField.AssociatedSymbol, DirectCast(firstField, Symbol))
Debug.Assert(symbolToReportErrorOn.Locations.Length > 0)
diagnostics.Add(ERRID.ERR_RecordCycle2,
symbolToReportErrorOn.Locations(0),
firstField.ContainingType.Name,
New CompoundDiagnosticInfo(diagnosticInfos.ToArrayAndFree()))
' Don't report errors for other fields of this type referencing 'structBeingAnalyzed'
cycleReportedForCurrentType = True
hasCycle = True
End If
ElseIf Not data.ProcessedTypes.Contains(fieldType) AndAlso Not data.TypesWithCycle.Contains(fieldType.OriginalDefinition) Then
' Add to the queue if we don't know yet if it was processed
' NOTE: we want to make sure we report the same error for the same types
' consistently and don't depend on the call order; this solution uses
' the following approach:
' (a) for each cycle we report the error only on the type which is
' 'smaller' than the other types in this cycle; the criteria
' used for detection of the 'smallest' type does not matter;
' (b) thus, this analysis only considers the cycles consisting of the
' types which are 'bigger' than 'structBeingAnalyzed' because we will not
' report the error regarding this cycle for this type anyway
Dim stepIntoType As Boolean = DetectTypeCircularity_ShouldStepIntoType(fieldType.OriginalDefinition)
If stepIntoType OrElse Not fieldType.IsDefinition Then
' enqueue to be processed
' First, visit type as a definition in order to detect the fact that it itself has a cycle.
' This prevents us from going into an infinite generic expansion while visiting constructed form
' of the type below.
data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement(
fieldType.OriginalDefinition, New ConsList(Of FieldSymbol)(field, current.FieldPath),
current.ContainingDefinitionsPath.Prepend(fieldType.OriginalDefinition),
report:=stepIntoType))
Else
' should not process
data.ProcessedTypes.Add(fieldType)
End If
If Not fieldType.IsDefinition Then
' Types constructed from generic types are considered to be a separate types. We never report
' errors on such types. We also process only fields actually changed compared to original generic type.
data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement(
fieldType, New ConsList(Of FieldSymbol)(field, current.FieldPath),
current.ContainingDefinitionsPath,
report:=True))
End If
End If
End If
End If
Next
End While
Finally
data.Free()
End Try
Return hasCycle
End Function
''' <summary>
''' Simple check of whether or not we should step into the type 'typeToTest' during
''' type graph traversal inside 'DetectStructureCircularity' or 'GetDependenceChain'.
'''
''' The following rules are in place:
''' (a) we order all symbols according their first source location
''' comparison rules: first, source file names are compared,
''' then SourceSpan.Start is used for symbols inside the same file;
''' (b) given this order we enter the loop if only 'typeToTest' is 'less' than
''' 'structBeingAnalyzed';
''' (c) we also always enter types from other modules
'''
''' !!! To be ONLY used in 'CheckStructureCircularity'.
''' </summary>
''' <returns>True if detect type circularity code should step into 'typeToTest' type </returns>
Friend Function DetectTypeCircularity_ShouldStepIntoType(typeToTest As NamedTypeSymbol) As Boolean
If typeToTest.ContainingModule Is Nothing OrElse Not typeToTest.ContainingModule.Equals(Me.ContainingModule) Then
' Types from other modules should never be considered target types
Return True
End If
' We can not get the relative order of a declaration without a source location
If typeToTest.Locations.IsEmpty Then
Return True
End If
' We use simple comparison based on source location
Dim typeToTestLocation = typeToTest.Locations(0)
Debug.Assert(Me.Locations.Length > 0)
Dim structBeingAnalyzedLocation = Me.Locations(0)
Dim compilation = Me.DeclaringCompilation
Dim fileCompResult = compilation.CompareSourceLocations(typeToTestLocation, structBeingAnalyzedLocation)
' NOTE: we use '>=' for locations comparison; this is a safeguard against the case where two different
' types are declared in the files with same file name (if possible) and have the same location;
' if we used '>' we would not report the cycle, with '>=' we will report the cycle twice.
Return (fileCompResult > 0) OrElse
((fileCompResult = 0) AndAlso typeToTestLocation.SourceSpan.Start >= structBeingAnalyzedLocation.SourceSpan.Start)
End Function
Private Function DetermineDefaultPropertyName(membersByName As Dictionary(Of String, ImmutableArray(Of Symbol)), diagBag As BindingDiagnosticBag) As String
Dim defaultPropertyName As String = Nothing
For Each pair In membersByName
Dim name = pair.Key
Dim members = pair.Value
Dim defaultProperty As PropertySymbol = Nothing
' Check if any of the properties are marked default.
For Each member In members
If member.Kind = SymbolKind.Property Then
Dim propertySymbol = DirectCast(member, PropertySymbol)
If propertySymbol.IsDefault Then
If defaultPropertyName Is Nothing Then
defaultProperty = propertySymbol
defaultPropertyName = name
If Not defaultProperty.ShadowsExplicitly Then
CheckDefaultPropertyAgainstAllBases(Me, defaultPropertyName, propertySymbol.Locations(0), diagBag)
End If
Else
' "'Default' can be applied to only one property name in a {0}."
diagBag.Add(ERRID.ERR_DuplicateDefaultProps1, propertySymbol.Locations(0), GetKindText())
End If
Exit For
End If
End If
Next
If defaultPropertyName IsNot Nothing AndAlso defaultPropertyName = name Then
Debug.Assert(defaultProperty IsNot Nothing)
' Report an error for any property with this name not marked as default.
For Each member In members
If (member.Kind = SymbolKind.Property) Then
Dim propertySymbol = DirectCast(member, SourcePropertySymbol)
If Not propertySymbol.IsDefault Then
' "'{0}' and '{1}' cannot overload each other because only one is declared 'Default'."
diagBag.Add(ERRID.ERR_DefaultMissingFromProperty2, propertySymbol.Locations(0), defaultProperty, propertySymbol)
End If
End If
Next
End If
Next
Return defaultPropertyName
End Function
' Check all bases of "namedType" and warn if they have a default property named "defaultPropertyName".
Private Sub CheckDefaultPropertyAgainstAllBases(namedType As NamedTypeSymbol, defaultPropertyName As String, location As Location, diagBag As BindingDiagnosticBag)
If namedType.IsInterfaceType() Then
For Each iface In namedType.InterfacesNoUseSiteDiagnostics
CheckDefaultPropertyAgainstBase(defaultPropertyName, iface, location, diagBag)
Next
Else
CheckDefaultPropertyAgainstBase(defaultPropertyName, namedType.BaseTypeNoUseSiteDiagnostics, location, diagBag)
End If
End Sub
' Check and warn if "baseType" has a default property named "defaultProperty Name.
' If "baseType" doesn't have a default property, check its base types.
Private Sub CheckDefaultPropertyAgainstBase(defaultPropertyName As String, baseType As NamedTypeSymbol, location As Location, diagBag As BindingDiagnosticBag)
If baseType IsNot Nothing Then
Dim baseDefaultPropertyName = baseType.DefaultPropertyName
If baseDefaultPropertyName IsNot Nothing Then
If Not CaseInsensitiveComparison.Equals(defaultPropertyName, baseDefaultPropertyName) Then
' BC40007: Default property '{0}' conflicts with the default property '{1}' in the base {2} '{3}'. '{0}' will be the default property
diagBag.Add(ERRID.WRN_DefaultnessShadowed4, location,
defaultPropertyName, baseDefaultPropertyName, baseType.GetKindText(), CustomSymbolDisplayFormatter.ShortErrorName(baseType))
End If
Else
' If this type didn't have a default property name, recursively check base(s) of this base.
' If this type did have a default property name, don't go any further.
CheckDefaultPropertyAgainstAllBases(baseType, defaultPropertyName, location, diagBag)
End If
End If
End Sub
''' <summary>
''' Returns true if at least one of the elements of this list needs to be injected into a
''' constructor because it's not a const or it is a const and it's type is either decimal
''' or date. Non const fields always require a constructor, so this function should be called to
''' determine if a synthesized constructor is needed that is not listed in members list.
''' </summary>
Friend Function AnyInitializerToBeInjectedIntoConstructor(
initializerSet As IEnumerable(Of ImmutableArray(Of FieldOrPropertyInitializer)),
includingNonMetadataConstants As Boolean
) As Boolean
If initializerSet IsNot Nothing Then
For Each initializers In initializerSet
For Each initializer In initializers
Dim fieldOrPropertyArray As ImmutableArray(Of Symbol) = initializer.FieldsOrProperties
If Not fieldOrPropertyArray.IsDefault Then
Debug.Assert(fieldOrPropertyArray.Length > 0)
Dim fieldOrProperty As Symbol = fieldOrPropertyArray.First
If fieldOrProperty.Kind = SymbolKind.Property Then
' All properties require initializers to be injected
Return True
Else
Dim fieldSymbol = DirectCast(fieldOrProperty, FieldSymbol)
If Not fieldSymbol.IsConst OrElse includingNonMetadataConstants AndAlso fieldSymbol.IsConstButNotMetadataConstant Then
Return True
End If
End If
End If
Next
Next
End If
Return False
End Function
''' <summary>
''' Performs a check for overloads/overrides/shadows conflicts, generates diagnostics.
''' </summary>
''' <param name="membersAndInitializers"></param>
''' <param name="diagBag"></param>
''' <remarks></remarks>
Private Sub CheckForOverloadOverridesShadowsClashesInSameType(membersAndInitializers As MembersAndInitializers, diagBag As BindingDiagnosticBag)
For Each member In membersAndInitializers.Members
' list may contain both properties and methods
Dim checkProperties As Boolean = True
Dim checkMethods As Boolean = True
' result
Dim explicitlyShadows As Boolean = False
Dim explicitlyOverloads As Boolean = False
' symbol flags
Dim shadowsExplicitly As Boolean
Dim overloadsExplicitly As Boolean
Dim overridesExplicitly As Boolean
For Each symbol In member.Value
' only ordinary methods
Select Case symbol.Kind
Case SymbolKind.Method
If Not checkMethods Then
Continue For
End If
' skip properties from with this name if any
checkProperties = False
Case SymbolKind.Property
If Not checkProperties Then
Continue For
End If
' skip methods from with this name if any
checkMethods = False
Case Else
' other kind of member cancels the analysis
explicitlyShadows = False
explicitlyOverloads = False
Exit For
End Select
' initialize symbol flags
If (GetExplicitSymbolFlags(symbol, shadowsExplicitly, overloadsExplicitly, overridesExplicitly)) Then
If shadowsExplicitly Then
' if the method/property shadows explicitly the rest of the methods may be skipped
explicitlyShadows = True
Exit For
ElseIf overloadsExplicitly OrElse overridesExplicitly Then
explicitlyOverloads = True
' continue search
End If
End If
Next
' skip the whole name
If explicitlyShadows OrElse explicitlyOverloads Then
' all symbols are SourceMethodSymbol
For Each symbol In member.Value
If (symbol.Kind = SymbolKind.Method AndAlso checkMethods) OrElse (symbol.IsPropertyAndNotWithEvents AndAlso checkProperties) Then
' initialize symbol flags
If (GetExplicitSymbolFlags(symbol, shadowsExplicitly, overloadsExplicitly, overridesExplicitly)) Then
If explicitlyShadows Then
If Not shadowsExplicitly Then
Debug.Assert(symbol.Locations.Length > 0)
diagBag.Add(ERRID.ERR_MustShadow2, symbol.Locations(0), symbol.GetKindText(), symbol.Name)
End If
ElseIf explicitlyOverloads Then
If Not overridesExplicitly AndAlso Not overloadsExplicitly Then
Debug.Assert(symbol.Locations.Length > 0)
diagBag.Add(ERRID.ERR_MustBeOverloads2, symbol.Locations(0), symbol.GetKindText(), symbol.Name)
End If
End If
End If
End If
Next
End If
Next
End Sub
Private Function GetExplicitSymbolFlags(symbol As Symbol, ByRef shadowsExplicitly As Boolean, ByRef overloadsExplicitly As Boolean, ByRef overridesExplicitly As Boolean) As Boolean
Select Case symbol.Kind
Case SymbolKind.Method
Dim sourceMethodSymbol As SourceMethodSymbol = TryCast(symbol, SourceMethodSymbol)
If (sourceMethodSymbol Is Nothing) Then
Return False
End If
shadowsExplicitly = sourceMethodSymbol.ShadowsExplicitly
overloadsExplicitly = sourceMethodSymbol.OverloadsExplicitly
overridesExplicitly = sourceMethodSymbol.OverridesExplicitly
Return sourceMethodSymbol.MethodKind = MethodKind.Ordinary OrElse sourceMethodSymbol.MethodKind = MethodKind.DeclareMethod
Case SymbolKind.Property
Dim sourcePropertySymbol As SourcePropertySymbol = TryCast(symbol, SourcePropertySymbol)
If (sourcePropertySymbol Is Nothing) Then
Return False
End If
shadowsExplicitly = sourcePropertySymbol.ShadowsExplicitly
overloadsExplicitly = sourcePropertySymbol.OverloadsExplicitly
overridesExplicitly = sourcePropertySymbol.OverridesExplicitly
Return True
Case Else
Throw ExceptionUtilities.UnexpectedValue(symbol.Kind)
End Select
End Function
' Declare all the non-type members and put them in a list.
Private Function BuildNonTypeMembers(diagnostics As BindingDiagnosticBag) As MembersAndInitializers
Dim membersBuilder As New MembersAndInitializersBuilder()
AddDeclaredNonTypeMembers(membersBuilder, diagnostics)
' Add the default constructor, if needed.
AddDefaultConstructorIfNeeded(membersBuilder, False, membersBuilder.InstanceInitializers, diagnostics)
' If there is a shared field, a shared constructor must be synthesized and added to the member list.
' Const fields of type Date or Decimal also require a synthesized shared constructor, but there was a decision
' to not add this to the member list in this case.
AddDefaultConstructorIfNeeded(membersBuilder, True, membersBuilder.StaticInitializers, diagnostics)
' If there are any "Handles" methods, optimistically create methods/ctors that would be hosting
' hookup code.
AddWithEventsHookupConstructorsIfNeeded(membersBuilder, diagnostics)
' Add "Group Class" members.
AddGroupClassMembersIfNeeded(membersBuilder, diagnostics)
' Add synthetic Main method, if needed.
AddEntryPointIfNeeded(membersBuilder)
CheckMemberDiagnostics(membersBuilder, diagnostics)
Dim membersAndInitializers = membersBuilder.ToReadOnlyAndFree()
' Check for overloads, overrides, shadows and implicit shadows clashes
CheckForOverloadOverridesShadowsClashesInSameType(membersAndInitializers, diagnostics)
Return membersAndInitializers
End Function
Protected Overridable Sub AddEntryPointIfNeeded(membersBuilder As MembersAndInitializersBuilder)
End Sub
Protected MustOverride Sub AddDeclaredNonTypeMembers(membersBuilder As MembersAndInitializersBuilder, diagnostics As BindingDiagnosticBag)
Protected Overridable Sub AddGroupClassMembersIfNeeded(membersBuilder As MembersAndInitializersBuilder, diagnostics As BindingDiagnosticBag)
End Sub
' Create symbol(s) for member syntax and add them to the member list
Protected Sub AddMember(memberSyntax As StatementSyntax,
binder As Binder,
diagBag As BindingDiagnosticBag,
members As MembersAndInitializersBuilder,
ByRef staticInitializers As ArrayBuilder(Of FieldOrPropertyInitializer),
ByRef instanceInitializers As ArrayBuilder(Of FieldOrPropertyInitializer),
reportAsInvalid As Boolean)
Debug.Assert(diagBag.AccumulatesDiagnostics)
' Partial methods are implemented by a postpass that matches up the declaration with the implementation.
' Here we treat them as independent methods.
Select Case memberSyntax.Kind
Case SyntaxKind.FieldDeclaration
Dim fieldDecl = DirectCast(memberSyntax, FieldDeclarationSyntax)
If reportAsInvalid Then
diagBag.Add(ERRID.ERR_InvalidInNamespace, fieldDecl.GetLocation())
End If
' Declare all variables that a declared by this syntax, and add them to the list.
SourceMemberFieldSymbol.Create(Me, fieldDecl, binder, members, staticInitializers, instanceInitializers, diagBag)
Case _
SyntaxKind.SubBlock,
SyntaxKind.FunctionBlock,
SyntaxKind.ConstructorBlock,
SyntaxKind.OperatorBlock
Dim methodDecl = DirectCast(memberSyntax, MethodBlockBaseSyntax).BlockStatement
If reportAsInvalid Then
diagBag.Add(ERRID.ERR_InvalidInNamespace, methodDecl.GetLocation())
End If
Dim methodSymbol = CreateMethodMember(methodDecl, binder, diagBag.DiagnosticBag)
If methodSymbol IsNot Nothing Then
AddMember(methodSymbol, binder, members, omitDiagnostics:=False)
End If
Case _
SyntaxKind.SubStatement,
SyntaxKind.FunctionStatement,
SyntaxKind.SubNewStatement,
SyntaxKind.DeclareSubStatement,
SyntaxKind.DeclareFunctionStatement,
SyntaxKind.OperatorStatement
Dim methodDecl = DirectCast(memberSyntax, MethodBaseSyntax)
If reportAsInvalid Then
diagBag.Add(ERRID.ERR_InvalidInNamespace, methodDecl.GetLocation())
End If
Dim methodSymbol = CreateMethodMember(DirectCast(memberSyntax, MethodBaseSyntax), binder, diagBag.DiagnosticBag)
If methodSymbol IsNot Nothing Then
AddMember(methodSymbol, binder, members, omitDiagnostics:=False)
End If
Case SyntaxKind.PropertyBlock
Dim propertyDecl = DirectCast(memberSyntax, PropertyBlockSyntax)
If reportAsInvalid Then
diagBag.Add(ERRID.ERR_InvalidInNamespace, propertyDecl.PropertyStatement.GetLocation())
End If
CreateProperty(propertyDecl.PropertyStatement, propertyDecl, binder, diagBag.DiagnosticBag, members, staticInitializers, instanceInitializers)
Case SyntaxKind.PropertyStatement
Dim propertyDecl = DirectCast(memberSyntax, PropertyStatementSyntax)
If reportAsInvalid Then
diagBag.Add(ERRID.ERR_InvalidInNamespace, propertyDecl.GetLocation())
End If
CreateProperty(propertyDecl, Nothing, binder, diagBag.DiagnosticBag, members, staticInitializers, instanceInitializers)
Case SyntaxKind.LabelStatement
' TODO (tomat): should be added to the initializers
Exit Select
Case SyntaxKind.EventStatement
Dim eventDecl = DirectCast(memberSyntax, EventStatementSyntax)
CreateEvent(eventDecl, Nothing, binder, diagBag.DiagnosticBag, members)
Case SyntaxKind.EventBlock
Dim eventDecl = DirectCast(memberSyntax, EventBlockSyntax)
CreateEvent(eventDecl.EventStatement, eventDecl, binder, diagBag.DiagnosticBag, members)
Case Else
If memberSyntax.Kind = SyntaxKind.EmptyStatement OrElse TypeOf memberSyntax Is ExecutableStatementSyntax Then
If binder.BindingTopLevelScriptCode Then
Debug.Assert(Not reportAsInvalid)
Dim initializer = Function(precedingInitializersLength As Integer)
Return New FieldOrPropertyInitializer(binder.GetSyntaxReference(memberSyntax), precedingInitializersLength)
End Function
SourceNamedTypeSymbol.AddInitializer(instanceInitializers, initializer, members.InstanceSyntaxLength)
ElseIf reportAsInvalid Then
diagBag.Add(ERRID.ERR_InvalidInNamespace, memberSyntax.GetLocation())
End If
End If
End Select
End Sub
Private Sub CreateProperty(syntax As PropertyStatementSyntax,
blockSyntaxOpt As PropertyBlockSyntax,
binder As Binder,
diagBag As DiagnosticBag,
members As MembersAndInitializersBuilder,
ByRef staticInitializers As ArrayBuilder(Of FieldOrPropertyInitializer),
ByRef instanceInitializers As ArrayBuilder(Of FieldOrPropertyInitializer))
Dim propertySymbol = SourcePropertySymbol.Create(Me, binder, syntax, blockSyntaxOpt, diagBag)
AddPropertyAndAccessors(propertySymbol, binder, members)
' initialization can happen because of a "= value" (InitializerOpt) or a "As New Type(...)" (AsClauseOpt)
Dim initializerOpt = syntax.Initializer
Dim asClauseOpt = syntax.AsClause
Dim equalsValueOrAsNewSyntax As VisualBasicSyntaxNode
If asClauseOpt IsNot Nothing AndAlso asClauseOpt.Kind = SyntaxKind.AsNewClause Then
equalsValueOrAsNewSyntax = asClauseOpt
Else
equalsValueOrAsNewSyntax = initializerOpt
End If
If equalsValueOrAsNewSyntax IsNot Nothing Then
Dim initializerOptRef = binder.GetSyntaxReference(equalsValueOrAsNewSyntax)
Dim initializer = Function(precedingInitializersLength As Integer)
Return New FieldOrPropertyInitializer(propertySymbol, initializerOptRef, precedingInitializersLength)
End Function
If propertySymbol.IsShared Then
AddInitializer(staticInitializers, initializer, members.StaticSyntaxLength)
Else
' auto implemented properties inside of structures can only have an initialization value
' if they are shared.
If propertySymbol.IsAutoProperty AndAlso
propertySymbol.ContainingType.TypeKind = TypeKind.Structure Then
Binder.ReportDiagnostic(diagBag, syntax.Identifier, ERRID.ERR_AutoPropertyInitializedInStructure)
End If
AddInitializer(instanceInitializers, initializer, members.InstanceSyntaxLength)
End If
End If
End Sub
Private Sub CreateEvent(syntax As EventStatementSyntax,
blockSyntaxOpt As EventBlockSyntax,
binder As Binder,
diagBag As DiagnosticBag,
members As MembersAndInitializersBuilder)
Dim propertySymbol = New SourceEventSymbol(Me, binder, syntax, blockSyntaxOpt, diagBag)
AddEventAndAccessors(propertySymbol, binder, members)
End Sub
Private Function CreateMethodMember(methodBaseSyntax As MethodBaseSyntax,
binder As Binder,
diagBag As DiagnosticBag) As SourceMethodSymbol
Select Case methodBaseSyntax.Kind
Case SyntaxKind.SubStatement, SyntaxKind.FunctionStatement
Return SourceMethodSymbol.CreateRegularMethod(Me, DirectCast(methodBaseSyntax, MethodStatementSyntax), binder, diagBag)
Case SyntaxKind.SubNewStatement
Return SourceMethodSymbol.CreateConstructor(Me, DirectCast(methodBaseSyntax, SubNewStatementSyntax), binder, diagBag)
Case SyntaxKind.OperatorStatement
Return SourceMethodSymbol.CreateOperator(Me, DirectCast(methodBaseSyntax, OperatorStatementSyntax), binder, diagBag)
Case SyntaxKind.DeclareSubStatement, SyntaxKind.DeclareFunctionStatement
Return SourceMethodSymbol.CreateDeclareMethod(Me, DirectCast(methodBaseSyntax, DeclareStatementSyntax), binder, diagBag)
Case Else
Throw ExceptionUtilities.UnexpectedValue(methodBaseSyntax.Kind)
End Select
End Function
''' <summary>
''' Check to see if we need a default instance|shared constructor, and if so, create it.
'''
''' NOTE: we only need a shared constructor if there are any initializers to be
''' injected into it, we don't create a constructor otherwise. In this case we also
''' ignore const fields which will still require to be injected, because in this case
''' we don't see the constructor to be visible in symbol table.
''' </summary>
Private Sub AddDefaultConstructorIfNeeded(members As MembersAndInitializersBuilder,
isShared As Boolean,
initializers As ArrayBuilder(Of ImmutableArray(Of FieldOrPropertyInitializer)),
diagnostics As BindingDiagnosticBag)
If TypeKind = TypeKind.Submission Then
' Only add a constructor if it is not shared OR if there are shared initializers
If Not isShared OrElse Me.AnyInitializerToBeInjectedIntoConstructor(initializers, False) Then
' a submission can only have a single declaration:
Dim syntaxRef = SyntaxReferences.Single()
Dim binder As Binder = BinderBuilder.CreateBinderForType(m_containingModule, syntaxRef.SyntaxTree, Me)
Dim constructor As New SynthesizedSubmissionConstructorSymbol(syntaxRef, Me, isShared, binder, diagnostics)
AddMember(constructor, binder, members, omitDiagnostics:=False)
End If
ElseIf TypeKind = TypeKind.Class OrElse
TypeKind = TypeKind.Structure OrElse
TypeKind = TypeKind.Enum OrElse
(TypeKind = TypeKind.Module AndAlso isShared) Then
' Do we need to create a constructor? We need a constructor if this is
' an instance constructor, or if this is a shared constructor and
' there is at least one non-constant initializers
Dim anyInitializersToInject As Boolean =
Me.AnyInitializerToBeInjectedIntoConstructor(initializers, Not isShared)
' NOTE: for shared constructor we DO NOT check for non-metadata-const
' initializers, those will be addressed later
If isShared AndAlso Not anyInitializersToInject Then
Return
End If
Dim isDebuggable As Boolean = anyInitializersToInject
EnsureCtor(members, isShared, isDebuggable, diagnostics)
End If
If Not isShared AndAlso IsScriptClass Then
' a submission can only have a single declaration:
Dim syntaxRef = SyntaxReferences.Single()
Dim scriptInitializer = New SynthesizedInteractiveInitializerMethod(syntaxRef, Me, diagnostics)
AddSymbolToMembers(scriptInitializer, members.Members)
Dim scriptEntryPoint = SynthesizedEntryPointSymbol.Create(scriptInitializer, diagnostics)
AddSymbolToMembers(scriptEntryPoint, members.Members)
End If
End Sub
Private Sub EnsureCtor(members As MembersAndInitializersBuilder, isShared As Boolean, isDebuggable As Boolean, diagBag As BindingDiagnosticBag)
Dim constructorName = If(isShared, WellKnownMemberNames.StaticConstructorName, WellKnownMemberNames.InstanceConstructorName)
' Check to see if we have already declared an instance|shared constructor.
Dim symbols As ArrayBuilder(Of Symbol) = Nothing
If members.Members.TryGetValue(constructorName, symbols) Then
Debug.Assert(symbols.Where(Function(sym) sym.Kind = SymbolKind.Method AndAlso
(DirectCast(sym, MethodSymbol).MethodKind = MethodKind.Constructor OrElse
DirectCast(sym, MethodSymbol).MethodKind = MethodKind.SharedConstructor)
).Any)
For Each method As MethodSymbol In symbols
If method.MethodKind = MethodKind.Constructor AndAlso method.ParameterCount = 0 Then
Return ' definitely don't need to synthesize a constructor
End If
Next
' have to synthesize a constructor if this is a non-shared struct
If TypeKind <> TypeKind.Structure OrElse isShared Then
Return ' already have an instance|shared constructor. Don't add another one.
End If
End If
' Add a new instance|shared constructor.
Dim syntaxRef = SyntaxReferences.First() ' use arbitrary part
' TODO: does it need to be deterministic?
Dim binder As Binder = BinderBuilder.CreateBinderForType(m_containingModule, syntaxRef.SyntaxTree, Me)
Dim constructor As New SynthesizedConstructorSymbol(syntaxRef, Me, isShared, isDebuggable, binder, diagBag)
AddMember(constructor, binder, members, omitDiagnostics:=False)
End Sub
Private Sub AddWithEventsHookupConstructorsIfNeeded(members As MembersAndInitializersBuilder, diagBag As BindingDiagnosticBag)
If TypeKind = TypeKind.Submission Then
'TODO: anything to do here?
ElseIf TypeKind = TypeKind.Class OrElse TypeKind = TypeKind.Module Then
' we need a separate list of methods since we may need to modify the members dictionary.
Dim sourceMethodsWithHandles As ArrayBuilder(Of SourceMethodSymbol) = Nothing
For Each membersOfSameName In members.Members.Values
For Each member In membersOfSameName
Dim sourceMethod = TryCast(member, SourceMethodSymbol)
If sourceMethod IsNot Nothing Then
If Not sourceMethod.HandlesEvents Then
Continue For
End If
If sourceMethodsWithHandles Is Nothing Then
sourceMethodsWithHandles = ArrayBuilder(Of SourceMethodSymbol).GetInstance
End If
sourceMethodsWithHandles.Add(sourceMethod)
End If
Next
Next
If sourceMethodsWithHandles Is Nothing Then
' no source methods with Handles - we are done
Return
End If
' binder used if we need to find something in the base. will be created when needed
Dim baseBinder As Binder = Nothing
For Each sourceMethod In sourceMethodsWithHandles
Dim methodStatement = DirectCast(sourceMethod.DeclarationSyntax, MethodStatementSyntax)
For Each handlesClause In methodStatement.HandlesClause.Events
If handlesClause.EventContainer.Kind = SyntaxKind.KeywordEventContainer Then
If Not sourceMethod.IsShared Then
' if the method is not shared, we will be hooking up in the instance ctor
EnsureCtor(members, isShared:=False, isDebuggable:=False, diagBag:=diagBag)
Else
' if both event and handler are shared, then hookup goes into shared ctor
' otherwise into instance ctor
' find our event
Dim eventName = handlesClause.EventMember.Identifier.ValueText
Dim eventSym As EventSymbol = Nothing
' look in current members
If handlesClause.EventContainer.Kind <> SyntaxKind.MyBaseKeyword Then
Dim candidates As ArrayBuilder(Of Symbol) = Nothing
If members.Members.TryGetValue(eventName, candidates) Then
If candidates.Count = 1 AndAlso candidates(0).Kind = SymbolKind.Event Then
eventSym = DirectCast(candidates(0), EventSymbol)
End If
End If
End If
' try find in base
If eventSym Is Nothing Then
' Set up a binder.
baseBinder = If(baseBinder, BinderBuilder.CreateBinderForType(m_containingModule, methodStatement.SyntaxTree, Me))
Dim useSiteInfo As New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, m_containingModule.ContainingAssembly)
eventSym = SourceMemberMethodSymbol.FindEvent(Me.BaseTypeNoUseSiteDiagnostics, baseBinder, eventName, isThroughMyBase:=True, useSiteInfo:=useSiteInfo)
diagBag.Add(handlesClause.EventMember, useSiteInfo)
End If
' still nothing?
If eventSym Is Nothing Then
Continue For
End If
EnsureCtor(members, eventSym.IsShared, isDebuggable:=False, diagBag:=diagBag)
End If
End If
Next
Next
sourceMethodsWithHandles.Free()
End If
End Sub
Private Sub AddPropertyAndAccessors(propertySymbol As SourcePropertySymbol,
binder As Binder,
members As MembersAndInitializersBuilder)
AddMember(propertySymbol, binder, members, omitDiagnostics:=False)
If propertySymbol.GetMethod IsNot Nothing Then
AddMember(propertySymbol.GetMethod, binder, members, omitDiagnostics:=False)
End If
If propertySymbol.SetMethod IsNot Nothing Then
AddMember(propertySymbol.SetMethod, binder, members, omitDiagnostics:=False)
End If
If propertySymbol.AssociatedField IsNot Nothing Then
AddMember(propertySymbol.AssociatedField, binder, members, omitDiagnostics:=False)
End If
End Sub
Private Sub AddEventAndAccessors(eventSymbol As SourceEventSymbol,
binder As Binder,
members As MembersAndInitializersBuilder)
AddMember(eventSymbol, binder, members, omitDiagnostics:=False)
If eventSymbol.AddMethod IsNot Nothing Then
AddMember(eventSymbol.AddMethod, binder, members, omitDiagnostics:=False)
End If
If eventSymbol.RemoveMethod IsNot Nothing Then
AddMember(eventSymbol.RemoveMethod, binder, members, omitDiagnostics:=False)
End If
If eventSymbol.RaiseMethod IsNot Nothing Then
AddMember(eventSymbol.RaiseMethod, binder, members, omitDiagnostics:=False)
End If
If eventSymbol.AssociatedField IsNot Nothing Then
AddMember(eventSymbol.AssociatedField, binder, members, omitDiagnostics:=False)
End If
End Sub
Private Sub CheckMemberDiagnostics(
members As MembersAndInitializersBuilder,
diagBag As BindingDiagnosticBag)
If Me.Locations.Length > 1 AndAlso Not Me.IsPartial Then
' Suppress conflict member diagnostics when the enclosing type is an accidental duplicate
Return
End If
For Each sym As Symbol In members.DeferredMemberDiagnostic
' Check name for duplicate type declarations
' First check if the member name conflicts with a type declaration in the container then
' Check if the member name conflicts with another member in the container.
If Not CheckIfMemberNameConflictsWithTypeMember(sym, members, diagBag) Then
CheckIfMemberNameIsDuplicate(sym, diagBag, members)
End If
If sym.CanBeReferencedByName AndAlso
TypeParameters.MatchesAnyName(sym.Name) Then
If sym.IsImplicitlyDeclared Then
Dim symImplicitlyDefinedBy = sym.ImplicitlyDefinedBy(members.Members)
Debug.Assert(symImplicitlyDefinedBy IsNot Nothing)
' "{0} '{1}' implicitly defines a member '{2}' which has the same name as a type parameter."
Binder.ReportDiagnostic(diagBag,
symImplicitlyDefinedBy.Locations(0),
ERRID.ERR_SyntMemberShadowsGenericParam3,
symImplicitlyDefinedBy.GetKindText(),
symImplicitlyDefinedBy.Name,
sym.Name)
Else
' "'{0}' has the same name as a type parameter."
Binder.ReportDiagnostic(diagBag, sym.Locations(0), ERRID.ERR_ShadowingGenericParamWithMember1, sym.Name)
End If
End If
Next
End Sub
Friend Sub AddMember(sym As Symbol,
binder As Binder,
members As MembersAndInitializersBuilder,
omitDiagnostics As Boolean)
If Not omitDiagnostics Then
members.DeferredMemberDiagnostic.Add(sym)
End If
AddSymbolToMembers(sym, members.Members)
End Sub
Friend Sub AddSymbolToMembers(memberSymbol As Symbol,
members As Dictionary(Of String, ArrayBuilder(Of Symbol)))
Dim symbols As ArrayBuilder(Of Symbol) = Nothing
If members.TryGetValue(memberSymbol.Name, symbols) Then
symbols.Add(memberSymbol)
Else
symbols = New ArrayBuilder(Of Symbol)
symbols.Add(memberSymbol)
members(memberSymbol.Name) = symbols
End If
End Sub
Private Function CheckIfMemberNameConflictsWithTypeMember(sym As Symbol,
members As MembersAndInitializersBuilder,
diagBag As BindingDiagnosticBag) As Boolean
' Check name for conflicts with type members
Dim definedTypes = Me.GetTypeMembers(sym.Name)
If definedTypes.Length > 0 Then
Dim type = definedTypes(0)
If Not Equals(TryCast(sym, TypeSymbol), type, TypeCompareKind.ConsiderEverything) Then
Return CheckIfMemberNameIsDuplicate(sym, type, members, diagBag, includeKind:=True)
End If
End If
Return False
End Function
Private Function CheckIfMemberNameIsDuplicate(sym As Symbol,
diagBag As BindingDiagnosticBag,
members As MembersAndInitializersBuilder) As Boolean
' Check name for duplicate declarations
Dim definedSymbols As ArrayBuilder(Of Symbol) = Nothing
If members.Members.TryGetValue(sym.Name, definedSymbols) Then
Debug.Assert(definedSymbols.Count > 0)
Dim other = definedSymbols(0)
If (sym <> other) Then
Return CheckIfMemberNameIsDuplicate(sym, other, members, diagBag, includeKind:=False)
End If
End If
Return False
End Function
Private Function CheckIfMemberNameIsDuplicate(firstSymbol As Symbol,
secondSymbol As Symbol,
members As MembersAndInitializersBuilder,
diagBag As BindingDiagnosticBag,
includeKind As Boolean) As Boolean
Dim firstAssociatedSymbol = secondSymbol.ImplicitlyDefinedBy(members.Members)
If firstAssociatedSymbol Is Nothing AndAlso secondSymbol.IsUserDefinedOperator() Then
' For the purpose of this check, operator methods are treated as implicitly defined by themselves.
firstAssociatedSymbol = secondSymbol
End If
Dim secondAssociatedSymbol = firstSymbol.ImplicitlyDefinedBy(members.Members)
If secondAssociatedSymbol Is Nothing AndAlso firstSymbol.IsUserDefinedOperator() Then
' For the purpose of this check, operator methods are treated as implicitly defined by themselves.
secondAssociatedSymbol = firstSymbol
End If
If firstAssociatedSymbol IsNot Nothing Then
If secondAssociatedSymbol Is Nothing Then
Dim asType = TryCast(firstAssociatedSymbol, TypeSymbol)
If asType IsNot Nothing AndAlso asType.IsEnumType Then
' enum members may conflict only with __Value and that produces a special diagnostics.
Return True
End If
' "{0} '{1}' implicitly defines '{2}', which conflicts with a member of the same name in {3} '{4}'."
Binder.ReportDiagnostic(
diagBag,
firstAssociatedSymbol.Locations(0),
ERRID.ERR_SynthMemberClashesWithMember5,
firstAssociatedSymbol.GetKindText(),
OverrideHidingHelper.AssociatedSymbolName(firstAssociatedSymbol),
secondSymbol.Name,
Me.GetKindText(),
Me.Name)
Return True
Else
' If both symbols are implicitly defined (say an overloaded property P where each
' overload implicitly defines get_P), no error is reported.
' If there are any errors in cases if defining members have same names.
' In such cases, the errors should be reported on the defining symbols.
If Not CaseInsensitiveComparison.Equals(firstAssociatedSymbol.Name,
secondAssociatedSymbol.Name) Then
'{0} '{1}' implicitly defines '{2}', which conflicts with a member implicitly declared for {3} '{4}' in {5} '{6}'.
Binder.ReportDiagnostic(
diagBag,
firstAssociatedSymbol.Locations(0),
ERRID.ERR_SynthMemberClashesWithSynth7,
firstAssociatedSymbol.GetKindText(),
OverrideHidingHelper.AssociatedSymbolName(firstAssociatedSymbol),
secondSymbol.Name,
secondAssociatedSymbol.GetKindText(),
OverrideHidingHelper.AssociatedSymbolName(secondAssociatedSymbol),
Me.GetKindText(),
Me.Name)
End If
End If
ElseIf secondAssociatedSymbol IsNot Nothing Then
' "{0} '{1}' conflicts with a member implicitly declared for {2} '{3}' in {4} '{5}'."
Binder.ReportDiagnostic(
diagBag,
secondSymbol.Locations(0),
ERRID.ERR_MemberClashesWithSynth6,
secondSymbol.GetKindText(),
secondSymbol.Name,
secondAssociatedSymbol.GetKindText(),
OverrideHidingHelper.AssociatedSymbolName(secondAssociatedSymbol),
Me.GetKindText(),
Me.Name)
Return True
ElseIf ((firstSymbol.Kind <> SymbolKind.Method) AndAlso (Not firstSymbol.IsPropertyAndNotWithEvents)) OrElse
(firstSymbol.Kind <> secondSymbol.Kind) Then
If Me.IsEnumType() Then
' For Enum members, give more specific simpler errors.
' "'{0}' is already declared in this {1}."
Binder.ReportDiagnostic(
diagBag,
firstSymbol.Locations(0),
ERRID.ERR_MultiplyDefinedEnumMember2,
firstSymbol.Name,
Me.GetKindText())
Else
' the formatting of this error message is quite special and needs special treatment
' e.g. 'goo' is already declared as 'Class Goo' in this class.
' "'{0}' is already declared as '{1}' in this {2}."
Binder.ReportDiagnostic(
diagBag,
firstSymbol.Locations(0),
ERRID.ERR_MultiplyDefinedType3,
firstSymbol.Name,
If(includeKind,
DirectCast(CustomSymbolDisplayFormatter.ErrorNameWithKind(secondSymbol), Object),
secondSymbol),
Me.GetKindText())
End If
Return True
End If
Return False
End Function
Public Overrides ReadOnly Property MemberNames As IEnumerable(Of String)
Get
Return _declaration.MemberNames
End Get
End Property
Friend Overrides Function GetMembersUnordered() As ImmutableArray(Of Symbol)
If _lazyMembersFlattened.IsDefault Then
Dim lookup = Me.MemberAndInitializerLookup
Dim result = lookup.Members.Flatten(Nothing) ' Do Not sort right now.
ImmutableInterlocked.InterlockedInitialize(Me._lazyMembersFlattened, result)
End If
Return _lazyMembersFlattened.ConditionallyDeOrder()
End Function
Public Overloads Overrides Function GetMembers() As ImmutableArray(Of Symbol)
If (m_lazyState And StateFlags.FlattenedMembersIsSortedMask) <> 0 Then
Return _lazyMembersFlattened
Else
Dim allMembers = Me.GetMembersUnordered()
If allMembers.Length >= 2 Then
allMembers = allMembers.Sort(LexicalOrderSymbolComparer.Instance)
ImmutableInterlocked.InterlockedExchange(_lazyMembersFlattened, allMembers)
End If
ThreadSafeFlagOperations.Set(m_lazyState, StateFlags.FlattenedMembersIsSortedMask)
Return allMembers
End If
End Function
Public Overloads Overrides Function GetMembers(name As String) As ImmutableArray(Of Symbol)
Dim lookup = Me.MemberAndInitializerLookup
Dim members As ImmutableArray(Of Symbol) = Nothing
If lookup.Members.TryGetValue(name, members) Then
Return members
End If
Return ImmutableArray(Of Symbol).Empty
End Function
Friend Overrides Function GetSimpleNonTypeMembers(name As String) As ImmutableArray(Of Symbol)
If _lazyMembersAndInitializers IsNot Nothing OrElse MemberNames.Contains(name, IdentifierComparison.Comparer) Then
Return GetMembers(name)
End If
Return ImmutableArray(Of Symbol).Empty
End Function
''' <summary>
''' In case the passed initializers require a shared constructor, this method returns a new MethodSymbol instance for the
''' shared constructor if there is not already an explicit shared constructor
''' </summary>
Friend Function CreateSharedConstructorsForConstFieldsIfRequired(binder As Binder, diagnostics As BindingDiagnosticBag) As MethodSymbol
Dim lookup = Me.MemberAndInitializerLookup
Dim staticInitializers = lookup.StaticInitializers
If Not staticInitializers.IsDefaultOrEmpty Then
Dim symbols As ImmutableArray(Of Symbol) = Nothing
If Not MemberAndInitializerLookup.Members.TryGetValue(WellKnownMemberNames.StaticConstructorName, symbols) Then
' call AnyInitializerToBeInjectedIntoConstructor if only there is no static constructor
If Me.AnyInitializerToBeInjectedIntoConstructor(staticInitializers, True) Then
Dim syntaxRef = SyntaxReferences.First() ' use arbitrary part
Return New SynthesizedConstructorSymbol(syntaxRef, Me,
isShared:=True, isDebuggable:=True,
binder:=binder, diagnostics:=diagnostics)
End If
End If
End If
Return Nothing
End Function
Friend Overrides Iterator Function GetFieldsToEmit() As IEnumerable(Of FieldSymbol)
For Each member In GetMembersForCci()
If member.Kind = SymbolKind.Field Then
Yield DirectCast(member, FieldSymbol)
End If
Next
End Function
''' <summary>
''' Gets the static initializers.
''' </summary>
Public ReadOnly Property StaticInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer))
Get
Return Me.MemberAndInitializerLookup.StaticInitializers
End Get
End Property
''' <summary>
''' Gets the instance initializers.
''' </summary>
Public ReadOnly Property InstanceInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer))
Get
Return Me.MemberAndInitializerLookup.InstanceInitializers
End Get
End Property
Friend Function CalculateSyntaxOffsetInSynthesizedConstructor(position As Integer, tree As SyntaxTree, isShared As Boolean) As Integer
If IsScriptClass AndAlso Not isShared Then
Dim aggregateLength As Integer = 0
For Each declaration In Me._declaration.Declarations
Dim syntaxRef = declaration.SyntaxReference
If tree Is syntaxRef.SyntaxTree Then
Return aggregateLength + position
End If
aggregateLength += syntaxRef.Span.Length
Next
' This point should not be reachable.
Throw ExceptionUtilities.Unreachable
End If
Dim syntaxOffset As Integer
If TryCalculateSyntaxOffsetOfPositionInInitializer(position, tree, isShared, syntaxOffset:=syntaxOffset) Then
Return syntaxOffset
End If
If Me._declaration.Declarations.Length >= 1 AndAlso position = Me._declaration.Declarations(0).Location.SourceSpan.Start Then
' With dynamic analysis instrumentation, the introducing declaration of a type can provide
' the syntax associated with both the analysis payload local of a synthesized constructor
' and with the constructor itself. If the synthesized constructor includes an initializer with a lambda,
' that lambda needs a closure that captures the analysis payload of the constructor,
' and the offset of the syntax for the local within the constructor is by definition zero.
Return 0
End If
' This point should not be reachable. An implicit constructor has no body and no initializer,
' so the variable has to be declared in a member initializer.
Throw ExceptionUtilities.Unreachable
End Function
' Calculates a syntax offset of a syntax position that is contained in a property or field initializer (if it is in fact contained in one).
Friend Function TryCalculateSyntaxOffsetOfPositionInInitializer(position As Integer, tree As SyntaxTree, isShared As Boolean, ByRef syntaxOffset As Integer) As Boolean
Dim membersAndInitializers = GetMembersAndInitializers()
Dim allInitializers = If(isShared, membersAndInitializers.StaticInitializers, membersAndInitializers.InstanceInitializers)
Dim siblingInitializers = GetInitializersInSourceTree(tree, allInitializers)
Dim index = IndexOfInitializerContainingPosition(siblingInitializers, position)
If index < 0 Then
syntaxOffset = 0
Return False
End If
' |<-----------distanceFromCtorBody---------->|
' [ initializer 0 ][ initializer 1 ][ initializer 2 ][ initializer 3 ][ctor body]
' |<--preceding init len-->| ^
' position
Dim initializersLength = If(isShared, membersAndInitializers.StaticInitializersSyntaxLength, membersAndInitializers.InstanceInitializersSyntaxLength)
Dim distanceFromInitializerStart = position - siblingInitializers(index).Syntax.Span.Start
Dim distanceFromCtorBody = initializersLength - (siblingInitializers(index).PrecedingInitializersLength + distanceFromInitializerStart)
Debug.Assert(distanceFromCtorBody > 0)
' syntax offset 0 is at the start of the ctor body:
syntaxOffset = -distanceFromCtorBody
Return True
End Function
Private Shared Function GetInitializersInSourceTree(tree As SyntaxTree, initializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer))) As ImmutableArray(Of FieldOrPropertyInitializer)
Dim builder = ArrayBuilder(Of FieldOrPropertyInitializer).GetInstance()
For Each siblingInitializers As ImmutableArray(Of FieldOrPropertyInitializer) In initializers
If (siblingInitializers.First().Syntax.SyntaxTree Is tree) Then
builder.AddRange(siblingInitializers)
End If
Next
Return builder.ToImmutableAndFree()
End Function
Private Shared Function IndexOfInitializerContainingPosition(initializers As ImmutableArray(Of FieldOrPropertyInitializer), position As Integer) As Integer
' Search for the start of the span (the spans are non-overlapping and sorted)
Dim index = initializers.BinarySearch(position, Function(initializer, pos) initializer.Syntax.Span.Start.CompareTo(pos))
' Binary search returns non-negative result if the position is exactly the start of some span.
If index >= 0 Then
Return index
End If
' Otherwise, "Not index" is the closest span whose start is greater than the position.
' Make sure that this closest span contains the position.
index = (Not index) - 1
If index >= 0 AndAlso initializers(index).Syntax.Span.Contains(position) Then
Return index
End If
Return -1
End Function
Public Overrides ReadOnly Property MightContainExtensionMethods As Boolean
Get
' Only Modules can declare extension methods.
If _lazyContainsExtensionMethods = ThreeState.Unknown Then
If Not (_containingSymbol.Kind = SymbolKind.Namespace AndAlso Me.AllowsExtensionMethods() AndAlso Me.AnyMemberHasAttributes) Then
_lazyContainsExtensionMethods = ThreeState.False
End If
End If
Return _lazyContainsExtensionMethods <> ThreeState.False
End Get
End Property
Friend Overrides Sub BuildExtensionMethodsMap(map As Dictionary(Of String, ArrayBuilder(Of MethodSymbol)),
appendThrough As NamespaceSymbol)
If Me.MightContainExtensionMethods Then
Dim lookup = Me.MemberAndInitializerLookup
If Not appendThrough.BuildExtensionMethodsMap(map, lookup.Members) Then
' Didn't find any extension methods, record the fact.
_lazyContainsExtensionMethods = ThreeState.False
End If
End If
End Sub
Friend Overrides Sub AddExtensionMethodLookupSymbolsInfo(nameSet As LookupSymbolsInfo,
options As LookupOptions,
originalBinder As Binder,
appendThrough As NamedTypeSymbol)
If Me.MightContainExtensionMethods Then
Dim lookup = Me.MemberAndInitializerLookup
If Not appendThrough.AddExtensionMethodLookupSymbolsInfo(nameSet, options, originalBinder, lookup.Members) Then
' Didn't find any extension methods, record the fact.
_lazyContainsExtensionMethods = ThreeState.False
End If
End If
End Sub
' Build the explicit interface map for this type.
'
' Also diagnoses the following errors and places diagnostics in the diagnostic bag:
' Same symbol implemented twice
' Interface symbol that should have been implemented wasn't.
' Generic interfaces might unify.
Private Function MakeExplicitInterfaceImplementationMap(diagnostics As BindingDiagnosticBag) As MultiDictionary(Of Symbol, Symbol)
If Me.IsClassType() OrElse Me.IsStructureType() OrElse Me.IsInterfaceType() Then
CheckInterfaceUnificationAndVariance(diagnostics)
End If
If Me.IsClassType() OrElse Me.IsStructureType() Then
' Go through all explicit interface implementations and record them.
Dim map = New MultiDictionary(Of Symbol, Symbol)(ExplicitInterfaceImplementationTargetMemberEqualityComparer.Instance)
For Each implementingMember In Me.GetMembers()
For Each interfaceMember In GetExplicitInterfaceImplementations(implementingMember)
If ShouldReportImplementationError(interfaceMember) AndAlso map.ContainsKey(interfaceMember) Then
'the same symbol was implemented twice.
Dim diag = ErrorFactory.ErrorInfo(ERRID.ERR_MethodAlreadyImplemented2,
CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(interfaceMember.ContainingType),
CustomSymbolDisplayFormatter.ShortErrorName(interfaceMember))
diagnostics.Add(New VBDiagnostic(diag, GetImplementingLocation(implementingMember, interfaceMember)))
End If
map.Add(interfaceMember, implementingMember)
Next
Next
' Check to make sure all members of interfaces were implemented. Note that if our base class implemented
' an interface, we do not have to implemented those members again (although we can).
For Each ifaceSet In InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.Values
For Each iface In ifaceSet
' Only check interfaces that our base type does NOT implement.
If Not Me.BaseTypeNoUseSiteDiagnostics.ImplementsInterface(iface, comparer:=Nothing, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded) Then
For Each ifaceMember In iface.GetMembers()
If ifaceMember.RequiresImplementation() Then
Dim implementingSet As MultiDictionary(Of Symbol, Symbol).ValueSet = map(ifaceMember)
Dim useSiteInfo = ifaceMember.GetUseSiteInfo()
If ShouldReportImplementationError(ifaceMember) Then
If implementingSet.Count = 0 Then
'member was not implemented.
Dim diag = If(useSiteInfo.DiagnosticInfo, ErrorFactory.ErrorInfo(If(ifaceMember.IsShared, ERRID.ERR_UnimplementedSharedMember, ERRID.ERR_UnimplementedMember3),
If(Me.IsStructureType(), "Structure", "Class"),
CustomSymbolDisplayFormatter.ShortErrorName(Me),
ifaceMember,
CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(iface)))
diagnostics.Add(New VBDiagnostic(diag, GetImplementsLocation(iface)))
ElseIf implementingSet.Count = 1 Then ' Otherwise, a duplicate implementation error is reported above
diagnostics.Add(useSiteInfo, implementingSet.Single.Locations(0))
End If
ElseIf implementingSet.Count = 1 Then
diagnostics.Add(useSiteInfo, implementingSet.Single.Locations(0))
End If
End If
Next
End If
Next
Next
If map.Count > 0 Then
Return map
Else
Return EmptyExplicitImplementationMap ' Better to use singleton and garbage collection the empty dictionary we just created.
End If
Else
Return EmptyExplicitImplementationMap
End If
End Function
' Should we report implementation errors for this member?
' We don't report errors on accessors, because we already report the errors on their containing property/event.
Private Function ShouldReportImplementationError(interfaceMember As Symbol) As Boolean
Dim method = TryCast(interfaceMember, MethodSymbol)
If method IsNot Nothing AndAlso
method.MethodKind <> MethodKind.Ordinary AndAlso
method.MethodKind <> MethodKind.UserDefinedOperator AndAlso
method.MethodKind <> MethodKind.Conversion Then
Return False
End If
Return True
End Function
''' <summary>
''' Get a dictionary with all the explicitly implemented interface symbols declared on this type.
'''
''' key = interface method/property/event compared using <see cref="ExplicitInterfaceImplementationTargetMemberEqualityComparer"/>,
''' value = explicitly implementing methods/properties/events declared on this type (normally a single value, multiple in case of
''' an error).
'''
''' Getting this property also ensures that diagnostics relating to interface implementation, overriding, hiding and
''' overloading are created.
''' </summary>
''' <returns></returns>
Friend Overrides ReadOnly Property ExplicitInterfaceImplementationMap As MultiDictionary(Of Symbol, Symbol)
Get
If m_lazyExplicitInterfaceImplementationMap Is Nothing Then
Dim diagnostics = BindingDiagnosticBag.GetInstance()
Dim implementationMap = MakeExplicitInterfaceImplementationMap(diagnostics)
OverrideHidingHelper.CheckHidingAndOverridingForType(Me, diagnostics)
' checking the overloads accesses the parameter types. The parameters are now lazily created to not happen at
' method/property symbol creation time. That's the reason why this check is delayed until here.
CheckForOverloadsErrors(diagnostics)
m_containingModule.AtomicStoreReferenceAndDiagnostics(m_lazyExplicitInterfaceImplementationMap, implementationMap, diagnostics)
diagnostics.Free()
End If
Return m_lazyExplicitInterfaceImplementationMap
End Get
End Property
''' <summary>
''' Reports the overloads error for this type.
''' </summary>
''' <param name="diagnostics">The diagnostics.</param>
Private Sub CheckForOverloadsErrors(diagnostics As BindingDiagnosticBag)
Debug.Assert(Me.IsDefinition) ' Don't do this on constructed types
' Enums and Delegates have nothing to do.
Dim myTypeKind As TypeKind = Me.TypeKind
Dim operatorsKnownToHavePair As HashSet(Of MethodSymbol) = Nothing
If myTypeKind = TypeKind.Class OrElse myTypeKind = TypeKind.Interface OrElse myTypeKind = TypeKind.Structure OrElse myTypeKind = TypeKind.Module Then
Dim lookup = MemberAndInitializerLookup
Dim structEnumerator As Dictionary(Of String, ImmutableArray(Of Symbol)).Enumerator = lookup.Members.GetEnumerator
Dim canDeclareOperators As Boolean = (myTypeKind <> TypeKind.Module AndAlso myTypeKind <> TypeKind.Interface)
While structEnumerator.MoveNext()
Dim memberList As ImmutableArray(Of Symbol) = structEnumerator.Current.Value
' only proceed if there are multiple declarations of methods with the same name
If memberList.Length < 2 Then
' Validate operator overloading
If canDeclareOperators AndAlso CheckForOperatorOverloadingErrors(memberList, 0, structEnumerator, operatorsKnownToHavePair, diagnostics) Then
Continue While
End If
Continue While
End If
For Each memberKind In {SymbolKind.Method, SymbolKind.Property}
For memberIndex = 0 To memberList.Length - 2
Dim member As Symbol = memberList(memberIndex)
If member.Kind <> memberKind OrElse member.IsAccessor OrElse member.IsWithEventsProperty Then
Continue For
End If
' Validate operator overloading
If canDeclareOperators AndAlso memberKind = SymbolKind.Method AndAlso
CheckForOperatorOverloadingErrors(memberList, memberIndex, structEnumerator, operatorsKnownToHavePair, diagnostics) Then
Continue For
End If
Dim sourceMethod = TryCast(member, SourceMemberMethodSymbol)
If sourceMethod IsNot Nothing Then
Debug.Assert(Not sourceMethod.IsUserDefinedOperator())
If sourceMethod.IsUserDefinedOperator() Then
Continue For
End If
If sourceMethod.SuppressDuplicateProcDefDiagnostics Then
Continue For
End If
End If
' TODO: this is O(N^2), maybe this can be improved.
For nextMemberIndex = memberIndex + 1 To memberList.Length - 1
Dim nextMember = memberList(nextMemberIndex)
If nextMember.Kind <> memberKind OrElse nextMember.IsAccessor OrElse nextMember.IsWithEventsProperty Then
Continue For
End If
sourceMethod = TryCast(nextMember, SourceMemberMethodSymbol)
If sourceMethod IsNot Nothing Then
If sourceMethod.IsUserDefinedOperator() Then
Continue For
End If
If sourceMethod.SuppressDuplicateProcDefDiagnostics Then
Continue For
End If
End If
' only process non synthesized symbols
If Not member.IsImplicitlyDeclared AndAlso
Not nextMember.IsImplicitlyDeclared Then
' Overload resolution (CollectOverloadedCandidates) does similar check for imported members
' from the same container. Both places should be in sync. CheckForOperatorOverloadingErrors too.
Dim comparisonResults As SymbolComparisonResults = OverrideHidingHelper.DetailedSignatureCompare(
member,
nextMember,
SymbolComparisonResults.AllMismatches And Not (SymbolComparisonResults.CallingConventionMismatch Or SymbolComparisonResults.ConstraintMismatch))
Debug.Assert((comparisonResults And SymbolComparisonResults.PropertyInitOnlyMismatch) = 0)
' only report diagnostics if the signature is considered equal following VB rules.
If (comparisonResults And Not SymbolComparisonResults.MismatchesForConflictingMethods) = 0 Then
ReportOverloadsErrors(comparisonResults, member, nextMember, member.Locations(0), diagnostics)
Exit For
End If
End If
Next
Next
' Validate operator overloading for the last operator, it is not handled by the loop
If canDeclareOperators AndAlso memberKind = SymbolKind.Method AndAlso
CheckForOperatorOverloadingErrors(memberList, memberList.Length - 1, structEnumerator, operatorsKnownToHavePair, diagnostics) Then
Continue For
End If
Next
End While
End If
End Sub
''' <summary>
''' Returns True if memberList(memberIndex) is an operator.
''' Also performs operator overloading validation and reports appropriate errors.
''' </summary>
Private Function CheckForOperatorOverloadingErrors(
memberList As ImmutableArray(Of Symbol),
memberIndex As Integer,
membersEnumerator As Dictionary(Of String, ImmutableArray(Of Symbol)).Enumerator,
<[In](), Out()> ByRef operatorsKnownToHavePair As HashSet(Of MethodSymbol),
diagnostics As BindingDiagnosticBag
) As Boolean
Dim member As Symbol = memberList(memberIndex)
If member.Kind <> SymbolKind.Method Then
Return False
End If
Dim method = DirectCast(member, MethodSymbol)
Dim significantDiff As SymbolComparisonResults = Not SymbolComparisonResults.MismatchesForConflictingMethods
Dim methodMethodKind As MethodKind = method.MethodKind
Select Case methodMethodKind
Case MethodKind.Conversion
significantDiff = significantDiff Or SymbolComparisonResults.ReturnTypeMismatch
Case MethodKind.UserDefinedOperator
Case Else
' Not an operator.
Return False
End Select
Dim opInfo As OverloadResolution.OperatorInfo = OverloadResolution.GetOperatorInfo(method.Name)
If Not OverloadResolution.ValidateOverloadedOperator(method, opInfo, diagnostics, ContainingAssembly) Then
' Malformed operator, but still an operator.
Return True
End If
' Check conflicting overloading with other operators.
If IsConflictingOperatorOverloading(method, significantDiff, memberList, memberIndex + 1, diagnostics) Then
Return True
End If
' CType overloads across Widening and Narrowing, which use different metadata names.
' Need to handle this specially.
If methodMethodKind = MethodKind.Conversion Then
Dim otherName As String = If(IdentifierComparison.Equals(WellKnownMemberNames.ImplicitConversionName, method.Name),
WellKnownMemberNames.ExplicitConversionName, WellKnownMemberNames.ImplicitConversionName)
Dim otherMembers As ImmutableArray(Of Symbol) = Nothing
If MemberAndInitializerLookup.Members.TryGetValue(otherName, otherMembers) Then
While membersEnumerator.MoveNext()
If membersEnumerator.Current.Value = otherMembers Then
If IsConflictingOperatorOverloading(method, significantDiff, otherMembers, 0, diagnostics) Then
Return True
End If
Exit While
End If
End While
End If
End If
' Check for operators that must be declared in pairs.
Dim nameOfThePair As String = Nothing
If opInfo.IsUnary Then
Select Case opInfo.UnaryOperatorKind
Case UnaryOperatorKind.IsTrue
nameOfThePair = WellKnownMemberNames.FalseOperatorName
Case UnaryOperatorKind.IsFalse
nameOfThePair = WellKnownMemberNames.TrueOperatorName
End Select
Else
Select Case opInfo.BinaryOperatorKind
Case BinaryOperatorKind.Equals
nameOfThePair = WellKnownMemberNames.InequalityOperatorName
Case BinaryOperatorKind.NotEquals
nameOfThePair = WellKnownMemberNames.EqualityOperatorName
Case BinaryOperatorKind.LessThan
nameOfThePair = WellKnownMemberNames.GreaterThanOperatorName
Case BinaryOperatorKind.GreaterThan
nameOfThePair = WellKnownMemberNames.LessThanOperatorName
Case BinaryOperatorKind.LessThanOrEqual
nameOfThePair = WellKnownMemberNames.GreaterThanOrEqualOperatorName
Case BinaryOperatorKind.GreaterThanOrEqual
nameOfThePair = WellKnownMemberNames.LessThanOrEqualOperatorName
End Select
End If
If nameOfThePair IsNot Nothing AndAlso
(operatorsKnownToHavePair Is Nothing OrElse Not operatorsKnownToHavePair.Contains(method)) Then
Dim otherMembers As ImmutableArray(Of Symbol) = Nothing
If MemberAndInitializerLookup.Members.TryGetValue(nameOfThePair, otherMembers) Then
For Each other As Symbol In otherMembers
If other.IsUserDefinedOperator() Then
Dim otherMethod = DirectCast(other, MethodSymbol)
Dim comparisonResults As SymbolComparisonResults = MethodSignatureComparer.DetailedCompare(
method,
otherMethod,
SymbolComparisonResults.AllMismatches And
Not (SymbolComparisonResults.CallingConventionMismatch Or
SymbolComparisonResults.ConstraintMismatch Or
SymbolComparisonResults.CustomModifierMismatch Or
SymbolComparisonResults.NameMismatch))
If (comparisonResults And (Not SymbolComparisonResults.MismatchesForConflictingMethods Or SymbolComparisonResults.ReturnTypeMismatch)) = 0 Then
' Found the pair
If operatorsKnownToHavePair Is Nothing Then
operatorsKnownToHavePair = New HashSet(Of MethodSymbol)(ReferenceEqualityComparer.Instance)
End If
operatorsKnownToHavePair.Add(otherMethod)
Return True
End If
End If
Next
End If
diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_MatchingOperatorExpected2,
SyntaxFacts.GetText(OverloadResolution.GetOperatorTokenKind(nameOfThePair)),
method), method.Locations(0))
End If
Return True
End Function
''' <summary>
''' See if any member in [memberList] starting with [memberIndex] conflict with [method],
''' report appropriate error and return true.
''' </summary>
Private Function IsConflictingOperatorOverloading(
method As MethodSymbol,
significantDiff As SymbolComparisonResults,
memberList As ImmutableArray(Of Symbol),
memberIndex As Integer,
diagnostics As BindingDiagnosticBag
) As Boolean
For nextMemberIndex = memberIndex To memberList.Length - 1
Dim nextMember = memberList(nextMemberIndex)
If nextMember.Kind <> SymbolKind.Method Then
Continue For
End If
Dim nextMethod = DirectCast(nextMember, MethodSymbol)
If nextMethod.MethodKind <> method.MethodKind Then
Continue For
End If
Dim comparisonResults As SymbolComparisonResults = MethodSignatureComparer.DetailedCompare(
method,
nextMethod,
SymbolComparisonResults.AllMismatches And
Not (SymbolComparisonResults.CallingConventionMismatch Or
SymbolComparisonResults.ConstraintMismatch Or
SymbolComparisonResults.CustomModifierMismatch Or
SymbolComparisonResults.NameMismatch))
Debug.Assert((comparisonResults And SymbolComparisonResults.PropertyInitOnlyMismatch) = 0)
' only report diagnostics if the signature is considered equal following VB rules.
If (comparisonResults And significantDiff) = 0 Then
ReportOverloadsErrors(comparisonResults, method, nextMethod, method.Locations(0), diagnostics)
Return True
End If
Next
Return False
End Function
''' <summary>
''' Check for two different diagnostics on the set of implemented interfaces:
''' 1) It is invalid for a type to directly (vs through a base class) implement two interfaces that
''' unify (i.e. are the same for some substitution of type parameters).
'''
''' 2) It is a warning to implement variant interfaces twice with type arguments that could cause
''' ambiguity during method dispatch.
''' </summary>
Private Sub CheckInterfaceUnificationAndVariance(diagnostics As BindingDiagnosticBag)
Dim interfaces = Me.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics
If interfaces.IsEmpty OrElse
(interfaces.Count = 1 AndAlso interfaces.Values.Single().Count = 1) Then
Return ' can't have any conflicts
End If
' Check no duplicate interfaces (ignoring tuple names)
Dim declaringSyntax = Me.GetDeclaringSyntaxNode(Of VisualBasicSyntaxNode)()
If declaringSyntax IsNot Nothing Then
For Each keySetPair In interfaces
If keySetPair.Value.Count > 1 Then
Dim other As NamedTypeSymbol = keySetPair.Key
For Each [interface] In keySetPair.Value
If other IsNot [interface] Then
Debug.Assert(EqualsIgnoringComparer.InstanceIgnoringTupleNames.Equals([interface], other))
Debug.Assert(Not TypeSymbol.Equals([interface], other, TypeCompareKind.ConsiderEverything))
ReportDuplicateInterfaceWithDifferentTupleNames(diagnostics, [interface], other)
End If
Next
End If
Next
End If
' We only need to check pairs of generic interfaces that have the same original definition. Put the interfaces
' into buckets by original definition.
Dim originalDefinitionBuckets As New MultiDictionary(Of NamedTypeSymbol, NamedTypeSymbol)
For Each iface In interfaces.Keys
If iface.IsGenericType Then
originalDefinitionBuckets.Add(iface.OriginalDefinition, iface)
End If
Next
' Compare all pairs of interfaces in each bucket.
For Each kvp In originalDefinitionBuckets
If kvp.Value.Count >= 2 Then
Dim i1 As Integer = 0
For Each interface1 In kvp.Value
Dim i2 As Integer = 0
For Each interface2 In kvp.Value
If i2 > i1 Then
Debug.Assert(interface2.IsGenericType AndAlso TypeSymbol.Equals(interface1.OriginalDefinition, interface2.OriginalDefinition, TypeCompareKind.ConsiderEverything))
' Check for interface unification, then variance ambiguity
If TypeUnification.CanUnify(Me, interface1, interface2) Then
ReportInterfaceUnificationError(diagnostics, interface1, interface2)
ElseIf VarianceAmbiguity.HasVarianceAmbiguity(Me, interface1, interface2, CompoundUseSiteInfo(Of AssemblySymbol).Discarded) Then
ReportVarianceAmbiguityWarning(diagnostics, interface1, interface2)
End If
End If
i2 += 1
Next
i1 += 1
Next
End If
Next
End Sub
Private Sub ReportOverloadsErrors(comparisonResults As SymbolComparisonResults, firstMember As Symbol, secondMember As Symbol, location As Location, diagnostics As BindingDiagnosticBag)
Debug.Assert((comparisonResults And SymbolComparisonResults.PropertyInitOnlyMismatch) = 0)
If (Me.Locations.Length > 1 AndAlso Not Me.IsPartial) Then
' if there was an error with the enclosing class, suppress these diagnostics
ElseIf comparisonResults = 0 Then
diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_DuplicateProcDef1, firstMember), location)
Else
' TODO: maybe rewrite these diagnostics to if/elseifs to report just one diagnostic per
' symbol. This would reduce the error count, but may lead to a new diagnostics once the
' previous one was fixed (byref + return type).
If (comparisonResults And SymbolComparisonResults.TupleNamesMismatch) <> 0 Then
diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_DuplicateProcDefWithDifferentTupleNames2, firstMember, secondMember), location)
End If
If (comparisonResults And SymbolComparisonResults.ParameterByrefMismatch) <> 0 Then
diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithByref2, firstMember, secondMember), location)
End If
If (comparisonResults And SymbolComparisonResults.ReturnTypeMismatch) <> 0 Then
diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithReturnType2, firstMember, secondMember), location)
End If
If (comparisonResults And SymbolComparisonResults.ParamArrayMismatch) <> 0 Then
diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithArrayVsParamArray2, firstMember, secondMember), location)
End If
If (comparisonResults And SymbolComparisonResults.OptionalParameterMismatch) <> 0 AndAlso (comparisonResults And SymbolComparisonResults.TotalParameterCountMismatch) = 0 Then
' We have Optional/Required parameter disparity AND the same number of parameters
diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithOptional2, firstMember, secondMember), location)
End If
' With changes in overloading with optional parameters this should never happen
Debug.Assert((comparisonResults And SymbolComparisonResults.OptionalParameterTypeMismatch) = 0)
'If (comparisonResults And SymbolComparisonResults.OptionalParameterTypeMismatch) <> 0 Then
' diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithOptionalTypes2, firstMember, secondMember), location)
' ...
' Dev10 only checks the equality of the default values if the types match in
' CompareParams, so we need to suppress the diagnostic here.
If (comparisonResults And SymbolComparisonResults.OptionalParameterValueMismatch) <> 0 Then
diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithDefault2, firstMember, secondMember), location)
End If
If (comparisonResults And SymbolComparisonResults.PropertyAccessorMismatch) <> 0 Then
diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadingPropertyKind2, firstMember, secondMember), location)
End If
End If
End Sub
''' <summary>
''' Interface1 and Interface2 conflict for some type arguments. Report the correct error in the correct location.
''' </summary>
Private Sub ReportInterfaceUnificationError(diagnostics As BindingDiagnosticBag, interface1 As NamedTypeSymbol, interface2 As NamedTypeSymbol)
If GetImplementsLocation(interface1).SourceSpan.Start > GetImplementsLocation(interface2).SourceSpan.Start Then
' Report error on second implement, for consistency.
Dim temp = interface1
interface1 = interface2
interface2 = temp
End If
' The direct base interfaces that interface1/2 were inherited through.
Dim directInterface1 As NamedTypeSymbol = Nothing
Dim directInterface2 As NamedTypeSymbol = Nothing
Dim location1, location2 As Location
location1 = GetImplementsLocation(interface1, directInterface1)
location2 = GetImplementsLocation(interface2, directInterface2)
Dim isInterface As Boolean = Me.IsInterfaceType()
Dim diag As DiagnosticInfo
If (TypeSymbol.Equals(directInterface1, interface1, TypeCompareKind.ConsiderEverything) AndAlso TypeSymbol.Equals(directInterface2, interface2, TypeCompareKind.ConsiderEverything)) Then
diag = ErrorFactory.ErrorInfo(If(isInterface, ERRID.ERR_InterfaceUnifiesWithInterface2, ERRID.ERR_InterfacePossiblyImplTwice2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1))
ElseIf (Not TypeSymbol.Equals(directInterface1, interface1, TypeCompareKind.ConsiderEverything) AndAlso TypeSymbol.Equals(directInterface2, interface2, TypeCompareKind.ConsiderEverything)) Then
diag = ErrorFactory.ErrorInfo(If(isInterface, ERRID.ERR_InterfaceUnifiesWithBase3, ERRID.ERR_ClassInheritsInterfaceUnifiesWithBase3),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface1))
ElseIf (TypeSymbol.Equals(directInterface1, interface1, TypeCompareKind.ConsiderEverything) AndAlso Not TypeSymbol.Equals(directInterface2, interface2, TypeCompareKind.ConsiderEverything)) Then
diag = ErrorFactory.ErrorInfo(If(isInterface, ERRID.ERR_BaseUnifiesWithInterfaces3, ERRID.ERR_ClassInheritsBaseUnifiesWithInterfaces3),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1))
Else
Debug.Assert(Not TypeSymbol.Equals(directInterface1, interface1, TypeCompareKind.ConsiderEverything) AndAlso Not TypeSymbol.Equals(directInterface2, interface2, TypeCompareKind.ConsiderEverything))
diag = ErrorFactory.ErrorInfo(If(isInterface, ERRID.ERR_InterfaceBaseUnifiesWithBase4, ERRID.ERR_ClassInheritsInterfaceBaseUnifiesWithBase4),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface1))
End If
diagnostics.Add(New VBDiagnostic(diag, location2))
End Sub
''' <summary>
''' Interface1 and Interface2 have variable ambiguity. Report the warning in the correct location.
''' </summary>
Private Sub ReportVarianceAmbiguityWarning(diagnostics As BindingDiagnosticBag, interface1 As NamedTypeSymbol, interface2 As NamedTypeSymbol)
Dim useSiteInfo As New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, ContainingAssembly)
Dim hasVarianceAmbiguity As Boolean = VarianceAmbiguity.HasVarianceAmbiguity(Me, interface1, interface2, useSiteInfo)
If hasVarianceAmbiguity OrElse Not useSiteInfo.Diagnostics.IsNullOrEmpty Then
If GetImplementsLocation(interface1).SourceSpan.Start > GetImplementsLocation(interface2).SourceSpan.Start Then
' Report error on second implement, for consistency.
Dim temp = interface1
interface1 = interface2
interface2 = temp
End If
' The direct base interfaces that interface1/2 were inherited through.
Dim directInterface1 As NamedTypeSymbol = Nothing
Dim directInterface2 As NamedTypeSymbol = Nothing
Dim location1, location2 As Location
location1 = GetImplementsLocation(interface1, directInterface1)
location2 = GetImplementsLocation(interface2, directInterface2)
If Not diagnostics.Add(location2, useSiteInfo) AndAlso hasVarianceAmbiguity Then
Dim diag As DiagnosticInfo
diag = ErrorFactory.ErrorInfo(ERRID.WRN_VarianceDeclarationAmbiguous3,
CustomSymbolDisplayFormatter.QualifiedName(directInterface2),
CustomSymbolDisplayFormatter.QualifiedName(directInterface1),
CustomSymbolDisplayFormatter.ErrorNameWithKind(interface1.OriginalDefinition))
diagnostics.Add(New VBDiagnostic(diag, location2))
End If
Else
diagnostics.AddDependencies(useSiteInfo)
End If
End Sub
''' <summary>
''' Interface1 and Interface2 match except for their tuple names. Report the error in the correct location.
''' </summary>
Private Sub ReportDuplicateInterfaceWithDifferentTupleNames(diagnostics As BindingDiagnosticBag, interface1 As NamedTypeSymbol, interface2 As NamedTypeSymbol)
If GetImplementsLocation(interface1).SourceSpan.Start > GetImplementsLocation(interface2).SourceSpan.Start Then
' Report error on second implement, for consistency.
Dim temp = interface1
interface1 = interface2
interface2 = temp
End If
' The direct base interfaces that interface1/2 were inherited through.
Dim directInterface1 As NamedTypeSymbol = Nothing
Dim directInterface2 As NamedTypeSymbol = Nothing
Dim location1 As Location = GetImplementsLocation(interface1, directInterface1)
Dim location2 As Location = GetImplementsLocation(interface2, directInterface2)
Dim diag As DiagnosticInfo
If (TypeSymbol.Equals(directInterface1, interface1, TypeCompareKind.ConsiderEverything) AndAlso TypeSymbol.Equals(directInterface2, interface2, TypeCompareKind.ConsiderEverything)) Then
diag = ErrorFactory.ErrorInfo(If(IsInterface, ERRID.ERR_InterfaceInheritedTwiceWithDifferentTupleNames2, ERRID.ERR_InterfaceImplementedTwiceWithDifferentTupleNames2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1))
ElseIf (Not TypeSymbol.Equals(directInterface1, interface1, TypeCompareKind.ConsiderEverything) AndAlso TypeSymbol.Equals(directInterface2, interface2, TypeCompareKind.ConsiderEverything)) Then
diag = ErrorFactory.ErrorInfo(If(IsInterface, ERRID.ERR_InterfaceInheritedTwiceWithDifferentTupleNames3, ERRID.ERR_InterfaceImplementedTwiceWithDifferentTupleNames3),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface1))
ElseIf (TypeSymbol.Equals(directInterface1, interface1, TypeCompareKind.ConsiderEverything) AndAlso Not TypeSymbol.Equals(directInterface2, interface2, TypeCompareKind.ConsiderEverything)) Then
diag = ErrorFactory.ErrorInfo(If(IsInterface, ERRID.ERR_InterfaceInheritedTwiceWithDifferentTupleNamesReverse3, ERRID.ERR_InterfaceImplementedTwiceWithDifferentTupleNamesReverse3),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1))
Else
Debug.Assert(Not TypeSymbol.Equals(directInterface1, interface1, TypeCompareKind.ConsiderEverything) AndAlso Not TypeSymbol.Equals(directInterface2, interface2, TypeCompareKind.ConsiderEverything))
diag = ErrorFactory.ErrorInfo(If(IsInterface, ERRID.ERR_InterfaceInheritedTwiceWithDifferentTupleNames4, ERRID.ERR_InterfaceImplementedTwiceWithDifferentTupleNames4),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface2),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1),
CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface1))
End If
diagnostics.Add(New VBDiagnostic(diag, location2))
End Sub
#End Region
#Region "Attributes"
Protected Sub SuppressExtensionAttributeSynthesis()
Debug.Assert(_lazyEmitExtensionAttribute <> ThreeState.True)
_lazyEmitExtensionAttribute = ThreeState.False
End Sub
Private ReadOnly Property EmitExtensionAttribute As Boolean
Get
If _lazyEmitExtensionAttribute = ThreeState.Unknown Then
BindAllMemberAttributes(cancellationToken:=Nothing)
End If
Debug.Assert(_lazyEmitExtensionAttribute <> ThreeState.Unknown)
Return _lazyEmitExtensionAttribute = ThreeState.True
End Get
End Property
Friend Overrides Sub AddSynthesizedAttributes(moduleBuilder As PEModuleBuilder, ByRef attributes As ArrayBuilder(Of SynthesizedAttributeData))
MyBase.AddSynthesizedAttributes(moduleBuilder, attributes)
If EmitExtensionAttribute Then
AddSynthesizedAttribute(attributes, Me.DeclaringCompilation.SynthesizeExtensionAttribute())
End If
End Sub
#End Region
Friend ReadOnly Property AnyMemberHasAttributes As Boolean
Get
If (Not Me._lazyAnyMemberHasAttributes.HasValue()) Then
Me._lazyAnyMemberHasAttributes = Me._declaration.AnyMemberHasAttributes.ToThreeState()
End If
Return Me._lazyAnyMemberHasAttributes.Value()
End Get
End Property
Friend NotOverridable Overrides ReadOnly Property HasAnyDeclaredRequiredMembers As Boolean
Get
Return False
End Get
End Property
End Class
Friend Class EqualsIgnoringComparer
Inherits EqualityComparer(Of TypeSymbol)
Public Shared ReadOnly Property InstanceIgnoringTupleNames As EqualsIgnoringComparer =
New EqualsIgnoringComparer(TypeCompareKind.IgnoreTupleNames)
Public Shared ReadOnly Property InstanceCLRSignatureCompare As EqualsIgnoringComparer =
New EqualsIgnoringComparer(TypeCompareKind.AllIgnoreOptionsForVB And Not TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds)
Private ReadOnly _comparison As TypeCompareKind
Public Sub New(comparison As TypeCompareKind)
_comparison = comparison
End Sub
Public Overrides Function Equals(type1 As TypeSymbol, type2 As TypeSymbol) As Boolean
Return If(type1 Is Nothing,
type2 Is Nothing,
type1.IsSameType(type2, _comparison))
End Function
Public Overrides Function GetHashCode(obj As TypeSymbol) As Integer
Return If(obj Is Nothing, 0, obj.GetHashCode())
End Function
End Class
End Namespace
|