File: Symbols\SymbolExtensions.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.Runtime.CompilerServices
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
    Friend Module SymbolExtensions
 
        Friend Const NamespaceKindNamespaceGroup As NamespaceKind = Nothing
 
        ''' <summary>
        ''' Does the compilation this symbol belongs to output to a winmdobj?
        ''' </summary>
        <Extension()>
        Friend Function IsCompilationOutputWinMdObj(symbol As Symbol) As Boolean
            Dim comp = symbol.DeclaringCompilation
            Return comp IsNot Nothing And comp.Options.OutputKind = OutputKind.WindowsRuntimeMetadata
        End Function
 
        ''' <summary>
        ''' String such as 'class', 'interface' etc that can be used in error messages.
        ''' </summary>
        <Extension()>
        Friend Function GetKindText(target As Symbol) As String
            Select Case target.Kind
 
                Case SymbolKind.Namespace
                    Return "namespace"
 
                Case SymbolKind.NamedType
                    Select Case DirectCast(target, TypeSymbol).TypeKind
                        Case TypeKind.Class
                            Return "class"
                        Case TypeKind.Enum
                            Return "enum"
                        Case TypeKind.Interface
                            Return "interface"
                        Case TypeKind.Structure
                            Return "structure"
                        Case TypeKind.Module
                            Return "module"
                        Case TypeKind.Delegate
                            ' Dev10 error message format "... delegate Class goo ..." instead of "... delegate goo ..."               
                            Return "delegate Class"
                        Case Else
                            'TODO: do we need string s for ByRef, Array, TypeParameter etc?
                            Return "type"
                    End Select
 
                Case SymbolKind.Field, SymbolKind.Local, SymbolKind.Parameter, SymbolKind.RangeVariable
                    Return "variable"
 
                Case SymbolKind.Method
                    Dim methodSymbol = DirectCast(target, MethodSymbol)
                    Select Case methodSymbol.MethodKind
                        Case MethodKind.Conversion, MethodKind.UserDefinedOperator, MethodKind.BuiltinOperator
                            Return "operator"
                        Case Else
                            If methodSymbol.IsSub Then
                                Return "sub"
                            Else
                                Return "function"
                            End If
                    End Select
 
                Case SymbolKind.Property
                    If DirectCast(target, PropertySymbol).IsWithEvents Then
                        Return "WithEvents variable"
                    Else
                        Return "property"
                    End If
 
                Case SymbolKind.Event
                    Return "event"
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(target.Kind)
            End Select
        End Function
 
        ''' <summary>
        ''' String "ReadOnly", "WriteOnly", or "" describing the kind of property.
        ''' </summary>
        <Extension()>
        Friend Function GetPropertyKindText(target As PropertySymbol) As String
            If target.IsWriteOnly Then
                Return SyntaxFacts.GetText(SyntaxKind.WriteOnlyKeyword)
            ElseIf target.IsReadOnly Then
                Return SyntaxFacts.GetText(SyntaxKind.ReadOnlyKeyword)
            Else
                Return ""
            End If
        End Function
 
        <Extension()>
        Friend Function ToErrorMessageArgument(target As Symbol, Optional errorCode As ERRID = ERRID.ERR_None) As Object
            If target.Kind = SymbolKind.Namespace Then
                Dim ns As NamespaceSymbol = DirectCast(target, NamespaceSymbol)
                If ns.IsGlobalNamespace Then
                    Return StringConstants.UnnamedNamespaceErrName
                End If
            End If
 
            If errorCode = ERRID.ERR_TypeConflict6 Then
                Return CustomSymbolDisplayFormatter.DefaultErrorFormat(target)
            End If
 
            Return target
        End Function
 
        ''' <summary>
        ''' Checks if there is a name match with any type parameter.
        ''' </summary>
        ''' <param name="this"></param>
        ''' <param name="name"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <Extension()>
        Friend Function MatchesAnyName(this As ImmutableArray(Of TypeParameterSymbol), name As String) As Boolean
            For Each tp In this
                If IdentifierComparison.Comparer.Compare(name, tp.Name) = 0 Then
                    Return True
                End If
            Next
 
            Return False
        End Function
 
        ''' <summary>
        ''' Returns true if this symbols can overload another of the same kind.
        ''' </summary>
        <Extension()>
        Public Function IsOverloadable(symbol As Symbol) As Boolean
            Dim kind = symbol.Kind
            If kind = SymbolKind.Method Then
                Return True
            ElseIf kind <> SymbolKind.Property Then
                Return False
            End If
 
            ' uncommon case - WithEvents property do not overload. They behave like fields when OHI is concerned.
            Return DirectCast(symbol, PropertySymbol).IsOverloadable
        End Function
 
        ''' <summary>
        ''' Returns true if this property can overload another.
        ''' </summary>
        <Extension()>
        Public Function IsOverloadable(propertySymbol As PropertySymbol) As Boolean
            Return Not propertySymbol.IsWithEvents
        End Function
 
        ''' <summary>
        ''' Helper that tells if symbol has Overloads (hidebysig) on it
        ''' </summary>
        <Extension()>
        Friend Function IsOverloads(sym As Symbol) As Boolean
            Debug.Assert(sym IsNot Nothing)
 
            ' methods and properties can overload.
            Select Case sym.Kind
                Case SymbolKind.Method
                    Dim method = DirectCast(sym, MethodSymbol)
                    If method.IsOverloads Then
                        Return True
                    End If
 
                Case SymbolKind.Property
                    Dim prop = DirectCast(sym, PropertySymbol)
                    If prop.IsOverloads Then
                        Return True
                    End If
            End Select
 
            Return False
        End Function
 
        ''' <summary>
        ''' Member that does not have Overloads, is considered Shadows (hidebyname)
        ''' </summary>
        <Extension()>
        Friend Function IsShadows(sym As Symbol) As Boolean
            ' everything that does not overload, shadows
            Return Not sym.IsOverloads
        End Function
 
        ''' <summary>
        ''' Is the symbol an instance member (i.e. access requires a receiver)
        ''' </summary>
        <Extension()>
        Friend Function IsInstanceMember(sym As Symbol) As Boolean
            Select Case sym.Kind
                Case SymbolKind.Field, SymbolKind.Property, SymbolKind.Method, SymbolKind.Event
                    Return Not sym.IsShared
                Case Else
                    Return False
            End Select
        End Function
 
        ''' <summary>
        ''' Is this a member of a interface that requires implementation?
        ''' </summary>
        <Extension()>
        Friend Function RequiresImplementation(sym As Symbol) As Boolean
            Select Case sym.Kind
                Case SymbolKind.Method, SymbolKind.Property, SymbolKind.Event
                    ' Note that in metadata, you can encounter methods that are static (shared) or non-virtual 
                    ' (for example TLBIMP VtblGap members), even though you can't define those in source.
                    Return sym.ContainingType.IsInterfaceType() AndAlso
                           Not sym.IsNotOverridable AndAlso
                           (sym.IsMustOverride OrElse sym.IsOverridable)
                Case Else
                    Return False
            End Select
        End Function
 
        <Extension()>
        Friend Function IsMetadataVirtual(method As MethodSymbol) As Boolean
            If method.IsOverridable OrElse
                    method.IsOverrides OrElse
                    method.IsMustOverride OrElse
                    Not method.ExplicitInterfaceImplementations.IsEmpty Then
                Return True
            End If
 
            Dim definition As MethodSymbol = method.OriginalDefinition
            Dim containingSourceType = TryCast(definition.ContainingSymbol, SourceNamedTypeSymbol)
 
            Return containingSourceType IsNot Nothing AndAlso
                   containingSourceType.GetCorrespondingComClassInterfaceMethod(definition) IsNot Nothing
        End Function
 
        <Extension()>
        Public Function IsAccessor(methodSymbol As MethodSymbol) As Boolean
            Return methodSymbol.AssociatedSymbol IsNot Nothing
        End Function
 
        <Extension()>
        Public Function IsAccessor(symbol As Symbol) As Boolean
            Return symbol.Kind = SymbolKind.Method AndAlso IsAccessor(DirectCast(symbol, MethodSymbol))
        End Function
 
        <Extension()>
        Public Function IsWithEventsProperty(symbol As Symbol) As Boolean
            Return symbol.Kind = SymbolKind.Property AndAlso DirectCast(symbol, PropertySymbol).IsWithEvents
        End Function
 
        ''' <summary>
        ''' Returns True for "regular" properties (those that are not WithEvents.
        ''' Typically used for OHI diagnostics where WithEvents properties are treated as variables.
        ''' </summary>
        <Extension()>
        Public Function IsPropertyAndNotWithEvents(symbol As Symbol) As Boolean
            Return symbol.Kind = SymbolKind.Property AndAlso Not DirectCast(symbol, PropertySymbol).IsWithEvents
        End Function
 
        <Extension()>
        Friend Function IsAnyConstructor(method As MethodSymbol) As Boolean
            Dim kind = method.MethodKind
            Return kind = MethodKind.Constructor OrElse kind = MethodKind.SharedConstructor
        End Function
 
        ''' <summary>
        ''' default zero-init constructor symbol is added to a struct when it does not define 
        ''' its own parameterless public constructor.
        ''' We do not emit this constructor and do not call it 
        ''' </summary>
        <Extension()>
        Friend Function IsDefaultValueTypeConstructor(method As MethodSymbol) As Boolean
            Return method.IsImplicitlyDeclared AndAlso
                   method.ContainingType.IsValueType AndAlso
                   method.IsParameterlessConstructor()
        End Function
 
        <Extension()>
        Friend Function IsReducedExtensionMethod(this As Symbol) As Boolean
            Return this.Kind = SymbolKind.Method AndAlso DirectCast(this, MethodSymbol).IsReducedExtensionMethod
        End Function
 
        ''' <summary>
        ''' Return the overridden symbol for either a method or property.
        ''' </summary>
        <Extension()>
        Friend Function OverriddenMember(sym As Symbol) As Symbol
            Select Case sym.Kind
                Case SymbolKind.Method
                    Return DirectCast(sym, MethodSymbol).OverriddenMethod
                Case SymbolKind.Property
                    Return DirectCast(sym, PropertySymbol).OverriddenProperty
                Case SymbolKind.Event
                    Return DirectCast(sym, EventSymbol).OverriddenEvent
                Case Else
                    Return Nothing
            End Select
        End Function
 
        ''' <summary> 
        ''' Return the arity of a member. 
        ''' </summary>
        <Extension()>
        Friend Function GetArity(symbol As Symbol) As Integer
            Select Case symbol.Kind
                Case SymbolKind.Method
                    Return (DirectCast(symbol, MethodSymbol)).Arity
                Case SymbolKind.NamedType, SymbolKind.ErrorType
                    Return (DirectCast(symbol, NamedTypeSymbol)).Arity
                Case Else
                    Return 0
            End Select
        End Function
 
        ''' <summary> 
        ''' Returns the Me symbol associated with a member. 
        ''' sym must be a member (method, field or property)
        ''' </summary>
        <Extension()>
        Friend Function GetMeParameter(sym As Symbol) As ParameterSymbol
            Select Case sym.Kind
                Case SymbolKind.Method
                    Return DirectCast(sym, MethodSymbol).MeParameter
                Case SymbolKind.Field
                    Return DirectCast(sym, FieldSymbol).MeParameter
                Case SymbolKind.Property
                    Return DirectCast(sym, PropertySymbol).MeParameter
                Case SymbolKind.Parameter
                    Return Nothing
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(sym.Kind)
            End Select
        End Function
 
        ''' <summary>
        ''' Returns the parameters of a given method or property.
        ''' </summary>
        <Extension()>
        Friend Function GetParameters(sym As Symbol) As ImmutableArray(Of ParameterSymbol)
            Select Case sym.Kind
                Case SymbolKind.Method
                    Return DirectCast(sym, MethodSymbol).Parameters
                Case SymbolKind.Property
                    Return DirectCast(sym, PropertySymbol).Parameters
                Case Else
                    Return ImmutableArray(Of ParameterSymbol).Empty
            End Select
        End Function
 
        <Extension()>
        Friend Function OfMinimalArity(symbols As IEnumerable(Of NamespaceOrTypeSymbol)) As NamespaceOrTypeSymbol
            Dim minAritySymbol As NamespaceOrTypeSymbol = Nothing
            Dim minArity As Integer = Int32.MaxValue
            For Each symbol In symbols
                Dim arity As Integer = GetArity(symbol)
                If arity < minArity Then
                    minArity = arity
                    minAritySymbol = symbol
                End If
            Next
 
            Return minAritySymbol
        End Function
 
        <Extension()>
        Friend Function UnwrapAlias(symbol As Symbol) As Symbol
            Dim aliasSym = TryCast(symbol, AliasSymbol)
            Return If(aliasSym Is Nothing, symbol, aliasSym.Target)
        End Function
 
        ''' <summary>
        ''' Is symbol a user-defined operator method.
        ''' </summary>
        <Extension()>
        Friend Function IsUserDefinedOperator(symbol As Symbol) As Boolean
            Return symbol.Kind = SymbolKind.Method AndAlso DirectCast(symbol, MethodSymbol).IsUserDefinedOperator()
        End Function
 
        ''' <summary>
        ''' Does symbol or its containing type have Microsoft.CodeAnalysis.Embedded() attribute
        ''' </summary>
        <Extension()>
        Friend Function IsHiddenByCodeAnalysisEmbeddedAttribute(symbol As Symbol) As Boolean
            ' Only upper-level types should be checked 
            Dim upperLevelType = GetUpperLevelNamedTypeSymbol(symbol)
            Return upperLevelType IsNot Nothing AndAlso upperLevelType.HasCodeAnalysisEmbeddedAttribute
        End Function
 
        ''' <summary>
        ''' Does symbol or its containing type have Microsoft.VisualBasic.Embedded() attribute
        ''' </summary>
        <Extension()>
        Friend Function IsHiddenByVisualBasicEmbeddedAttribute(symbol As Symbol) As Boolean
            ' Only upper-level types should be checked 
            Dim upperLevelType = GetUpperLevelNamedTypeSymbol(symbol)
            Return upperLevelType IsNot Nothing AndAlso upperLevelType.HasVisualBasicEmbeddedAttribute
        End Function
 
        ''' <summary>
        ''' Gets the upper-level named type symbol, or returns Nothing if it does not exist.
        ''' </summary>
        <Extension()>
        Friend Function GetUpperLevelNamedTypeSymbol(symbol As Symbol) As NamedTypeSymbol
            Dim upperLevelType = If(symbol.Kind = SymbolKind.NamedType, DirectCast(symbol, NamedTypeSymbol), symbol.ContainingType)
            If upperLevelType Is Nothing Then
                Return Nothing
            End If
 
            While upperLevelType.ContainingType IsNot Nothing
                upperLevelType = upperLevelType.ContainingType
            End While
 
            Return upperLevelType
        End Function
 
        <Extension>
        Friend Function GetDeclaringSyntaxNode(Of T As VisualBasicSyntaxNode)(this As Symbol) As T
 
            For Each node In this.DeclaringSyntaxReferences.Select(Function(d) d.GetSyntax())
                Dim node_T = TryCast(node, T)
                If node_T IsNot Nothing Then
                    Return node_T
                End If
            Next
 
            Debug.Assert(this.IsImplicitlyDeclared)
            Return DirectCast(Symbol.GetDeclaringSyntaxNodeHelper(Of T)(this.Locations).FirstOrDefault, T)
        End Function
 
        <Extension>
        Friend Function AsMember(Of T As Symbol)(origMember As T, type As NamedTypeSymbol) As T
            Debug.Assert(origMember.IsDefinition)
 
            ' ContainingType is always a definition here which is a type definition so we can use "Is"
            If type Is origMember.ContainingType Then
                Return origMember
            End If
 
            Dim substituted = DirectCast(type, SubstitutedNamedType)
            Return DirectCast(substituted.GetMemberForDefinition(origMember), T)
        End Function
 
        <Extension>
        Friend Function EnsureVbSymbolOrNothing(Of TSource As ISymbol, TDestination As {Symbol, TSource})(symbol As TSource, paramName As String) As TDestination
            Dim vbSymbol = TryCast(symbol, TDestination)
 
            If vbSymbol Is Nothing AndAlso symbol IsNot Nothing Then
                Throw New ArgumentException(VBResources.NotAVbSymbol, paramName)
            End If
 
            Return vbSymbol
        End Function
 
        <Extension>
        Friend Function ContainingNonLambdaMember(member As Symbol) As Symbol
            While If(member?.Kind = SymbolKind.Method, False) AndAlso DirectCast(member, MethodSymbol).MethodKind = MethodKind.AnonymousFunction
                member = member.ContainingSymbol
            End While
 
            Return member
        End Function
 
        <Extension>
        Friend Function ContainsTupleNames(member As Symbol) As Boolean
            Select Case member.Kind
                Case SymbolKind.Method
                    Dim method = DirectCast(member, MethodSymbol)
                    Return method.ReturnType.ContainsTupleNames() OrElse ContainsTupleNames(method.Parameters)
                Case SymbolKind.Property
                    Dim [property] = DirectCast(member, PropertySymbol)
                    Return [property].Type.ContainsTupleNames() OrElse ContainsTupleNames([property].Parameters)
                Case SymbolKind.Event
                    ' We don't check the event Type directly because materializing it requires checking the tuple names in the type (to validate interface implementations)
                    Return ContainsTupleNames(DirectCast(member, EventSymbol).DelegateParameters)
                Case Else
                    '  We currently don't need to use this method for other kinds of symbols
                    Throw ExceptionUtilities.UnexpectedValue(member.Kind)
            End Select
        End Function
 
        Private Function ContainsTupleNames(parameters As ImmutableArray(Of ParameterSymbol)) As Boolean
            Return parameters.Any(Function(p) p.Type.ContainsTupleNames())
        End Function
 
        <Extension>
        Friend Function IsRequired(member As Symbol) As Boolean
            Return If(TryCast(member, PropertySymbol)?.IsRequired,
                      If(TryCast(member, FieldSymbol)?.IsRequired,
                         False))
        End Function
    End Module
End Namespace