File: Binding\BinderFactory.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.Concurrent
Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    ''' <summary>
    ''' The <see cref="BinderFactory"/> class finds the correct Binder to use for a node in a syntax
    ''' tree, down to method level. Within a method, the <see cref="ExecutableCodeBinder"/> has a
    ''' cache of further binders within the method.
    ''' 
    ''' The <see cref="BinderFactory"/> caches results so that binders are efficiently reused between queries.
    ''' </summary>
    Partial Friend Class BinderFactory
        Private ReadOnly _sourceModule As SourceModuleSymbol
        Private ReadOnly _tree As SyntaxTree
 
        ' Some syntax nodes, like method blocks, have multiple different binders associated with them
        ' for different parts of the method (the body, where parameters are available, and the header, where
        ' parameters aren't). To be able to cache a few different binders for each syntax node, we use
        ' a NodeUsage to sub-distinguish binders associated with nodes. Each kind of syntax node must have its
        ' associated usage value(s), because the usage is used when creating the binder (if not found in the cache).
        Private ReadOnly _cache As ConcurrentDictionary(Of ValueTuple(Of VisualBasicSyntaxNode, Byte), Binder)
 
        Private Shared ReadOnly s_binderFactoryVisitorPool As ObjectPool(Of BinderFactoryVisitor) = New ObjectPool(Of BinderFactoryVisitor)(Function() New BinderFactoryVisitor())
        Private ReadOnly _binderFactoryVisitorPool As ObjectPool(Of BinderFactoryVisitor)
 
        Private ReadOnly Property InScript As Boolean
            Get
                Return _tree.Options.Kind = SourceCodeKind.Script
            End Get
        End Property
 
        Public Sub New(sourceModule As SourceModuleSymbol, tree As SyntaxTree, Optional binderFactoryVisitorPoolOpt As ObjectPool(Of BinderFactoryVisitor) = Nothing)
            Me._sourceModule = sourceModule
            Me._tree = tree
            Me._cache = New ConcurrentDictionary(Of ValueTuple(Of VisualBasicSyntaxNode, Byte), Binder)
            Me._binderFactoryVisitorPool = If(binderFactoryVisitorPoolOpt, s_binderFactoryVisitorPool)
        End Sub
 
        Private Function MakeBinder(node As SyntaxNode, position As Integer) As Binder
            If SyntaxFacts.InSpanOrEffectiveTrailingOfNode(node, position) OrElse
               node.Kind = SyntaxKind.CompilationUnit Then
 
                Dim visitor = GetBinderFactoryVisitor(position)
                Dim result = visitor.Visit(node)
                ClearBinderFactoryVisitor(visitor)
                Return result
            End If
 
            Return Nothing
        End Function
 
        Private Function GetBinderFactoryVisitor(position As Integer) As BinderFactoryVisitor
            Dim visitor = _binderFactoryVisitorPool.Allocate()
            visitor.Initialize(Me, position)
 
            Return visitor
        End Function
 
        Private Sub ClearBinderFactoryVisitor(visitor As BinderFactoryVisitor)
            visitor.Clear()
            _binderFactoryVisitorPool.Free(visitor)
        End Sub
 
        ' Get binder for interior of a namespace block
        Public Function GetNamespaceBinder(node As NamespaceBlockSyntax) As Binder
            Return GetBinderForNodeAndUsage(node, NodeUsage.NamespaceBlockInterior, node.Parent, node.SpanStart)
        End Function
 
        ' Get binder for a type
        Public Function GetNamedTypeBinder(node As TypeStatementSyntax) As Binder
            Dim possibleParentBlock = TryCast(node.Parent, TypeBlockSyntax)
            Dim parentForEnclosingBinder As VisualBasicSyntaxNode = If(possibleParentBlock IsNot Nothing, possibleParentBlock.Parent, node.Parent)
 
            Return GetBinderForNodeAndUsage(node, NodeUsage.TypeBlockFull, parentForEnclosingBinder, node.SpanStart)
        End Function
 
        ' Get binder for an enum
        Public Function GetNamedTypeBinder(node As EnumStatementSyntax) As Binder
            Dim possibleParentBlock = TryCast(node.Parent, EnumBlockSyntax)
            Dim parentForEnclosingBinder As VisualBasicSyntaxNode = If(possibleParentBlock IsNot Nothing, possibleParentBlock.Parent, node.Parent)
 
            Return GetBinderForNodeAndUsage(node, NodeUsage.EnumBlockFull, parentForEnclosingBinder, node.SpanStart)
        End Function
 
        ' Get binder for a delegate
        Public Function GetNamedTypeBinder(node As DelegateStatementSyntax) As Binder
            Return GetBinderForNodeAndUsage(node, NodeUsage.DelegateDeclaration, node.Parent, node.SpanStart)
        End Function
 
        ' Find the binder to use for a position in the tree. The position should have been adjusted
        ' already to be at the start of a token.
        Public Function GetBinderForPosition(node As SyntaxNode, position As Integer) As Binder
            Return GetBinderAtOrAbove(node, position)
        End Function
 
        ' Find the binder for a node or above at a given position
        Private Function GetBinderAtOrAbove(node As SyntaxNode, position As Integer) As Binder
            ' Go up the tree until we find a node that has a corresponding binder.
            Do
                Dim binder As Binder = MakeBinder(node, position)
                If binder IsNot Nothing Then
                    Return binder
                End If
 
                If node.Kind = SyntaxKind.DocumentationCommentTrivia Then
                    node = DirectCast(DirectCast(node, StructuredTriviaSyntax).ParentTrivia.Token.Parent, VisualBasicSyntaxNode)
                Else
                    node = node.Parent
                End If
 
                ' We should always find a binder, because the compilation unit should always give a binder,
                ' and going up the parent node chain should always get us a compilation unit.
                Debug.Assert(node IsNot Nothing, "We should always get a binder")
            Loop
        End Function
 
        ' Given a node and usage, find the correct binder to use. Use the cache first, if not in the cache, then
        ' create a new binder. The parent node and position are used if we need to find an enclosing binder unless specified explicitly.
        Private Function GetBinderForNodeAndUsage(node As VisualBasicSyntaxNode,
                                                  usage As NodeUsage,
                                                  Optional parentNode As VisualBasicSyntaxNode = Nothing,
                                                  Optional position As Integer = -1,
                                                  Optional containingBinder As Binder = Nothing) As Binder
 
            ' either parentNode and position is specified or the containingBinder is specified
            Debug.Assert((parentNode Is Nothing) = (position < 0))
            Debug.Assert(containingBinder Is Nothing OrElse parentNode Is Nothing)
 
            Dim binder As Binder = Nothing
            Dim nodeUsagePair = (node, CByte(usage))
 
            If Not _cache.TryGetValue(nodeUsagePair, binder) Then
                ' Didn't find it in the cache, so we need to create it. But we need the containing binder first.
                If containingBinder Is Nothing AndAlso parentNode IsNot Nothing Then
                    containingBinder = GetBinderAtOrAbove(parentNode, position)
                End If
 
                binder = CreateBinderForNodeAndUsage(node, usage, containingBinder)
                _cache.TryAdd(nodeUsagePair, binder)
            End If
 
            Return binder
        End Function
 
        ' Given a node, usage, and containing binder, create the binder for this node and usage. This is called
        ' only when we've found the given node & usage are not cached (and the caller will cache the result).
        Private Function CreateBinderForNodeAndUsage(node As VisualBasicSyntaxNode,
                                                     usage As NodeUsage,
                                                     containingBinder As Binder) As Binder
            Select Case usage
                Case NodeUsage.CompilationUnit
                    ' Get the binder associated with the default project namespace
                    Return BinderBuilder.CreateBinderForNamespace(_sourceModule, _tree, _sourceModule.RootNamespace)
 
                Case NodeUsage.ImplicitClass
                    Dim implicitType As NamedTypeSymbol
                    If node.Kind <> SyntaxKind.CompilationUnit OrElse _tree.Options.Kind = SourceCodeKind.Regular Then
                        implicitType = DirectCast(containingBinder.ContainingNamespaceOrType.GetMembers(TypeSymbol.ImplicitTypeName).Single(), NamedTypeSymbol)
                    Else
                        implicitType = _sourceModule.ContainingSourceAssembly.DeclaringCompilation.SourceScriptClass
                    End If
                    Return New NamedTypeBinder(containingBinder, implicitType)
 
                Case NodeUsage.ScriptCompilationUnit
                    Dim rootNamespaceBinder = GetBinderForNodeAndUsage(node, NodeUsage.CompilationUnit)
                    Debug.Assert(TypeOf rootNamespaceBinder Is NamespaceBinder)
 
                    ' TODO (tomat): this is just a simple temporary solution, we'll need to plug-in submissions, interactive imports and host object members:
                    Return New NamedTypeBinder(rootNamespaceBinder, _sourceModule.ContainingSourceAssembly.DeclaringCompilation.SourceScriptClass)
 
                Case NodeUsage.TopLevelExecutableStatement
                    Debug.Assert(TypeOf containingBinder Is NamedTypeBinder AndAlso containingBinder.ContainingType.IsScriptClass)
                    Return New TopLevelCodeBinder(containingBinder.ContainingType.InstanceConstructors.Single(), containingBinder)
 
                Case NodeUsage.ImportsStatement
                    Return BinderBuilder.CreateBinderForSourceFileImports(_sourceModule, _tree)
 
                Case NodeUsage.NamespaceBlockInterior
                    Dim nsBlockSyntax = DirectCast(node, NamespaceBlockSyntax)
 
                    Dim containingNamespaceBinder = TryCast(containingBinder, NamespaceBinder)
                    If containingNamespaceBinder Is Nothing Then
                        ' If the containing binder is a script class binder use its namespace as a containing binder.
                        ' It is an error 
                        Dim containingNamedTypeBinder = TryCast(containingBinder, NamedTypeBinder)
                        If containingNamedTypeBinder IsNot Nothing AndAlso containingNamedTypeBinder.ContainingType.IsScriptClass Then
                            Dim rootNamespaceBinder = GetBinderForNodeAndUsage(node, NodeUsage.CompilationUnit)
                            containingNamespaceBinder = DirectCast(rootNamespaceBinder, NamespaceBinder)
                        End If
                    End If
 
                    If containingNamespaceBinder IsNot Nothing Then
                        Return BuildNamespaceBinder(containingNamespaceBinder, nsBlockSyntax.NamespaceStatement.Name, nsBlockSyntax.Parent.Kind = SyntaxKind.CompilationUnit)
                    End If
 
                    Return containingBinder   ' This occurs is some edge case error, like declaring a namespace inside a class.
 
                Case NodeUsage.TypeBlockFull
                    Dim declarationSyntax = DirectCast(node, TypeStatementSyntax)
 
                    Dim symbol = SourceNamedTypeSymbol.FindSymbolFromSyntax(declarationSyntax,
                                                                            containingBinder.ContainingNamespaceOrType,
                                                                            _sourceModule)
 
                    '  if symbol is invalid, we might be dealing with different error cases like class/namespace/class declaration
                    Return If(symbol IsNot Nothing, New NamedTypeBinder(containingBinder, symbol), containingBinder)
 
                Case NodeUsage.EnumBlockFull
                    Dim declarationSyntax = DirectCast(node, EnumStatementSyntax)
 
                    Dim symbol = SourceNamedTypeSymbol.FindSymbolFromSyntax(declarationSyntax,
                                                                            containingBinder.ContainingNamespaceOrType,
                                                                            _sourceModule)
 
                    '  if symbol is invalid, we might be dealing with different error cases like class/namespace/class declaration
                    Return If(symbol IsNot Nothing, New NamedTypeBinder(containingBinder, symbol), containingBinder)
 
                Case NodeUsage.DelegateDeclaration
                    Dim delegateSyntax = DirectCast(node, DelegateStatementSyntax)
 
                    Dim symbol = SourceNamedTypeSymbol.FindSymbolFromSyntax(delegateSyntax,
                                                                            containingBinder.ContainingNamespaceOrType,
                                                                            _sourceModule)
 
                    '  if symbol is invalid, we might be dealing with different error cases like class/namespace/class declaration
                    Return If(symbol IsNot Nothing, New NamedTypeBinder(containingBinder, symbol), containingBinder)
 
                Case NodeUsage.InheritsStatement
                    Dim containingNamedTypeBinder = TryCast(containingBinder, NamedTypeBinder)
 
                    If containingNamedTypeBinder IsNot Nothing Then
                        ' When binding the inherits clause, we don't want to look to base types of our own type, to follow how actual
                        ' determination of the base type is done. This is done by using a BasesBeingResolvedBinder.
                        Debug.Assert(containingNamedTypeBinder.ContainingType IsNot Nothing)
                        Return New BasesBeingResolvedBinder(containingBinder, BasesBeingResolved.Empty.PrependInheritsBeingResolved(containingNamedTypeBinder.ContainingType))
                    Else
                        Return containingBinder
                    End If
 
                Case NodeUsage.ImplementsStatement
                    Dim containingNamedTypeBinder = TryCast(containingBinder, NamedTypeBinder)
 
                    If containingNamedTypeBinder IsNot Nothing Then
                        Debug.Assert(containingNamedTypeBinder.ContainingType IsNot Nothing)
                        Return New BasesBeingResolvedBinder(containingBinder, BasesBeingResolved.Empty.PrependImplementsBeingResolved(containingNamedTypeBinder.ContainingType))
                    Else
                        Return containingBinder
                    End If
 
                Case NodeUsage.PropertyFull
                    Return GetContainingNamedTypeBinderForMemberNode(DirectCast(node, PropertyStatementSyntax).Parent.Parent, containingBinder)
 
                Case NodeUsage.MethodFull, NodeUsage.MethodInterior
                    Dim methodBase = DirectCast(node, MethodBaseSyntax)
 
                    Dim containingNamedTypeBinder = GetContainingNamedTypeBinderForMemberNode(node.Parent.Parent, containingBinder)
                    If containingNamedTypeBinder Is Nothing Then
                        Return containingBinder
                    End If
 
                    ' UNDONE: Remove this once we can create MethodSymbols for other kinds of declarations.
                    Select Case methodBase.Kind
                        Case SyntaxKind.FunctionStatement,
                            SyntaxKind.SubStatement,
                            SyntaxKind.SubNewStatement,
                            SyntaxKind.GetAccessorStatement,
                            SyntaxKind.SetAccessorStatement,
                            SyntaxKind.AddHandlerAccessorStatement,
                            SyntaxKind.RemoveHandlerAccessorStatement,
                            SyntaxKind.RaiseEventAccessorStatement,
                            SyntaxKind.OperatorStatement
                        Case Else
                            Return containingBinder
                    End Select
 
                    Return BuildMethodBinder(containingNamedTypeBinder, methodBase, (usage = NodeUsage.MethodInterior))
 
                Case NodeUsage.FieldOrPropertyInitializer
                    Dim fieldOrProperty As Symbol = Nothing
                    Dim additionalFieldsOrProperties = ImmutableArray(Of Symbol).Empty
                    Dim containingNamedTypeBinder As NamedTypeBinder
 
                    Select Case node.Kind
                        Case SyntaxKind.VariableDeclarator
                            Dim declarator = DirectCast(node, VariableDeclaratorSyntax)
                            ' Declaration should have initializer or AsNew.
                            Debug.Assert(declarator.Initializer IsNot Nothing OrElse TryCast(declarator.AsClause, AsNewClauseSyntax) IsNot Nothing)
                            ' more than one name may happen if there is a syntax error or AsNew clause with multiple fields/properties
                            Debug.Assert(declarator.Names.Count > 0)
 
                            containingNamedTypeBinder = GetContainingNamedTypeBinderForMemberNode(node.Parent.Parent, containingBinder)
                            If containingNamedTypeBinder Is Nothing Then
                                Return Nothing
                            End If
 
                            Dim identifier = declarator.Names(0).Identifier
                            fieldOrProperty = containingNamedTypeBinder.ContainingType.FindFieldOrProperty(identifier.ValueText, identifier.Span, _tree)
 
                            ' Handle multiple fields/properties initialized with AsNew clause
                            If declarator.Names.Count > 1 Then
                                Dim builder = ArrayBuilder(Of Symbol).GetInstance
                                For Each name In declarator.Names.Skip(1)
                                    identifier = name.Identifier
                                    Dim additionalFieldOrProperty As Symbol = containingNamedTypeBinder.ContainingType.FindFieldOrProperty(identifier.ValueText, identifier.Span, _tree)
                                    builder.Add(additionalFieldOrProperty)
                                Next
                                additionalFieldsOrProperties = builder.ToImmutableAndFree
                            End If
 
                        Case SyntaxKind.EnumMemberDeclaration
                            Dim enumDeclaration = DirectCast(node, EnumMemberDeclarationSyntax)
                            Debug.Assert(enumDeclaration.Initializer IsNot Nothing)
 
                            containingNamedTypeBinder = DirectCast(containingBinder, NamedTypeBinder)
 
                            Dim identifier = enumDeclaration.Identifier
                            fieldOrProperty = containingNamedTypeBinder.ContainingType.FindMember(identifier.ValueText, SymbolKind.Field, identifier.Span, _tree)
 
                        Case SyntaxKind.PropertyStatement
                            Dim propertyStatement = DirectCast(node, PropertyStatementSyntax)
                            Debug.Assert(propertyStatement.Initializer IsNot Nothing OrElse TryCast(propertyStatement.AsClause, AsNewClauseSyntax) IsNot Nothing)
 
                            containingNamedTypeBinder = GetContainingNamedTypeBinderForMemberNode(node.Parent, containingBinder)
                            If containingNamedTypeBinder Is Nothing Then
                                Return Nothing
                            End If
 
                            Dim identifier = propertyStatement.Identifier
                            fieldOrProperty = containingNamedTypeBinder.ContainingType.FindMember(identifier.ValueText, SymbolKind.Property, identifier.Span, _tree)
 
                        Case Else
                            Throw ExceptionUtilities.UnexpectedValue(node.Kind)
 
                    End Select
 
                    If fieldOrProperty IsNot Nothing Then
                        Return BuildInitializerBinder(containingNamedTypeBinder, fieldOrProperty, additionalFieldsOrProperties)
                    End If
 
                    Return Nothing
 
                Case NodeUsage.FieldArrayBounds
                    Dim modifiedIdentifier = DirectCast(node, ModifiedIdentifierSyntax)
                    Dim containingNamedTypeBinder = TryCast(containingBinder, NamedTypeBinder)
 
                    If containingNamedTypeBinder IsNot Nothing Then
                        Dim containingType = containingNamedTypeBinder.ContainingType
                        Dim identifier = modifiedIdentifier.Identifier
                        Dim field = containingType.FindMember(identifier.ValueText, SymbolKind.Field, identifier.Span, _tree)
                        If field IsNot Nothing Then
                            Return BuildInitializerBinder(containingNamedTypeBinder, field, ImmutableArray(Of Symbol).Empty)
                        End If
                    End If
 
                    Return Nothing
 
                Case NodeUsage.Attribute
                    Return BuildAttributeBinder(containingBinder, node)
 
                Case NodeUsage.ParameterDefaultValue
                    Dim parameterSyntax = DirectCast(node, ParameterSyntax)
 
                    If parameterSyntax.Default IsNot Nothing Then
                        Dim parameterListSyntax = DirectCast(parameterSyntax.Parent, ParameterListSyntax)
                        Dim methodSyntax = DirectCast(parameterListSyntax.Parent, MethodBaseSyntax)
                        Dim parameterSymbol As ParameterSymbol = Nothing
 
                        Select Case methodSyntax.Kind
                            Case SyntaxKind.SubNewStatement,
                                SyntaxKind.FunctionStatement,
                                SyntaxKind.OperatorStatement,
                                SyntaxKind.SubStatement,
                                SyntaxKind.DeclareFunctionStatement,
                                SyntaxKind.DeclareSubStatement
                                Dim containingType = GetParameterDeclarationContainingType(containingBinder)
                                If containingType IsNot Nothing Then
                                    Dim methodSymbol = DirectCast(SourceMethodSymbol.FindSymbolFromSyntax(methodSyntax, _tree, containingType), SourceMethodSymbol)
                                    If methodSymbol IsNot Nothing Then
                                        parameterSymbol = GetParameterSymbol(methodSymbol.Parameters, parameterSyntax)
                                    End If
                                End If
 
                            Case SyntaxKind.DelegateFunctionStatement,
                                SyntaxKind.DelegateSubStatement
                                Dim containingType = GetParameterDeclarationContainingType(containingBinder)
                                If containingType IsNot Nothing AndAlso
                                    containingType.TypeKind = TypeKind.Delegate Then
                                    Dim invokeSymbol = containingType.DelegateInvokeMethod
                                    Debug.Assert(invokeSymbol IsNot Nothing, "Delegate should always have an invoke method.")
                                    parameterSymbol = GetParameterSymbol(invokeSymbol.Parameters, parameterSyntax)
                                End If
 
                            Case SyntaxKind.EventStatement
                                Dim containingType = GetParameterDeclarationContainingType(containingBinder)
                                If containingType IsNot Nothing Then
                                    Dim eventSymbol = DirectCast(SourceMethodSymbol.FindSymbolFromSyntax(methodSyntax, _tree, containingType), SourceEventSymbol)
                                    If eventSymbol IsNot Nothing Then
                                        parameterSymbol = GetParameterSymbol(eventSymbol.DelegateParameters, parameterSyntax)
                                    End If
                                End If
 
                            Case SyntaxKind.PropertyStatement
                                Dim containingType = GetParameterDeclarationContainingType(containingBinder)
                                If containingType IsNot Nothing Then
                                    Dim propertySymbol = DirectCast(SourceMethodSymbol.FindSymbolFromSyntax(methodSyntax, _tree, containingType), SourcePropertySymbol)
                                    If propertySymbol IsNot Nothing Then
                                        parameterSymbol = GetParameterSymbol(propertySymbol.Parameters, parameterSyntax)
                                    End If
                                End If
 
                            Case SyntaxKind.FunctionLambdaHeader,
                                 SyntaxKind.SubLambdaHeader,
                                 SyntaxKind.SetAccessorStatement,
                                 SyntaxKind.GetAccessorStatement,
                                 SyntaxKind.AddHandlerAccessorStatement,
                                 SyntaxKind.RemoveHandlerAccessorStatement,
                                 SyntaxKind.RaiseEventAccessorStatement
                                ' Default values are not valid (and not bound) for lambda parameters or property accessors
                                Return Nothing
 
                            Case Else
                                Throw ExceptionUtilities.UnexpectedValue(methodSyntax.Kind)
 
                        End Select
 
                        If parameterSymbol IsNot Nothing Then
                            Return BinderBuilder.CreateBinderForParameterDefaultValue(parameterSymbol, containingBinder, parameterSyntax)
                        End If
                    End If
 
                    Return Nothing
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(usage)
            End Select
        End Function
 
        Private Function CreateDocumentationCommentBinder(node As DocumentationCommentTriviaSyntax, binderType As DocumentationCommentBinder.BinderType) As Binder
            Debug.Assert(binderType <> DocumentationCommentBinder.BinderType.None)
 
            ' Now we need to find a symbol for class/structure, method, event, or property
            ' Those may be needed to bind parameters and/or type parameters
            ' Note that we actually don't need field/module/enum symbols, because they 
            ' do not have type parameters or parameters
            Dim trivia As SyntaxTrivia = node.ParentTrivia
            Dim token As SyntaxToken = CType(trivia.Token, SyntaxToken)
            Dim parent = DirectCast(token.Parent, VisualBasicSyntaxNode)
            Debug.Assert(parent IsNot Nothing)
 
            ' This is a binder for commented symbol's containing type or namespace 
            Dim nodeForOuterBinder As VisualBasicSyntaxNode = Nothing
lAgain:
            Select Case parent.Kind
                Case SyntaxKind.ClassStatement,
                     SyntaxKind.EnumStatement,
                     SyntaxKind.InterfaceStatement,
                     SyntaxKind.StructureStatement,
                     SyntaxKind.ModuleStatement
 
                    ' BREAK: Roslyn uses the type binder, whereas Dev11 uses the scope strictly above the type declaration.
                    ' This change was made to improve the consistency with C#.  In particular, it allows unqualified references
                    ' to members of the type to which the doc comment has been applied.
                    nodeForOuterBinder = parent
 
                Case SyntaxKind.SubStatement,
                     SyntaxKind.SubNewStatement,
                     SyntaxKind.FunctionStatement,
                     SyntaxKind.DelegateSubStatement,
                     SyntaxKind.DelegateFunctionStatement,
                     SyntaxKind.DeclareSubStatement,
                     SyntaxKind.DeclareFunctionStatement,
                     SyntaxKind.OperatorStatement
 
                    ' Delegates don't have user-defined members, so it makes more sense to treat
                    ' them like methods.
 
                    nodeForOuterBinder = parent.Parent
                    If nodeForOuterBinder IsNot Nothing AndAlso TypeOf (nodeForOuterBinder) Is MethodBlockBaseSyntax Then
                        nodeForOuterBinder = nodeForOuterBinder.Parent
                    End If
 
                Case SyntaxKind.PropertyStatement
                    nodeForOuterBinder = parent.Parent
                    If nodeForOuterBinder IsNot Nothing AndAlso nodeForOuterBinder.Kind = SyntaxKind.PropertyBlock Then
                        nodeForOuterBinder = nodeForOuterBinder.Parent
                    End If
 
                Case SyntaxKind.EventStatement
                    nodeForOuterBinder = parent.Parent
                    If nodeForOuterBinder IsNot Nothing AndAlso nodeForOuterBinder.Kind = SyntaxKind.EventStatement Then
                        nodeForOuterBinder = nodeForOuterBinder.Parent
                    End If
 
                Case SyntaxKind.FieldDeclaration,
                     SyntaxKind.EnumMemberDeclaration
 
                    nodeForOuterBinder = parent.Parent
 
                Case SyntaxKind.AttributeList
                    nodeForOuterBinder = parent.Parent
                    If nodeForOuterBinder IsNot Nothing Then
                        parent = nodeForOuterBinder
                        nodeForOuterBinder = Nothing
                        GoTo lAgain
                    End If
 
            End Select
 
            If nodeForOuterBinder Is Nothing Then
                Return GetBinderAtOrAbove(parent, parent.SpanStart)
            End If
 
            Dim containingBinder As Binder = GetBinderAtOrAbove(nodeForOuterBinder, parent.SpanStart)
            Dim symbol As Symbol = Nothing
 
            Select Case parent.Kind
                Case SyntaxKind.ClassStatement,
                     SyntaxKind.InterfaceStatement,
                     SyntaxKind.StructureStatement
 
                    symbol = containingBinder.ContainingNamespaceOrType
 
                Case SyntaxKind.SubStatement,
                     SyntaxKind.SubNewStatement,
                     SyntaxKind.FunctionStatement,
                     SyntaxKind.DeclareSubStatement,
                     SyntaxKind.DeclareFunctionStatement,
                     SyntaxKind.OperatorStatement,
                     SyntaxKind.PropertyStatement,
                     SyntaxKind.EventStatement
 
                    If containingBinder.ContainingType IsNot Nothing Then
                        symbol = SourceMethodSymbol.FindSymbolFromSyntax(
                            DirectCast(parent, MethodBaseSyntax), _tree, containingBinder.ContainingType)
                    End If
 
                Case SyntaxKind.DelegateSubStatement,
                     SyntaxKind.DelegateFunctionStatement
 
                    If containingBinder.ContainingType IsNot Nothing Then
                        symbol = SourceMethodSymbol.FindSymbolFromSyntax(
                            DirectCast(parent, MethodBaseSyntax), _tree, containingBinder.ContainingType)
                    Else
                        symbol = SourceNamedTypeSymbol.FindSymbolFromSyntax(
                            DirectCast(parent, DelegateStatementSyntax), containingBinder.ContainingNamespaceOrType, _sourceModule)
                    End If
 
                Case SyntaxKind.FieldDeclaration,
                     SyntaxKind.EnumStatement,
                     SyntaxKind.EnumMemberDeclaration,
                     SyntaxKind.ModuleStatement
 
                    ' we are not using field, enum, module symbols for params or type params resolution
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(parent.Kind)
            End Select
 
            Return BinderBuilder.CreateBinderForDocumentationComment(containingBinder, symbol, binderType)
        End Function
 
        Private Function GetContainingNamedTypeBinderForMemberNode(node As VisualBasicSyntaxNode, containingBinder As Binder) As NamedTypeBinder
            Dim containingNamedTypeBinder = TryCast(containingBinder, NamedTypeBinder)
            If containingNamedTypeBinder IsNot Nothing Then
                Return containingNamedTypeBinder
            End If
 
            ' member declared on top-level or in a namespace is enclosed in an implicit type:
            If node IsNot Nothing AndAlso (node.Kind = SyntaxKind.NamespaceBlock OrElse node.Kind = SyntaxKind.CompilationUnit) Then
                Return DirectCast(
                    GetBinderForNodeAndUsage(node, NodeUsage.ImplicitClass,
                                             containingBinder:=containingBinder), NamedTypeBinder)
            End If
 
            Return Nothing
        End Function
 
        Private Shared Function GetParameterDeclarationContainingType(containingBinder As Binder) As NamedTypeSymbol
            ' Method declarations are bound using either a NamedTypeBinder or a MethodTypeParametersBinder
            Dim namedTypeBinder = TryCast(containingBinder, NamedTypeBinder)
            If namedTypeBinder Is Nothing Then
                ' Must be a MethodTypeParametersBinder unless the member
                ' containing the parameter is outside of a type (in invalid code).
                Dim methodDeclarationBinder = TryCast(containingBinder, MethodTypeParametersBinder)
                If methodDeclarationBinder Is Nothing Then
                    Return Nothing
                End If
 
                namedTypeBinder = DirectCast(methodDeclarationBinder.ContainingBinder, NamedTypeBinder)
            End If
 
            Return namedTypeBinder.ContainingType
        End Function
 
        ' Given the name of a child namespace, build up a binder from a containing binder.
        Private Function BuildNamespaceBinder(containingBinder As NamespaceBinder, childName As NameSyntax, globalNamespaceAllowed As Boolean) As NamespaceBinder
            Dim name As String
 
            Select Case childName.Kind
                Case SyntaxKind.GlobalName
                    If globalNamespaceAllowed Then
                        Return DirectCast(BinderBuilder.CreateBinderForNamespace(_sourceModule, _tree, _sourceModule.GlobalNamespace), NamespaceBinder)
                    Else
                        ' Global namespace isn't allowed here. Use a namespace named "Global" as error recovery (see corresponding code in DeclarationTreeBuilder)
                        name = "Global"
                    End If
                Case SyntaxKind.QualifiedName
                    Dim dotted = DirectCast(childName, QualifiedNameSyntax)
                    containingBinder = BuildNamespaceBinder(containingBinder, dotted.Left, globalNamespaceAllowed)
                    name = dotted.Right.Identifier.ValueText
                Case SyntaxKind.IdentifierName
                    name = DirectCast(childName, IdentifierNameSyntax).Identifier.ValueText
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(childName.Kind)
            End Select
 
            For Each symbol As NamespaceOrTypeSymbol In containingBinder.NamespaceSymbol.GetMembers(name)
                Dim nsChild = TryCast(symbol, NamespaceSymbol)
 
                If nsChild IsNot Nothing Then
                    Return New NamespaceBinder(containingBinder, nsChild)
                End If
            Next
 
            ' Namespace is expected to be found by name in parent binder.
            Throw ExceptionUtilities.Unreachable
        End Function
 
        ' Given the name of a method, and the span of the name, and the containing type binder, get the 
        ' binder for the method. We just search all the method symbols with the given name.
        Private Function BuildMethodBinder(containingBinder As NamedTypeBinder,
                                            methodSyntax As MethodBaseSyntax,
                                            forBody As Boolean) As Binder
            Dim containingType = containingBinder.ContainingType
 
            Dim symbol = SourceMethodSymbol.FindSymbolFromSyntax(methodSyntax, _tree, containingType)
 
            If (symbol IsNot Nothing) AndAlso
                (symbol.Kind = SymbolKind.Method) Then
                Dim methodSymbol = DirectCast(symbol, SourceMethodSymbol)
                If forBody Then
                    Return BinderBuilder.CreateBinderForMethodBody(methodSymbol, methodSymbol.Syntax, containingBinder)
                Else
                    Return BinderBuilder.CreateBinderForMethodDeclaration(methodSymbol, containingBinder)
                End If
            Else
                ' Not sure if there's a case where we get here. For now, fail. Maybe if a declarations
                ' is so malformed there isn't a symbol for it?
 
                ' This can happen if property has multiple accessors. 
                ' Parser allows multiple accessors, but binder will accept only one of a kind
                Return containingBinder
            End If
        End Function
 
        Private Function BuildInitializerBinder(containingBinder As Binder, fieldOrProperty As Symbol, additionalFieldsOrProperties As ImmutableArray(Of Symbol)) As Binder
            Return BinderBuilder.CreateBinderForInitializer(containingBinder, fieldOrProperty, additionalFieldsOrProperties)
        End Function
 
        Private Function BuildAttributeBinder(containingBinder As Binder, node As VisualBasicSyntaxNode) As Binder
            Debug.Assert(node.Kind = SyntaxKind.Attribute)
 
            If containingBinder IsNot Nothing AndAlso node.Parent IsNot Nothing Then
 
                ' Go to attribute block
                Dim attributeBlock = node.Parent
 
                ' Go to statement that owns the attribute
                If attributeBlock.Parent IsNot Nothing Then
 
                    Select Case attributeBlock.Parent.Kind
                        Case SyntaxKind.ClassStatement, SyntaxKind.ModuleStatement, SyntaxKind.StructureStatement, SyntaxKind.InterfaceStatement, SyntaxKind.EnumStatement
                            ' Attributes on a class, module, structure, interface, or enum are contained within those nodes.  However, the members of those
                            ' blocks should not be in scope when evaluating expressions within the attribute. Therefore, remove the named type binder from 
                            ' the binder hierarchy.
                            If TypeOf containingBinder Is NamedTypeBinder Then
                                containingBinder = containingBinder.ContainingBinder
                            End If
                    End Select
 
                End If
 
            End If
 
            Return BinderBuilder.CreateBinderForAttribute(_tree, containingBinder, node)
        End Function
 
    End Class
End Namespace