File: Binding\DocumentationCommentBinder.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 Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports System.Runtime.InteropServices
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    ''' <summary>
    ''' Binder used for interiors of documentation comment
    ''' </summary>
    Friend MustInherit Class DocumentationCommentBinder
        Inherits Binder
 
        Protected Sub New(containingBinder As Binder, commentedSymbol As Symbol)
            MyBase.New(containingBinder)
 
            CheckBinderSymbolRelationship(containingBinder, commentedSymbol)
 
            Me.CommentedSymbol = commentedSymbol
        End Sub
 
        ''' <summary>
        ''' Assuming there is one, the containing member of the binder is the commented symbol if and only if
        ''' the commented symbol is a non-delegate named type.  (Otherwise, it is the containing type or namespace of the commented symbol.)
        ''' </summary>
        ''' <remarks>
        ''' Delegates don't have user-defined members, so it makes more sense to treat
        ''' them like methods.
        ''' </remarks>
        <Conditional("DEBUG")>
        Private Shared Sub CheckBinderSymbolRelationship(containingBinder As Binder, commentedSymbol As Symbol)
            If commentedSymbol Is Nothing Then
                Return
            End If
 
            Dim commentedNamedType = TryCast(commentedSymbol, NamedTypeSymbol)
            Dim binderContainingMember As Symbol = containingBinder.ContainingMember
            If commentedNamedType IsNot Nothing AndAlso commentedNamedType.TypeKind <> TypeKind.Delegate Then
                Debug.Assert(binderContainingMember = commentedSymbol)
            ElseIf commentedSymbol.ContainingType IsNot Nothing Then
                Debug.Assert(TypeSymbol.Equals(DirectCast(binderContainingMember, TypeSymbol), commentedSymbol.ContainingType, TypeCompareKind.ConsiderEverything))
            Else
                ' It's not worth writing a complicated check that handles merged namespaces.
                Debug.Assert(binderContainingMember <> commentedSymbol)
                Debug.Assert(binderContainingMember.Kind = SymbolKind.Namespace)
            End If
 
        End Sub
 
        Friend Enum BinderType
            None
            Cref
            NameInTypeParamRef
            NameInTypeParam
            NameInParamOrParamRef
        End Enum
 
        Public Shared Function IsIntrinsicTypeForDocumentationComment(kind As SyntaxKind) As Boolean
            Select Case kind
                Case SyntaxKind.ShortKeyword,
                     SyntaxKind.UShortKeyword,
                     SyntaxKind.IntegerKeyword,
                     SyntaxKind.UIntegerKeyword,
                     SyntaxKind.LongKeyword,
                     SyntaxKind.ULongKeyword,
                     SyntaxKind.DecimalKeyword,
                     SyntaxKind.SingleKeyword,
                     SyntaxKind.DoubleKeyword,
                     SyntaxKind.SByteKeyword,
                     SyntaxKind.ByteKeyword,
                     SyntaxKind.BooleanKeyword,
                     SyntaxKind.CharKeyword,
                     SyntaxKind.DateKeyword,
                     SyntaxKind.StringKeyword
                    Return True
 
                Case Else
                    Return False
            End Select
        End Function
 
        Friend Shared Function GetBinderTypeForNameAttribute(node As BaseXmlAttributeSyntax) As DocumentationCommentBinder.BinderType
            Return GetBinderTypeForNameAttribute(GetParentXmlElementName(node))
        End Function
 
        Friend Shared Function GetBinderTypeForNameAttribute(parentNodeName As String) As DocumentationCommentBinder.BinderType
            If parentNodeName IsNot Nothing Then
                If DocumentationCommentXmlNames.ElementEquals(parentNodeName, DocumentationCommentXmlNames.ParameterElementName, True) OrElse
                        DocumentationCommentXmlNames.ElementEquals(parentNodeName, DocumentationCommentXmlNames.ParameterReferenceElementName, True) Then
                    Return DocumentationCommentBinder.BinderType.NameInParamOrParamRef
 
                ElseIf DocumentationCommentXmlNames.ElementEquals(parentNodeName, DocumentationCommentXmlNames.TypeParameterElementName, True) Then
                    Return DocumentationCommentBinder.BinderType.NameInTypeParam
 
                ElseIf DocumentationCommentXmlNames.ElementEquals(parentNodeName, DocumentationCommentXmlNames.TypeParameterReferenceElementName, True) Then
                    Return DocumentationCommentBinder.BinderType.NameInTypeParamRef
                End If
            End If
 
            Return DocumentationCommentBinder.BinderType.None
        End Function
 
        Friend Shared Function GetParentXmlElementName(attr As BaseXmlAttributeSyntax) As String
            Dim parent As VisualBasicSyntaxNode = attr.Parent
            If parent Is Nothing Then
                Return Nothing
            End If
 
            Select Case parent.Kind
                Case SyntaxKind.XmlEmptyElement
                    Dim element = DirectCast(parent, XmlEmptyElementSyntax)
                    If element.Name.Kind <> SyntaxKind.XmlName Then
                        Return Nothing
                    End If
                    Return DirectCast(element.Name, XmlNameSyntax).LocalName.ValueText
 
                Case SyntaxKind.XmlElementStartTag
                    Dim element = DirectCast(parent, XmlElementStartTagSyntax)
                    If element.Name.Kind <> SyntaxKind.XmlName Then
                        Return Nothing
                    End If
                    Return DirectCast(element.Name, XmlNameSyntax).LocalName.ValueText
 
            End Select
 
            Return Nothing
        End Function
 
        ''' <summary>
        ''' Symbol commented with the documentation comment handled by this binder. In general,
        ''' all name lookup is being performed in context of this symbol's containing symbol.
        ''' We still need this symbol, though, to be able to find type parameters or parameters
        ''' referenced from 'param', 'paramref', 'typeparam' and 'typeparamref' tags.
        ''' </summary>
        Protected ReadOnly CommentedSymbol As Symbol
 
        Friend Overrides Function BindXmlNameAttributeValue(identifier As IdentifierNameSyntax, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ImmutableArray(Of Symbol)
            Throw ExceptionUtilities.Unreachable
        End Function
 
        Friend Overrides Function BindInsideCrefAttributeValue(name As TypeSyntax, preserveAliases As Boolean, diagnosticBag As BindingDiagnosticBag, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ImmutableArray(Of Symbol)
            Throw ExceptionUtilities.Unreachable
        End Function
 
        Friend Overrides Function BindInsideCrefAttributeValue(reference As CrefReferenceSyntax, preserveAliases As Boolean, diagnosticBag As BindingDiagnosticBag, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ImmutableArray(Of Symbol)
            Throw ExceptionUtilities.Unreachable
        End Function
 
        Protected Shared Function FindSymbolInSymbolArray(Of TSymbol As Symbol)(
                                            name As String, symbols As ImmutableArray(Of TSymbol)) As ImmutableArray(Of Symbol)
 
            If Not symbols.IsEmpty Then
                For Each p In symbols
                    If IdentifierComparison.Equals(name, p.Name) Then
                        Return ImmutableArray.Create(Of Symbol)(p)
                    End If
                Next
            End If
 
            Return ImmutableArray(Of Symbol).Empty
        End Function
 
        Friend Overrides Function BinderSpecificLookupOptions(options As LookupOptions) As LookupOptions
            Return ContainingBinder.BinderSpecificLookupOptions(options) Or LookupOptions.UseBaseReferenceAccessibility
        End Function
 
        ''' <summary>
        ''' Removes from symbol collection overridden methods or properties
        ''' </summary>
        Protected Shared Sub RemoveOverriddenMethodsAndProperties(symbols As ArrayBuilder(Of Symbol))
            If symbols Is Nothing OrElse symbols.Count < 2 Then
                Return
            End If
 
            ' Do we have any method or property?
            Dim originalDef2Symbol As Dictionary(Of Symbol, Integer) = Nothing
            For i = 0 To symbols.Count - 1
                Dim sym As Symbol = symbols(i)
                Select Case sym.Kind
                    Case SymbolKind.Method, SymbolKind.Property
                        If originalDef2Symbol Is Nothing Then
                            originalDef2Symbol = New Dictionary(Of Symbol, Integer)()
                        End If
                        originalDef2Symbol.Add(sym.OriginalDefinition, i)
                End Select
            Next
 
            If originalDef2Symbol Is Nothing Then
                Return
            End If
 
            ' Do we need to remove any?
            Dim indices2remove As ArrayBuilder(Of Integer) = Nothing
            For i = 0 To symbols.Count - 1
                Dim index As Integer = -1
                Dim sym As Symbol = symbols(i)
 
                Select Case sym.Kind
 
                    Case SymbolKind.Method
                        ' Remove overridden methods
                        Dim method = DirectCast(sym.OriginalDefinition, MethodSymbol)
                        While True
                            method = method.OverriddenMethod
                            If method Is Nothing Then
                                Exit While
                            End If
 
                            If originalDef2Symbol.TryGetValue(method, index) Then
                                If indices2remove Is Nothing Then
                                    indices2remove = ArrayBuilder(Of Integer).GetInstance
                                End If
                                indices2remove.Add(index)
                            End If
                        End While
 
                    Case SymbolKind.Property
                        ' Remove overridden properties
                        Dim prop = DirectCast(sym.OriginalDefinition, PropertySymbol)
                        While True
                            prop = prop.OverriddenProperty
                            If prop Is Nothing Then
                                Exit While
                            End If
 
                            If originalDef2Symbol.TryGetValue(prop, index) Then
                                If indices2remove Is Nothing Then
                                    indices2remove = ArrayBuilder(Of Integer).GetInstance
                                End If
                                indices2remove.Add(index)
                            End If
                        End While
 
                End Select
            Next
 
            If indices2remove Is Nothing Then
                Return
            End If
 
            ' remove elements by indices from 'indices2remove'
            For i = 0 To indices2remove.Count - 1
                symbols(indices2remove(i)) = Nothing
            Next
 
            Dim target As Integer = 0
            For source = 0 To symbols.Count - 1
                Dim sym As Symbol = symbols(source)
                If sym IsNot Nothing Then
                    symbols(target) = sym
                    target += 1
                End If
            Next
            symbols.Clip(target)
 
            indices2remove.Free()
        End Sub
 
    End Class
 
End Namespace