File: Symbols\Source\CustomEventAccessorSymbol.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
 
    Friend NotInheritable Class CustomEventAccessorSymbol
        Inherits SourceNonPropertyAccessorMethodSymbol
 
        Private ReadOnly _event As SourceEventSymbol
        Private ReadOnly _name As String
        Private _lazyExplicitImplementations As ImmutableArray(Of MethodSymbol) ' lazily populated with explicit implementations
 
        Friend Sub New(container As SourceMemberContainerTypeSymbol,
                       [event] As SourceEventSymbol,
                       name As String,
                       flags As SourceMemberFlags,
                       syntaxRef As SyntaxReference,
                       location As Location)
            MyBase.New(container, flags, syntaxRef, locations:=ImmutableArray.Create(location))
 
            _event = [event]
            _name = name
        End Sub
 
        Public Overrides ReadOnly Property Name As String
            Get
                Return _name
            End Get
        End Property
 
        Public Overrides ReadOnly Property MetadataName As String
            Get
                ' Event symbols aren't affected if the output kind is winmd, mark false
                ' (N.B., events only emits helpers named add_ and remove_, not set_)
                Return Binder.GetAccessorName(_event.MetadataName, Me.MethodKind, isWinMd:=False)
            End Get
        End Property
 
        Public Overrides ReadOnly Property TypeParameters As ImmutableArray(Of TypeParameterSymbol)
            Get
                Return ImmutableArray(Of TypeParameterSymbol).Empty
            End Get
        End Property
 
        Public Overrides ReadOnly Property DeclaredAccessibility As Accessibility
            Get
                ' Raise is always private
                If Me.MethodKind = MethodKind.EventRaise Then
                    Return Accessibility.Private
                End If
 
                Return _event.DeclaredAccessibility
            End Get
        End Property
 
        Protected Overrides Function GetParameters(sourceModule As SourceModuleSymbol, diagBag As BindingDiagnosticBag) As ImmutableArray(Of ParameterSymbol)
            Dim type = DirectCast(Me.ContainingType, SourceMemberContainerTypeSymbol)
            Dim binder As Binder = BinderBuilder.CreateBinderForType(sourceModule, Me.SyntaxTree, type)
            binder = New LocationSpecificBinder(BindingLocation.EventAccessorSignature, Me, binder)
 
            Return BindParameters(Me.Locations.FirstOrDefault, binder, BlockSyntax.BlockStatement.ParameterList, diagBag)
        End Function
 
        Public Overrides ReadOnly Property AssociatedSymbol As Symbol
            Get
                Return _event
            End Get
 
        End Property
 
        Friend Overrides ReadOnly Property ShadowsExplicitly As Boolean
            Get
                Return _event.ShadowsExplicitly
            End Get
        End Property
 
        Public Overrides ReadOnly Property ExplicitInterfaceImplementations As ImmutableArray(Of MethodSymbol)
            Get
                If _lazyExplicitImplementations.IsDefault Then
                    ImmutableInterlocked.InterlockedCompareExchange(
                        _lazyExplicitImplementations,
                        _event.GetAccessorImplementations(Me.MethodKind),
                        Nothing)
                End If
 
                Return _lazyExplicitImplementations
            End Get
        End Property
 
        Public Overrides ReadOnly Property OverriddenMethod As MethodSymbol
            Get
                ' custom event methods do not override
                Return Nothing
            End Get
        End Property
 
        Public Overrides ReadOnly Property ReturnTypeCustomModifiers As ImmutableArray(Of CustomModifier)
            Get
                ' custom event methods do not have explicit returns and never implement or override
                Return ImmutableArray(Of CustomModifier).Empty
            End Get
        End Property
 
        Public Overrides ReadOnly Property IsExtensionMethod As Boolean
            Get
                ' Event accessors can never be an extension method.
                Return False
            End Get
        End Property
 
        Friend Overrides ReadOnly Property MayBeReducibleExtensionMethod As Boolean
            Get
                ' Event accessors can never be an extension method.
                Return False
            End Get
        End Property
 
        Protected Overrides Function GetAttributeDeclarations() As OneOrMany(Of SyntaxList(Of AttributeListSyntax))
            Return OneOrMany.Create(AttributeDeclarationSyntaxList)
        End Function
 
        Protected Overrides Function GetReturnTypeAttributeDeclarations() As OneOrMany(Of SyntaxList(Of AttributeListSyntax))
            Return Nothing
        End Function
 
        ''' <remarks>
        ''' We're going to ignore SourceMemberFlags.MethodIsSub and override IsSub explicitly.  We do this because
        ''' the flags have to be set at construction time, but IsSub depends on IsWindowsRuntimeEvent, which depends
        ''' on interface implementations, which we don't want to bind until the member list is complete.  (It's probably 
        ''' okay now (2012/12/17), but it would be very fragile to take a dependency on the exact mechanism by which
        '''  interface members are looked up.)
        ''' </remarks>
        Public Overrides ReadOnly Property IsSub As Boolean
            Get
                Return Not (Me.MethodKind = MethodKind.EventAdd AndAlso _event.IsWindowsRuntimeEvent)
            End Get
        End Property
 
        ''' <summary>
        ''' Bind and validate parameters declared on the accessor.
        ''' </summary>
        Private Function BindParameters(location As Location,
                                        binder As Binder,
                                        parameterListOpt As ParameterListSyntax,
                                        diagnostics As BindingDiagnosticBag) As ImmutableArray(Of ParameterSymbol)
 
            Dim parameterListSyntax = If(parameterListOpt Is Nothing, Nothing, parameterListOpt.Parameters)
            Dim nParameters = parameterListSyntax.Count
            Dim paramBuilder = ArrayBuilder(Of ParameterSymbol).GetInstance(nParameters)
 
            ' Bind all parameters (even though we know how many to expect), 
            ' to ensure all diagnostics are generated and ensure parameter symbols are available for binding the method body.
            binder.DecodeParameterList(
                Me,
                False,
                SourceMemberFlags.None,
                parameterListSyntax,
                paramBuilder,
                If(Me.MethodKind = MethodKind.EventRaise,
                   s_checkRaiseParameterModifierCallback,
                   s_checkAddRemoveParameterModifierCallback),
                diagnostics)
 
            Dim parameters = paramBuilder.ToImmutableAndFree()
 
            If Me.MethodKind = MethodKind.EventRaise Then
                ' Dev10 does something weird here - it checks for method conversion, but does it 
                ' backwards - it allows delegate Invoke to be more specific than signature of Raise. 
                ' Example: delegate may take int, but Raise may take long argument.
                '
                ' For backwards compatibility we will do the same.
                '
                ' NOTE: no change in raise event shape for WinRT events.
                Dim eventType = TryCast(_event.Type, NamedTypeSymbol)
                If eventType IsNot Nothing AndAlso
                    Not eventType.IsErrorType Then
 
                    Dim delInvoke = eventType.DelegateInvokeMethod
 
                    ' If delegate is a function method we should already have diagnostics about that
                    If delInvoke IsNot Nothing AndAlso delInvoke.IsSub Then
                        Dim useSiteInfo = binder.GetNewCompoundUseSiteInfo(diagnostics)
                        Dim conversion = Conversions.ClassifyMethodConversionForEventRaise(
                                                            delInvoke,
                                                            parameters,
                                                            useSiteInfo)
 
                        If Not diagnostics.Add(location, useSiteInfo) AndAlso
                            (Not Conversions.IsDelegateRelaxationSupportedFor(conversion) OrElse
                             (binder.OptionStrict = OptionStrict.On AndAlso Conversions.IsNarrowingMethodConversion(conversion, False))) Then
 
                            diagnostics.Add(ERRID.ERR_RaiseEventShapeMismatch1, location, eventType)
                        End If
 
                    End If
                End If
            Else
                If parameters.Length <> 1 Then
                    diagnostics.Add(ERRID.ERR_EventAddRemoveHasOnlyOneParam, location)
                Else
                    Dim eventType = _event.Type
                    Debug.Assert(eventType IsNot Nothing)
                    Dim parameterType = parameters(0).Type
                    Debug.Assert(parameterType IsNot Nothing)
 
                    If Me.MethodKind = MethodKind.EventAdd Then
                        If Not eventType.IsErrorType AndAlso Not TypeSymbol.Equals(eventType, parameterType, TypeCompareKind.ConsiderEverything) Then
                            Dim errid As ERRID = If(_event.IsWindowsRuntimeEvent, ERRID.ERR_AddParamWrongForWinRT, ERRID.ERR_AddRemoveParamNotEventType)
                            diagnostics.Add(errid, location)
                        End If
                    Else
                        Debug.Assert(Me.MethodKind = MethodKind.EventRemove)
 
                        If _event.ExplicitInterfaceImplementations.Any() Then
                            ' Reporting diagnostics when this type is missing will only ever result in cascading, so don't bother.
                            Dim registrationTokenType As NamedTypeSymbol =
                                binder.Compilation.GetWellKnownType(WellKnownType.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationToken)
 
                            Dim firstImplementedEvent As EventSymbol = _event.ExplicitInterfaceImplementations(0)
                            If Not registrationTokenType.IsErrorType AndAlso firstImplementedEvent.IsWindowsRuntimeEvent <> (TypeSymbol.Equals(parameterType, registrationTokenType, TypeCompareKind.ConsiderEverything)) Then
                                diagnostics.Add(ERRID.ERR_EventImplRemoveHandlerParamWrong, location, _event.Name, firstImplementedEvent.Name, firstImplementedEvent.ContainingType)
                            End If
                        ElseIf _event.IsWindowsRuntimeEvent Then
                            ' Reporting diagnostics when this type is missing will only ever result in cascading, so don't bother.
                            Dim registrationTokenType As NamedTypeSymbol =
                                binder.Compilation.GetWellKnownType(WellKnownType.System_Runtime_InteropServices_WindowsRuntime_EventRegistrationToken)
                            If Not registrationTokenType.IsErrorType AndAlso Not TypeSymbol.Equals(parameterType, registrationTokenType, TypeCompareKind.ConsiderEverything) Then
                                diagnostics.Add(ERRID.ERR_RemoveParamWrongForWinRT, location)
                            End If
                        Else
                            If Not eventType.IsErrorType AndAlso Not TypeSymbol.Equals(eventType, parameterType, TypeCompareKind.ConsiderEverything) Then
                                diagnostics.Add(ERRID.ERR_AddRemoveParamNotEventType, location)
                            End If
                        End If
                    End If
                End If
            End If
 
            Return parameters
        End Function
 
        Private Shared ReadOnly s_checkAddRemoveParameterModifierCallback As Binder.CheckParameterModifierDelegate = AddressOf CheckAddRemoveParameterModifier
        Private Shared ReadOnly s_checkRaiseParameterModifierCallback As Binder.CheckParameterModifierDelegate = AddressOf CheckEventMethodParameterModifier
 
        ' applicable to all event methods
        Private Shared Function CheckEventMethodParameterModifier(container As Symbol, token As SyntaxToken, flag As SourceParameterFlags, diagnostics As BindingDiagnosticBag) As SourceParameterFlags
            If (flag And SourceParameterFlags.Optional) <> 0 Then
                Dim location = token.GetLocation()
                diagnostics.Add(ERRID.ERR_EventMethodOptionalParamIllegal1, location, token.ToString())
                flag = flag And (Not SourceParameterFlags.Optional)
            End If
 
            If (flag And SourceParameterFlags.ParamArray) <> 0 Then
                Dim location = token.GetLocation()
                diagnostics.Add(ERRID.ERR_EventMethodOptionalParamIllegal1, location, token.ToString())
                flag = flag And (Not SourceParameterFlags.ParamArray)
            End If
 
            Return flag
        End Function
 
        ' additional rules for Add and Remove
        Private Shared Function CheckAddRemoveParameterModifier(container As Symbol, token As SyntaxToken, flag As SourceParameterFlags, diagnostics As BindingDiagnosticBag) As SourceParameterFlags
            If (flag And SourceParameterFlags.ByRef) <> 0 Then
                Dim location = token.GetLocation()
                diagnostics.Add(ERRID.ERR_EventAddRemoveByrefParamIllegal, location, token.ToString())
                flag = flag And (Not SourceParameterFlags.ByRef)
            End If
 
            Return CheckEventMethodParameterModifier(container, token, flag, diagnostics)
        End Function
    End Class
 
End Namespace