' 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.Generic
Imports System.Collections.Immutable
Imports System.Collections.ObjectModel
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Operations
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind

Namespace Microsoft.CodeAnalysis.VisualBasic

    ''' <summary>
    ''' Allows asking semantic questions about any node in a SyntaxTree within a Compilation.
    ''' </summary>
    Friend Class SyntaxTreeSemanticModel
        Inherits PublicSemanticModel

        Private ReadOnly _compilation As VisualBasicCompilation
        Private ReadOnly _sourceModule As SourceModuleSymbol
        Private ReadOnly _syntaxTree As SyntaxTree
        Private ReadOnly _binderFactory As BinderFactory
        Private ReadOnly _ignoresAccessibility As Boolean

        ' maps from a higher-level binder to an appropriate SemanticModel for the construct (such as a method, or initializer).
        Private ReadOnly _semanticModelCache As New ConcurrentDictionary(Of Binder, MemberSemanticModel)()

        Friend Sub New(compilation As VisualBasicCompilation, sourceModule As SourceModuleSymbol, syntaxTree As SyntaxTree, ignoreAccessibility As Boolean)
            _compilation = compilation
            _sourceModule = sourceModule
            _syntaxTree = syntaxTree
            _ignoresAccessibility = ignoreAccessibility
            _binderFactory = New BinderFactory(sourceModule, syntaxTree)
        End Sub

        ''' <summary> 
        ''' The compilation associated with this binding.
        ''' </summary> 
        Public Overrides ReadOnly Property Compilation As VisualBasicCompilation
            Get
                Return _compilation
            End Get
        End Property

        ''' <summary> 
        ''' The root node of the syntax tree that this binding is based on.
        ''' </summary> 
        Friend Overrides ReadOnly Property Root As SyntaxNode
            Get
                Return DirectCast(_syntaxTree.GetRoot(), VisualBasicSyntaxNode)
            End Get
        End Property

        ''' <summary> 
        ''' The SyntaxTree that is bound
        ''' </summary> 
        Public Overrides ReadOnly Property SyntaxTree As SyntaxTree
            Get
                Return Me._syntaxTree
            End Get
        End Property

        ''' <summary>
        ''' Returns true if this Is a SemanticModel that ignores accessibility rules when answering semantic questions.
        ''' </summary>
        Public NotOverridable Overrides ReadOnly Property IgnoresAccessibility As Boolean
            Get
                Return Me._ignoresAccessibility
            End Get
        End Property

        ''' <summary>
        ''' Get all the errors within the syntax tree associated with this object. Includes errors involving compiling
        ''' method bodies or initializers, in addition to the errors returned by GetDeclarationDiagnostics and parse errors.
        ''' </summary>
        ''' <param name="span">Optional span within the syntax tree for which to get diagnostics.
        ''' If no argument is specified, then diagnostics for the entire tree are returned.</param>
        ''' <param name="cancellationToken">A cancellation token that can be used to cancel the process of obtaining the
        ''' diagnostics.</param>
        ''' <remarks>
        ''' Because this method must semantically analyze all method bodies and initializers to check for diagnostics, it may
        ''' take a significant amount of time. Unlike GetDeclarationDiagnostics, diagnostics for method bodies and
        ''' initializers are not cached, the any semantic information used to obtain the diagnostics is discarded.
        ''' </remarks>
        Public Overrides Function GetDiagnostics(Optional span As TextSpan? = Nothing, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Diagnostic)
            Return _compilation.GetDiagnosticsForSyntaxTree(CompilationStage.Compile, _syntaxTree, span, includeEarlierStages:=True, cancellationToken:=cancellationToken)
        End Function

        ''' <summary>
        ''' Get all of the syntax errors within the syntax tree associated with this
        ''' object. Does not get errors involving declarations or compiling method bodies or initializers.
        ''' </summary>
        ''' <param name="span">Optional span within the syntax tree for which to get diagnostics.
        ''' If no argument is specified, then diagnostics for the entire tree are returned.</param>
        ''' <param name="cancellationToken">A cancellation token that can be used to cancel the
        ''' process of obtaining the diagnostics.</param>
        Public Overrides Function GetSyntaxDiagnostics(Optional span As TextSpan? = Nothing, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Diagnostic)
            Return _compilation.GetDiagnosticsForSyntaxTree(CompilationStage.Parse, _syntaxTree, span, includeEarlierStages:=False, cancellationToken:=cancellationToken)
        End Function

        ''' <summary>
        ''' Get all the syntax and declaration errors within the syntax tree associated with this object. Does not get
        ''' errors involving compiling method bodies or initializers.
        ''' </summary>
        ''' <param name="span">Optional span within the syntax tree for which to get diagnostics.
        ''' If no argument is specified, then diagnostics for the entire tree are returned.</param>
        ''' <param name="cancellationToken">A cancellation token that can be used to cancel the process of obtaining the
        ''' diagnostics.</param>
        ''' <remarks>The declaration errors for a syntax tree are cached. The first time this method is called, a ll
        ''' declarations are analyzed for diagnostics. Calling this a second time will return the cached diagnostics.
        ''' </remarks>
        Public Overrides Function GetDeclarationDiagnostics(Optional span As TextSpan? = Nothing, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Diagnostic)
            Return _compilation.GetDiagnosticsForSyntaxTree(CompilationStage.Declare, _syntaxTree, span, includeEarlierStages:=False, cancellationToken:=cancellationToken)
        End Function

        ''' <summary>
        ''' Get all the syntax and declaration errors within the syntax tree associated with this object. Does not get
        ''' errors involving compiling method bodies or initializers.
        ''' </summary>
        ''' <param name="span">Optional span within the syntax tree for which to get diagnostics.
        ''' If no argument is specified, then diagnostics for the entire tree are returned.</param>
        ''' <param name="cancellationToken">A cancellation token that can be used to cancel the process of obtaining the
        ''' diagnostics.</param>
        ''' <remarks>The declaration errors for a syntax tree are cached. The first time this method is called, a ll
        ''' declarations are analyzed for diagnostics. Calling this a second time will return the cached diagnostics.
        ''' </remarks>
        Public Overrides Function GetMethodBodyDiagnostics(Optional span As TextSpan? = Nothing, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Diagnostic)
            Return _compilation.GetDiagnosticsForSyntaxTree(CompilationStage.Compile, _syntaxTree, span, includeEarlierStages:=False, cancellationToken:=cancellationToken)
        End Function

        ' PERF: These variables avoid repeated allocation of Func(Of Binder, MemberSemanticModel) in GetMemberSemanticModel
        Private ReadOnly _methodBodySemanticModelCreator As Func(Of Binder, MemberSemanticModel) = Function(key As Binder) MethodBodySemanticModel.Create(Me, DirectCast(key, SubOrFunctionBodyBinder))
        Private ReadOnly _initializerSemanticModelCreator As Func(Of Binder, MemberSemanticModel) = Function(key As Binder) InitializerSemanticModel.Create(Me, DirectCast(key, DeclarationInitializerBinder))
        Private ReadOnly _attributeSemanticModelCreator As Func(Of Binder, MemberSemanticModel) = Function(key As Binder) AttributeSemanticModel.Create(Me, DirectCast(key, AttributeBinder))

        Public Function GetMemberSemanticModel(binder As Binder) As MemberSemanticModel

            If TypeOf binder Is MethodBodyBinder Then
                Return _semanticModelCache.GetOrAdd(binder, _methodBodySemanticModelCreator)
            End If

            If TypeOf binder Is DeclarationInitializerBinder Then
                Return _semanticModelCache.GetOrAdd(binder, _initializerSemanticModelCreator)
            End If

            If TypeOf binder Is AttributeBinder Then
                Return _semanticModelCache.GetOrAdd(binder, _attributeSemanticModelCreator)
            End If

            If TypeOf binder Is TopLevelCodeBinder Then
                Return _semanticModelCache.GetOrAdd(binder, _methodBodySemanticModelCreator)
            End If

            Return Nothing
        End Function

        Friend Function GetMemberSemanticModel(position As Integer) As MemberSemanticModel
            Dim binder As binder = _binderFactory.GetBinderForPosition(FindInitialNodeFromPosition(position), position)
            Dim model = GetMemberSemanticModel(binder) ' Depends on the runtime type, so don't wrap in a SemanticModelBinder.
            Debug.Assert(model Is Nothing OrElse model.RootBinder.IsSemanticModelBinder)
            Return model
        End Function

        Friend Function GetMemberSemanticModel(node As SyntaxNode) As MemberSemanticModel
            Return GetMemberSemanticModel(node.SpanStart)
        End Function

        Friend Overrides Function GetEnclosingBinder(position As Integer) As Binder
            ' special case if node is from interior of a member declaration (method body, etc)
            Dim model As MemberSemanticModel = GetMemberSemanticModel(position)
            If model IsNot Nothing Then
                ' If the node is from the interior of a member declaration, then we need to go further
                ' to find more nested binders.
                Return model.GetEnclosingBinder(position)
            Else
                Dim binder As binder = _binderFactory.GetBinderForPosition(FindInitialNodeFromPosition(position), position)
                Return SemanticModelBinder.Mark(binder, IgnoresAccessibility)
            End If
        End Function

        Friend Overrides Function GetInvokeSummaryForRaiseEvent(node As RaiseEventStatementSyntax) As BoundNodeSummary
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(node)
            If model IsNot Nothing Then
                Return model.GetInvokeSummaryForRaiseEvent(node)
            Else
                Return Nothing
            End If
        End Function

        Friend Overrides Function GetCrefReferenceSymbolInfo(crefReference As CrefReferenceSyntax, options As VBSemanticModel.SymbolInfoOptions, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
            ValidateSymbolInfoOptions(options)
            Debug.Assert(IsInCrefOrNameAttributeInterior(crefReference))
            Return GetSymbolInfoForCrefOrNameAttributeReference(crefReference, options)
        End Function

        Friend Overrides Function GetExpressionSymbolInfo(node As ExpressionSyntax, options As SymbolInfoOptions, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
            ValidateSymbolInfoOptions(options)

            node = SyntaxFactory.GetStandaloneExpression(DirectCast(node, ExpressionSyntax))

            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(node)
            Dim result As SymbolInfo

            If model IsNot Nothing Then
                result = model.GetExpressionSymbolInfo(node, options, cancellationToken)

                ' If we didn't get anything and were in Type/Namespace only context, let's bind normally and see
                ' if any symbol comes out.
                If result.IsEmpty AndAlso SyntaxFacts.IsInNamespaceOrTypeContext(node) Then
                    Dim tryAsExpression = TryBindNamespaceOrTypeAsExpression(node, options)
                    If Not tryAsExpression.IsEmpty Then
                        result = tryAsExpression
                    End If
                End If
            Else
                ' We don't have a bound tree to examine outside of a member semantic model. Instead, we just
                ' rebind the appropriate syntax as if we were binding it as part of symbol creation.
                '
                ' if expression is not part of a member semantic model then 
                '   a) it may be a reference to a type or namespace name
                '   b) it may be a reference to a interface member in an Implements clause
                '   c) it may be a reference to a field in an Handles clause
                '   d) it may be a reference to an event in an Handles clause

                If SyntaxFacts.IsImplementedMember(node) Then
                    result = GetImplementedMemberSymbolInfo(DirectCast(node, QualifiedNameSyntax), options)
                ElseIf SyntaxFacts.IsHandlesEvent(node) Then
                    result = GetHandlesEventSymbolInfo(DirectCast(node.Parent, HandlesClauseItemSyntax), options)
                ElseIf SyntaxFacts.IsHandlesContainer(node) Then
                    Dim parent = node.Parent
                    If parent.Kind <> SyntaxKind.HandlesClauseItem Then
                        parent = parent.Parent
                    End If

                    result = GetHandlesContainerSymbolInfo(DirectCast(parent, HandlesClauseItemSyntax), options)
                ElseIf SyntaxFacts.IsHandlesProperty(node) Then
                    result = GetHandlesPropertySymbolInfo(DirectCast(node.Parent.Parent, HandlesClauseItemSyntax), options)
                ElseIf IsInCrefOrNameAttributeInterior(node) Then
                    result = GetSymbolInfoForCrefOrNameAttributeReference(node, options)
                ElseIf SyntaxFacts.IsInNamespaceOrTypeContext(node) Then
                    ' Bind the type or namespace name.
                    result = GetTypeOrNamespaceSymbolInfoNotInMember(DirectCast(node, TypeSyntax), options)
                Else
                    result = SymbolInfo.None
                End If
            End If

            Return result
        End Function

        Friend Overrides Function GetCollectionInitializerAddSymbolInfo(collectionInitializer As ObjectCreationExpressionSyntax, node As ExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(collectionInitializer)

            If model IsNot Nothing Then
                Return model.GetCollectionInitializerAddSymbolInfo(collectionInitializer, node, cancellationToken)
            End If

            Return SymbolInfo.None
        End Function

        Private Function TryBindNamespaceOrTypeAsExpression(node As ExpressionSyntax, options As SymbolInfoOptions) As SymbolInfo
            ' Let's bind expression normally and use what comes back as candidate symbols.
            Dim binder As Binder = GetEnclosingBinder(node.SpanStart)

            If binder IsNot Nothing Then
                Dim bound As BoundExpression = binder.BindExpression(node, BindingDiagnosticBag.Discarded)

                Dim newSymbolInfo = GetSymbolInfoForNode(options, New BoundNodeSummary(bound, bound, Nothing), binderOpt:=Nothing)

                Dim allSymbols = newSymbolInfo.GetAllSymbols()
                If Not allSymbols.IsDefaultOrEmpty Then
                    Return SymbolInfoFactory.Create(allSymbols, LookupResultKind.NotATypeOrNamespace)
                End If
            End If

            Return SymbolInfo.None
        End Function

        Friend Overrides Function GetExpressionTypeInfo(node As ExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As VisualBasicTypeInfo
            node = SyntaxFactory.GetStandaloneExpression(DirectCast(node, ExpressionSyntax))

            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(node)

            If model IsNot Nothing Then
                Return model.GetExpressionTypeInfo(node, cancellationToken)
            Else
                ' We don't have a bound tree to examine outside of a member semantic model. Instead, we just
                ' rebind the appropriate syntax as if we were binding it as part of symbol creation.
                '
                ' if expression is not part of a member semantic model then 
                '   a) it may be a reference to a type or namespace name
                '   b) it may be a reference to a interface member in an Implements clause
                '   c) it may be a reference to a field in an Handles clause
                '   d) it may be a reference to an event in an Handles clause

                If SyntaxFacts.IsImplementedMember(node) Then
                    Return GetImplementedMemberTypeInfo(DirectCast(node, QualifiedNameSyntax))
                ElseIf SyntaxFacts.IsHandlesEvent(node) Then
                    Return GetHandlesEventTypeInfo(DirectCast(node, IdentifierNameSyntax))
                ElseIf SyntaxFacts.IsHandlesContainer(node) Then
                    Dim parent = node.Parent
                    If parent.Kind <> SyntaxKind.HandlesClauseItem Then
                        parent = parent.Parent
                    End If

                    Return GetHandlesContainerTypeInfo(DirectCast(parent, HandlesClauseItemSyntax))
                ElseIf SyntaxFacts.IsHandlesProperty(node) Then
                    Return GetHandlesPropertyTypeInfo(DirectCast(node.Parent.Parent, HandlesClauseItemSyntax))
                ElseIf IsInCrefOrNameAttributeInterior(node) Then
                    Dim typeSyntax = TryCast(node, TypeSyntax)
                    If typeSyntax IsNot Nothing Then
                        Return GetTypeInfoForCrefOrNameAttributeReference(typeSyntax)
                    End If
                ElseIf SyntaxFacts.IsInNamespaceOrTypeContext(node) Then
                    ' Bind the type or namespace name.
                    Return GetTypeOrNamespaceTypeInfoNotInMember(DirectCast(node, TypeSyntax))
                End If

                Return VisualBasicTypeInfo.None
            End If
        End Function

        Friend Overrides Function GetExpressionMemberGroup(node As ExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Symbol)
            node = SyntaxFactory.GetStandaloneExpression(DirectCast(node, ExpressionSyntax))

            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(node)

            If model IsNot Nothing Then
                Return model.GetExpressionMemberGroup(node, cancellationToken)
            Else
                Return ImmutableArray(Of Symbol).Empty
            End If
        End Function

        Friend Overrides Function GetExpressionConstantValue(node As ExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As ConstantValue
            node = SyntaxFactory.GetStandaloneExpression(DirectCast(node, ExpressionSyntax))

            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(node)

            If model IsNot Nothing Then
                Return model.GetExpressionConstantValue(node, cancellationToken)
            Else
                Return Nothing
            End If
        End Function

        Friend Overrides Function GetOperationWorker(node As VisualBasicSyntaxNode, cancellationToken As CancellationToken) As IOperation
            Dim model As MemberSemanticModel

            Dim methodBlock = TryCast(node, MethodBlockBaseSyntax)
            If methodBlock IsNot Nothing Then
                ' Trying to get the MemberSemanticModel for a MethodBlock will end up returning
                ' nothing.  That's because trying to get Binder for the MethodBlock will actually
                ' return the binder for the containing type.  To avoid this we ask for the model
                ' passing in a position at the end of the method's starting block-statement.
                ' This will cause it to try to get the interior MemberSemanticModel.
                model = GetMemberSemanticModel(methodBlock.BlockStatement.EndPosition)
            Else
                model = Me.GetMemberSemanticModel(node)
            End If

            If model IsNot Nothing Then
                Return model.GetOperationWorker(node, cancellationToken)
            Else
                Return Nothing
            End If
        End Function

        Friend Overrides Function GetAttributeSymbolInfo(attribute As AttributeSyntax, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(attribute)

            If model IsNot Nothing Then
                Return model.GetAttributeSymbolInfo(attribute, cancellationToken)
            Else
                Return SymbolInfo.None
            End If
        End Function

        Friend Overrides Function GetQueryClauseSymbolInfo(node As QueryClauseSyntax, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(node)

            If model IsNot Nothing Then
                Return model.GetQueryClauseSymbolInfo(node, cancellationToken)
            Else
                Return SymbolInfo.None
            End If
        End Function

        Friend Overrides Function GetLetClauseSymbolInfo(node As ExpressionRangeVariableSyntax, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(node)

            If model IsNot Nothing Then
                Return model.GetLetClauseSymbolInfo(node, cancellationToken)
            Else
                Return SymbolInfo.None
            End If
        End Function

        Friend Overrides Function GetOrderingSymbolInfo(node As OrderingSyntax, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(node)

            If model IsNot Nothing Then
                Return model.GetOrderingSymbolInfo(node, cancellationToken)
            Else
                Return SymbolInfo.None
            End If
        End Function

        Friend Overrides Function GetAggregateClauseSymbolInfoWorker(node As AggregateClauseSyntax, Optional cancellationToken As CancellationToken = Nothing) As AggregateClauseSymbolInfo
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(node)

            If model IsNot Nothing Then
                Return model.GetAggregateClauseSymbolInfoWorker(node, cancellationToken)
            Else
                Return New AggregateClauseSymbolInfo(SymbolInfo.None, SymbolInfo.None)
            End If
        End Function

        Friend Overrides Function GetCollectionRangeVariableSymbolInfoWorker(node As CollectionRangeVariableSyntax, Optional cancellationToken As CancellationToken = Nothing) As CollectionRangeVariableSymbolInfo
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(node)

            If model IsNot Nothing Then
                Return model.GetCollectionRangeVariableSymbolInfoWorker(node, cancellationToken)
            Else
                Return CollectionRangeVariableSymbolInfo.None
            End If
        End Function

        Friend Overrides Function GetAttributeTypeInfo(attribute As AttributeSyntax, Optional cancellationToken As CancellationToken = Nothing) As VisualBasicTypeInfo
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(attribute)

            If model IsNot Nothing Then
                Return model.GetAttributeTypeInfo(attribute, cancellationToken)
            Else
                Return VisualBasicTypeInfo.None
            End If
        End Function

        Friend Overrides Function GetAttributeMemberGroup(attribute As AttributeSyntax, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Symbol)
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(attribute)

            If model IsNot Nothing Then
                Return model.GetAttributeMemberGroup(attribute, cancellationToken)
            Else
                Return ImmutableArray(Of Symbol).Empty
            End If
        End Function

        Private Function GetTypeOrNamespaceSymbolNotInMember(expression As TypeSyntax) As Symbol
            ' Set up the binding context.
            Dim binder As Binder = GetEnclosingBinder(expression.SpanStart)

            ' Attempt to bind the type or namespace
            Dim resultSymbol As Symbol
            If SyntaxFacts.IsInTypeOnlyContext(expression) Then
                resultSymbol = binder.BindTypeOrAliasSyntax(expression, BindingDiagnosticBag.Discarded)
            Else
                resultSymbol = binder.BindNamespaceOrTypeOrAliasSyntax(expression, BindingDiagnosticBag.Discarded)
            End If

            ' Create the result.
            Return resultSymbol
        End Function

        ' Get the symbol info of reference from 'cref' or 'name' attribute value
        Private Function GetSymbolInfoForCrefOrNameAttributeReference(node As VisualBasicSyntaxNode, options As SymbolInfoOptions) As SymbolInfo
            Dim typeParameters As ImmutableArray(Of Symbol) = Nothing
            Dim result As ImmutableArray(Of Symbol) = GetCrefOrNameAttributeReferenceSymbols(node, (options And SymbolInfoOptions.ResolveAliases) = 0, typeParameters)

            If result.IsDefaultOrEmpty Then
                If typeParameters.IsDefaultOrEmpty Then
                    Return SymbolInfo.None
                Else
                    Return SymbolInfoFactory.Create(typeParameters, LookupResultKind.NotReferencable)
                End If
            End If

            If result.Length = 1 Then
                Dim retValue As SymbolInfo = GetSymbolInfoForSymbol(result(0), options)
                If retValue.CandidateReason = CandidateReason.None Then
                    Return retValue
                End If

                result = ImmutableArray(Of Symbol).Empty
            End If

            Dim symbolsBuilder = ArrayBuilder(Of Symbol).GetInstance()
            symbolsBuilder.AddRange(result)
            Dim symbols As ImmutableArray(Of Symbol) = RemoveErrorTypesAndDuplicates(symbolsBuilder, options)
            symbolsBuilder.Free()

            If symbols.Length = 0 Then
                Return SymbolInfoFactory.Create(symbols, LookupResultKind.Empty)
            End If

            Return SymbolInfoFactory.Create(symbols, If(symbols.Length = 1, LookupResultKind.Good, LookupResultKind.Ambiguous))
        End Function

        ' Get the type info of reference from 'cref' or 'name' attribute value
        Private Function GetTypeInfoForCrefOrNameAttributeReference(name As TypeSyntax) As VisualBasicTypeInfo
            Dim typeParameters As ImmutableArray(Of Symbol) = Nothing
            Dim result As ImmutableArray(Of Symbol) = GetCrefOrNameAttributeReferenceSymbols(name, preserveAlias:=False, typeParameters:=typeParameters)

            If result.IsDefaultOrEmpty Then
                result = typeParameters
                If result.IsDefaultOrEmpty Then
                    Return VisualBasicTypeInfo.None
                End If
            End If

            If result.Length > 1 Then
                Return VisualBasicTypeInfo.None
            End If

            Dim resultSymbol As Symbol = result(0)

            Select Case resultSymbol.Kind
                Case SymbolKind.ArrayType,
                     SymbolKind.TypeParameter,
                     SymbolKind.NamedType
                    Return GetTypeInfoForSymbol(resultSymbol)

            End Select

            Return VisualBasicTypeInfo.None
        End Function

        ''' <summary>
        ''' Get symbols referenced from 'cref' or 'name' attribute value.
        ''' </summary>
        ''' <param name="node">Node to bind.</param>
        ''' <param name="preserveAlias">True to leave <see cref="AliasSymbol"/>s, False to unwrap them.</param>
        ''' <param name="typeParameters">Out: symbols that would have been in the return value but improperly refer to type parameters.</param>
        ''' <returns>Referenced symbols, less type parameters.</returns>
        Private Function GetCrefOrNameAttributeReferenceSymbols(node As VisualBasicSyntaxNode,
                                                                preserveAlias As Boolean,
                                                                <Out> ByRef typeParameters As ImmutableArray(Of Symbol)) As ImmutableArray(Of Symbol)
            typeParameters = ImmutableArray(Of Symbol).Empty

            ' We only allow a certain list of node kinds to be processed here
            If node.Kind = SyntaxKind.XmlString Then
                Return Nothing
            End If
            Debug.Assert(node.Kind = SyntaxKind.IdentifierName OrElse
                         node.Kind = SyntaxKind.GenericName OrElse
                         node.Kind = SyntaxKind.PredefinedType OrElse
                         node.Kind = SyntaxKind.QualifiedName OrElse
                         node.Kind = SyntaxKind.GlobalName OrElse
                         node.Kind = SyntaxKind.QualifiedCrefOperatorReference OrElse
                         node.Kind = SyntaxKind.CrefOperatorReference OrElse
                         node.Kind = SyntaxKind.CrefReference)

            ' We need to find trivia's enclosing binder first
            Dim parent As VisualBasicSyntaxNode = node.Parent
            Debug.Assert(parent IsNot Nothing)

            Dim attributeNode As BaseXmlAttributeSyntax = Nothing
            Do
                Debug.Assert(parent IsNot Nothing)
                Select Case parent.Kind
                    Case SyntaxKind.XmlCrefAttribute,
                         SyntaxKind.XmlNameAttribute
                        attributeNode = DirectCast(parent, BaseXmlAttributeSyntax)

                    Case SyntaxKind.DocumentationCommentTrivia
                        Exit Do
                End Select
                parent = parent.Parent
            Loop
            Debug.Assert(parent IsNot Nothing)

            If attributeNode Is Nothing Then
                Return Nothing
            End If

            Dim isCrefAttribute As Boolean = attributeNode.Kind = SyntaxKind.XmlCrefAttribute
            Debug.Assert(isCrefAttribute OrElse attributeNode.Kind = SyntaxKind.XmlNameAttribute)

            Dim trivia As SyntaxTrivia = DirectCast(parent, DocumentationCommentTriviaSyntax).ParentTrivia
            If trivia.Kind = SyntaxKind.None Then
                Return Nothing
            End If

            Dim token As SyntaxToken = CType(trivia.Token, SyntaxToken)
            If token.Kind = SyntaxKind.None Then
                Return Nothing
            End If

            Dim docCommentBinder = Me._binderFactory.GetBinderForPosition(node, node.SpanStart)
            docCommentBinder = SemanticModelBinder.Mark(docCommentBinder, IgnoresAccessibility)

            If isCrefAttribute Then
                Dim symbols As ImmutableArray(Of Symbol)
                Dim isTopLevel As Boolean
                If node.Kind = SyntaxKind.CrefReference Then
                    isTopLevel = True
                    symbols = docCommentBinder.BindInsideCrefAttributeValue(DirectCast(node, CrefReferenceSyntax), preserveAlias, Nothing, CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
                Else
                    isTopLevel = node.Parent IsNot Nothing AndAlso node.Parent.Kind = SyntaxKind.CrefReference
                    symbols = docCommentBinder.BindInsideCrefAttributeValue(DirectCast(node, TypeSyntax), preserveAlias, Nothing, CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
                End If

                If isTopLevel Then
                    Dim symbolsBuilder As ArrayBuilder(Of Symbol) = Nothing
                    Dim typeParametersBuilder As ArrayBuilder(Of Symbol) = Nothing

                    For i = 0 To symbols.Length - 1
                        Dim symbol = symbols(i)
                        If symbol.Kind = SymbolKind.TypeParameter Then
                            If symbolsBuilder Is Nothing Then
                                symbolsBuilder = ArrayBuilder(Of Symbol).GetInstance(i)
                                typeParametersBuilder = ArrayBuilder(Of Symbol).GetInstance()
                                symbolsBuilder.AddRange(symbols, i)
                            End If
                            typeParametersBuilder.Add(DirectCast(symbol, TypeParameterSymbol))
                        ElseIf symbolsBuilder IsNot Nothing Then
                            symbolsBuilder.Add(symbol)
                        End If
                    Next

                    If symbolsBuilder IsNot Nothing Then
                        symbols = symbolsBuilder.ToImmutableAndFree()
                        typeParameters = typeParametersBuilder.ToImmutableAndFree()
                    End If
                End If

                Return symbols
            Else
                Return docCommentBinder.BindXmlNameAttributeValue(DirectCast(node, IdentifierNameSyntax), useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
            End If
        End Function

        ' Get the symbol info of type or namespace syntax that is outside a member body
        Private Function GetTypeOrNamespaceSymbolInfoNotInMember(expression As TypeSyntax, options As SymbolInfoOptions) As SymbolInfo
            Dim resultSymbol As Symbol = GetTypeOrNamespaceSymbolNotInMember(expression)

            ' Deal with the case of a namespace group. We may need to bind more in order to see if the ambiguity can be resolved.
            If resultSymbol.Kind = SymbolKind.Namespace AndAlso
               expression.Parent IsNot Nothing AndAlso
               expression.Parent.Kind = SyntaxKind.QualifiedName AndAlso
               DirectCast(expression.Parent, QualifiedNameSyntax).Left Is expression Then
                Dim ns = DirectCast(resultSymbol, NamespaceSymbol)

                If ns.NamespaceKind = NamespaceKindNamespaceGroup Then
                    Dim parentInfo As SymbolInfo = GetTypeOrNamespaceSymbolInfoNotInMember(DirectCast(expression.Parent, QualifiedNameSyntax), Nothing)

                    If Not parentInfo.IsEmpty Then
                        Dim namespaces = New SmallDictionary(Of NamespaceSymbol, Boolean)()

                        If parentInfo.Symbol IsNot Nothing Then
                            If Not Binder.AddReceiverNamespaces(namespaces, DirectCast(parentInfo.Symbol, Symbol), Compilation) Then
                                namespaces = Nothing
                            End If
                        Else
                            For Each candidate In parentInfo.CandidateSymbols
                                If Not Binder.AddReceiverNamespaces(namespaces, DirectCast(candidate, Symbol), Compilation) Then
                                    namespaces = Nothing
                                    Exit For
                                End If
                            Next
                        End If

                        If namespaces IsNot Nothing AndAlso namespaces.Count < ns.ConstituentNamespaces.Length Then
                            resultSymbol = DirectCast(ns, MergedNamespaceSymbol).Shrink(namespaces.Keys)
                        End If
                    End If
                End If
            End If

            ' Create the result.
            Dim result = GetSymbolInfoForSymbol(resultSymbol, options)

            ' If we didn't get anything and were in Type/Namespace only context, let's bind normally and see
            ' if any symbol comes out.
            If result.IsEmpty Then
                Dim tryAsExpression = TryBindNamespaceOrTypeAsExpression(expression, options)
                If Not tryAsExpression.IsEmpty Then
                    result = tryAsExpression
                End If
            End If

            Return result
        End Function

        ' Get the symbol info of type or namespace syntax that is outside a member body
        Private Function GetTypeOrNamespaceTypeInfoNotInMember(expression As TypeSyntax) As VisualBasicTypeInfo
            Dim resultSymbol As Symbol = GetTypeOrNamespaceSymbolNotInMember(expression)

            ' Create the result.
            Return GetTypeInfoForSymbol(resultSymbol)
        End Function

        Private Function GetImplementedMemberAndResultKind(symbolBuilder As ArrayBuilder(Of Symbol), memberName As QualifiedNameSyntax) As LookupResultKind
            Debug.Assert(symbolBuilder.Count = 0)

            Dim resultKind As LookupResultKind = LookupResultKind.Good

            ' Set up the binding context.
            Dim binder As Binder = GetEnclosingBinder(memberName.SpanStart)

            ' Figure out the symbol this implements clause is on, and bind the syntax for it.
            Dim implementingMemberSyntax = TryCast(memberName.Parent.Parent, MethodBaseSyntax)
            If implementingMemberSyntax IsNot Nothing Then
                Dim implementingMember = GetDeclaredSymbol(implementingMemberSyntax)

                If implementingMember IsNot Nothing Then
                    Select Case implementingMember.Kind
                        Case SymbolKind.Method
                            ImplementsHelper.FindExplicitlyImplementedMember(Of MethodSymbol)(
                                                    DirectCast(implementingMember, MethodSymbol),
                                                    DirectCast(implementingMember, MethodSymbol).ContainingType,
                                                    memberName,
                                                    binder,
                                                    BindingDiagnosticBag.Discarded,
                                                    symbolBuilder,
                                                    resultKind)

                        Case SymbolKind.Property
                            ImplementsHelper.FindExplicitlyImplementedMember(Of PropertySymbol)(
                                                    DirectCast(implementingMember, PropertySymbol),
                                                    DirectCast(implementingMember, PropertySymbol).ContainingType,
                                                    memberName,
                                                    binder,
                                                    BindingDiagnosticBag.Discarded,
                                                    symbolBuilder,
                                                    resultKind)

                        Case SymbolKind.Event
                            ImplementsHelper.FindExplicitlyImplementedMember(Of EventSymbol)(
                                                    DirectCast(implementingMember, EventSymbol),
                                                    DirectCast(implementingMember, EventSymbol).ContainingType,
                                                    memberName,
                                                    binder,
                                                    BindingDiagnosticBag.Discarded,
                                                    symbolBuilder,
                                                    resultKind)

                        Case Else
                            Throw ExceptionUtilities.UnexpectedValue(implementingMember.Kind)
                    End Select
                End If
            End If

            Return resultKind
        End Function

        Private Function GetHandledEventOrContainerSymbolsAndResultKind(eventSymbolBuilder As ArrayBuilder(Of Symbol),
                                                                       containerSymbolBuilder As ArrayBuilder(Of Symbol),
                                                                       propertySymbolBuilder As ArrayBuilder(Of Symbol),
                                                                       handlesClause As HandlesClauseItemSyntax) As LookupResultKind

            Dim resultKind As LookupResultKind = LookupResultKind.Good

            ' Set up the binding context.
            Dim binder As Binder = GetEnclosingBinder(handlesClause.SpanStart)

            ' Figure out the symbol this handles clause is on, and bind the syntax for it.
            Dim handlingMethodSyntax = TryCast(handlesClause.Parent.Parent, MethodStatementSyntax)
            If handlingMethodSyntax IsNot Nothing Then
                Dim implementingMember = GetDeclaredSymbol(handlingMethodSyntax)

                If implementingMember IsNot Nothing Then
                    Dim methodSym = DirectCast(implementingMember, SourceMemberMethodSymbol)

                    methodSym.BindSingleHandlesClause(handlesClause,
                                                      binder,
                                                      BindingDiagnosticBag.Discarded,
                                                      eventSymbolBuilder,
                                                      containerSymbolBuilder,
                                                      propertySymbolBuilder,
                                                      resultKind)
                End If
            End If

            Return resultKind
        End Function

        ' Get the symbol info of an implemented member in an implements clause.
        Private Function GetImplementedMemberSymbolInfo(memberName As QualifiedNameSyntax, options As SymbolInfoOptions) As SymbolInfo
            Dim implementedMemberBuilder As ArrayBuilder(Of Symbol) = ArrayBuilder(Of Symbol).GetInstance()
            Dim resultKind As LookupResultKind = GetImplementedMemberAndResultKind(implementedMemberBuilder, memberName)
            Dim symbols As ImmutableArray(Of Symbol) = RemoveErrorTypesAndDuplicates(implementedMemberBuilder, options)
            implementedMemberBuilder.Free()

            Return SymbolInfoFactory.Create(symbols, resultKind)
        End Function

        ' Get the symbol info of a handled event in a handles clause.
        Private Function GetHandlesEventSymbolInfo(handlesClause As HandlesClauseItemSyntax, options As SymbolInfoOptions) As SymbolInfo
            Dim builder As ArrayBuilder(Of Symbol) = ArrayBuilder(Of Symbol).GetInstance()
            Dim resultKind As LookupResultKind = GetHandledEventOrContainerSymbolsAndResultKind(eventSymbolBuilder:=builder,
                                                                                                containerSymbolBuilder:=Nothing,
                                                                                                propertySymbolBuilder:=Nothing,
                                                                                                handlesClause:=handlesClause)

            Dim symbols As ImmutableArray(Of Symbol) = RemoveErrorTypesAndDuplicates(builder, options)
            builder.Free()

            Return SymbolInfoFactory.Create(symbols, resultKind)
        End Function

        ' Get the symbol info of an identifier event container in a handles clause.
        Private Function GetHandlesContainerSymbolInfo(handlesClause As HandlesClauseItemSyntax, options As SymbolInfoOptions) As SymbolInfo
            Dim builder As ArrayBuilder(Of Symbol) = ArrayBuilder(Of Symbol).GetInstance()
            Dim resultKind As LookupResultKind = GetHandledEventOrContainerSymbolsAndResultKind(eventSymbolBuilder:=Nothing,
                                                                                                containerSymbolBuilder:=builder,
                                                                                                propertySymbolBuilder:=Nothing,
                                                                                                handlesClause:=handlesClause)

            Dim symbols As ImmutableArray(Of Symbol) = RemoveErrorTypesAndDuplicates(builder, options)
            builder.Free()

            Return SymbolInfoFactory.Create(symbols, resultKind)
        End Function

        ' Get the symbol info of a withevents sourcing property in a handles clause.
        Private Function GetHandlesPropertySymbolInfo(handlesClause As HandlesClauseItemSyntax, options As SymbolInfoOptions) As SymbolInfo
            Dim builder As ArrayBuilder(Of Symbol) = ArrayBuilder(Of Symbol).GetInstance()
            Dim resultKind As LookupResultKind = GetHandledEventOrContainerSymbolsAndResultKind(eventSymbolBuilder:=Nothing,
                                                                                                containerSymbolBuilder:=Nothing,
                                                                                                propertySymbolBuilder:=builder,
                                                                                                handlesClause:=handlesClause)

            Dim symbols As ImmutableArray(Of Symbol) = RemoveErrorTypesAndDuplicates(builder, options)
            builder.Free()

            Return SymbolInfoFactory.Create(symbols, resultKind)
        End Function

        ' Get the type info of a implemented member in a implements clause.
        Private Function GetImplementedMemberTypeInfo(memberName As QualifiedNameSyntax) As VisualBasicTypeInfo
            ' Implemented members have no type.
            Return VisualBasicTypeInfo.None
        End Function

        ' Get the type info of a implemented member in a implements clause.
        Private Function GetHandlesEventTypeInfo(memberName As IdentifierNameSyntax) As VisualBasicTypeInfo
            ' Handled events have no type.
            Return VisualBasicTypeInfo.None
        End Function

        ' Get the type info of a implemented member in a implements clause.
        Private Function GetHandlesContainerTypeInfo(memberName As HandlesClauseItemSyntax) As VisualBasicTypeInfo
            ' Handled events have no type.
            Return VisualBasicTypeInfo.None
        End Function

        ' Get the type info of a implemented member in a implements clause.
        Private Function GetHandlesPropertyTypeInfo(memberName As HandlesClauseItemSyntax) As VisualBasicTypeInfo
            ' Handled events have no type.
            Return VisualBasicTypeInfo.None
        End Function

        ''' <summary>
        ''' Checks all symbol locations against the syntax provided and return symbol if any of the locations is 
        ''' inside the syntax span. Returns Nothing otherwise.
        ''' </summary>
        Private Function CheckSymbolLocationsAgainstSyntax(symbol As NamedTypeSymbol, nodeToCheck As VisualBasicSyntaxNode) As NamedTypeSymbol
            For Each location In symbol.Locations
                If location.SourceTree Is Me.SyntaxTree AndAlso nodeToCheck.Span.Contains(location.SourceSpan) Then
                    Return symbol
                End If
            Next
            Return Nothing
        End Function

        ''' <summary>
        ''' Given a delegate declaration, get the corresponding type symbol.
        ''' </summary>
        ''' <param name="declarationSyntax">The syntax node that declares a type.</param>
        ''' <returns>The type symbol that was declared.</returns>
        Public Overloads Function GetDeclaredSymbol(declarationSyntax As DelegateStatementSyntax, Optional cancellationToken As CancellationToken = Nothing) As NamedTypeSymbol
            If declarationSyntax Is Nothing Then Throw New ArgumentNullException(NameOf(declarationSyntax))
            If Not IsInTree(declarationSyntax) Then Throw New ArgumentException(VBResources.DeclarationSyntaxNotWithinTree)

            ' Don't need to wrap in a SemanticModelBinder, since we're not binding.
            Dim binder As Binder = _binderFactory.GetNamedTypeBinder(declarationSyntax)

            If binder IsNot Nothing AndAlso TypeOf binder Is NamedTypeBinder Then
                Return CheckSymbolLocationsAgainstSyntax(DirectCast(binder.ContainingType, NamedTypeSymbol), declarationSyntax)
            Else
                Return Nothing  ' Can this happen? Maybe in some edge case error cases.
            End If
        End Function

        ''' <summary>
        ''' Given a type declaration, get the corresponding type symbol.
        ''' </summary>
        ''' <param name="declarationSyntax">The syntax node that declares a type.</param>
        ''' <returns>The type symbol that was declared.</returns>
        Public Overloads Overrides Function GetDeclaredSymbol(declarationSyntax As TypeStatementSyntax, Optional cancellationToken As CancellationToken = Nothing) As INamedTypeSymbol
            If declarationSyntax Is Nothing Then Throw New ArgumentNullException(NameOf(declarationSyntax))
            If Not IsInTree(declarationSyntax) Then Throw New ArgumentException(VBResources.DeclarationSyntaxNotWithinTree)

            ' Don't need to wrap in a SemanticModelBinder, since we're not binding.
            Dim binder As Binder = _binderFactory.GetNamedTypeBinder(declarationSyntax)

            If binder IsNot Nothing AndAlso TypeOf binder Is NamedTypeBinder Then
                Return CheckSymbolLocationsAgainstSyntax(DirectCast(binder.ContainingType, NamedTypeSymbol), declarationSyntax)
            Else
                Return Nothing  ' Can this happen? Maybe in some edge case error cases.
            End If
        End Function

        ''' <summary>
        ''' Given a enum declaration, get the corresponding type symbol.
        ''' </summary>
        ''' <param name="declarationSyntax">The syntax node that declares an enum.</param>
        ''' <returns>The type symbol that was declared.</returns>
        Public Overloads Overrides Function GetDeclaredSymbol(declarationSyntax As EnumStatementSyntax, Optional cancellationToken As CancellationToken = Nothing) As INamedTypeSymbol
            If declarationSyntax Is Nothing Then Throw New ArgumentNullException(NameOf(declarationSyntax))
            If Not IsInTree(declarationSyntax) Then Throw New ArgumentException(VBResources.DeclarationSyntaxNotWithinTree)

            ' Don't need to wrap in a SemanticModelBinder, since we're not binding.
            Dim binder As Binder = _binderFactory.GetNamedTypeBinder(declarationSyntax)

            If binder IsNot Nothing AndAlso TypeOf binder Is NamedTypeBinder Then
                Return CheckSymbolLocationsAgainstSyntax(DirectCast(binder.ContainingType, NamedTypeSymbol), declarationSyntax)
            Else
                Return Nothing  ' Can this happen? Maybe in some edge case with errors
            End If
        End Function

        ''' <summary>
        ''' Given a namespace declaration, get the corresponding type symbol.
        ''' </summary>
        ''' <param name="declarationSyntax">The syntax node that declares a namespace.</param>
        ''' <returns>The namespace symbol that was declared.</returns>
        Public Overloads Overrides Function GetDeclaredSymbol(declarationSyntax As NamespaceStatementSyntax, Optional cancellationToken As CancellationToken = Nothing) As INamespaceSymbol
            If declarationSyntax Is Nothing Then Throw New ArgumentNullException(NameOf(declarationSyntax))
            If Not IsInTree(declarationSyntax) Then Throw New ArgumentException(VBResources.DeclarationSyntaxNotWithinTree)

            Dim parentBlock = TryCast(declarationSyntax.Parent, NamespaceBlockSyntax)
            If parentBlock IsNot Nothing Then
                ' Don't need to wrap in a SemanticModelBinder, since we're not binding.
                Dim binder As Binder = _binderFactory.GetNamespaceBinder(parentBlock)

                If binder IsNot Nothing AndAlso TypeOf binder Is NamespaceBinder Then
                    Return DirectCast(binder.ContainingNamespaceOrType, NamespaceSymbol)
                End If
            End If

            Return Nothing ' Edge case with errors
        End Function

        ''' <summary>
        ''' Given a method, property, or event declaration, get the corresponding symbol.
        ''' </summary>
        ''' <param name="declarationSyntax">The syntax node that declares a method, property, or event.</param>
        ''' <returns>The method, property, or event symbol that was declared.</returns>
        Friend Overloads Overrides Function GetDeclaredSymbol(declarationSyntax As MethodBaseSyntax, Optional cancellationToken As CancellationToken = Nothing) As ISymbol
            If declarationSyntax Is Nothing Then Throw New ArgumentNullException(NameOf(declarationSyntax))
            If Not IsInTree(declarationSyntax) Then
                Throw New ArgumentException(VBResources.DeclarationSyntaxNotWithinTree)
            End If

            ' Delegate declarations are a subclass of MethodBaseSyntax syntax-wise, but they are
            ' more like a type declaration, so we need to special case here.
            If declarationSyntax.Kind = SyntaxKind.DelegateFunctionStatement OrElse
                declarationSyntax.Kind = SyntaxKind.DelegateSubStatement Then
                Return GetDeclaredSymbol(DirectCast(declarationSyntax, DelegateStatementSyntax), cancellationToken)
            End If

            Dim statementSyntax = TryCast(declarationSyntax.Parent, StatementSyntax)
            If statementSyntax IsNot Nothing Then

                '  get parent type block
                Dim parentTypeBlock As TypeBlockSyntax = Nothing
                Select Case statementSyntax.Kind
                    Case SyntaxKind.ClassBlock, SyntaxKind.EnumBlock, SyntaxKind.StructureBlock, SyntaxKind.InterfaceBlock, SyntaxKind.ModuleBlock
                        parentTypeBlock = TryCast(statementSyntax, TypeBlockSyntax)

                    Case SyntaxKind.SubBlock, SyntaxKind.FunctionBlock, SyntaxKind.ConstructorBlock, SyntaxKind.OperatorBlock, SyntaxKind.PropertyBlock, SyntaxKind.EventBlock
                        parentTypeBlock = TryCast(statementSyntax.Parent, TypeBlockSyntax)

                        ' EDMAURER maybe this is a top-level decl in which case the parent is a CompilationUnitSyntax
                        If parentTypeBlock Is Nothing AndAlso statementSyntax.Parent IsNot Nothing Then
                            Dim namespaceToLookInForImplicitType As INamespaceSymbol = Nothing
                            Select Case statementSyntax.Parent.Kind
                                Case SyntaxKind.CompilationUnit
                                    namespaceToLookInForImplicitType = Me._sourceModule.RootNamespace
                                Case SyntaxKind.NamespaceBlock
                                    namespaceToLookInForImplicitType = GetDeclaredSymbol(DirectCast(statementSyntax.Parent, NamespaceBlockSyntax), cancellationToken)
                            End Select

                            If namespaceToLookInForImplicitType IsNot Nothing Then
                                Dim implicitType = DirectCast(namespaceToLookInForImplicitType.GetMembers(TypeSymbol.ImplicitTypeName).SingleOrDefault(), NamedTypeSymbol)

                                If implicitType IsNot Nothing Then
                                    Return SourceMethodSymbol.FindSymbolFromSyntax(declarationSyntax, _syntaxTree, implicitType)
                                End If
                            End If
                        End If
                    Case SyntaxKind.GetAccessorBlock, SyntaxKind.SetAccessorBlock, SyntaxKind.AddHandlerAccessorBlock, SyntaxKind.RemoveHandlerAccessorBlock, SyntaxKind.RaiseEventAccessorBlock
                        '  redirect to upper property or event symbol
                        If statementSyntax.Parent IsNot Nothing Then
                            parentTypeBlock = TryCast(statementSyntax.Parent.Parent, TypeBlockSyntax)
                        End If

                    Case SyntaxKind.AddHandlerAccessorBlock, SyntaxKind.RemoveHandlerAccessorBlock
                        '  redirect to upper event symbol
                        If statementSyntax.Parent IsNot Nothing Then
                            parentTypeBlock = TryCast(statementSyntax.Parent.Parent, TypeBlockSyntax)
                        End If

                    Case Else
                        ' broken code scenarios end up here

                        ' to end up here, a methodbasesyntax's parent must be a statement and not be one of the above. 
                        ' The parser does e.g. not generate an enclosing block for accessors statements,
                        ' but for Operators, conversions and constructors.

                        ' The case where an invalid accessor is contained in e.g. an interface is handled further down in "FindSymbolFromSyntax".

                        ' TODO: consider always creating a (missing) block around the statements in the parser

                        ' We are asserting what we know so far. If this assert fails, this is not a bug, we either need to remove this assert or relax the assert. 
                        Debug.Assert(statementSyntax.Kind = SyntaxKind.NamespaceBlock AndAlso
                                     (TypeOf (declarationSyntax) Is AccessorStatementSyntax OrElse
                                      TypeOf (declarationSyntax) Is EventStatementSyntax OrElse
                                      TypeOf (declarationSyntax) Is MethodStatementSyntax OrElse
                                      TypeOf (declarationSyntax) Is PropertyStatementSyntax))

                        Return Nothing
                End Select

                If parentTypeBlock IsNot Nothing Then
                    Dim containingType = DirectCast(GetDeclaredSymbol(parentTypeBlock.BlockStatement, cancellationToken), NamedTypeSymbol)
                    If containingType IsNot Nothing Then
                        Return SourceMethodSymbol.FindSymbolFromSyntax(declarationSyntax, _syntaxTree, containingType)
                    End If
                End If
            End If

            Return Nothing
        End Function

        ''' <summary>
        ''' Given a parameter declaration, get the corresponding parameter symbol.
        ''' </summary>
        ''' <param name="parameter">The syntax node that declares a parameter.</param>
        ''' <returns>The parameter symbol that was declared.</returns>
        Public Overloads Overrides Function GetDeclaredSymbol(parameter As ParameterSyntax, Optional cancellationToken As CancellationToken = Nothing) As IParameterSymbol
            If parameter Is Nothing Then
                Throw New ArgumentNullException(NameOf(parameter))
            End If

            Dim paramList As ParameterListSyntax = TryCast(parameter.Parent, ParameterListSyntax)
            If paramList IsNot Nothing Then
                Dim declarationSyntax As MethodBaseSyntax = TryCast(paramList.Parent, MethodBaseSyntax)
                If declarationSyntax IsNot Nothing Then
                    Dim symbol = GetDeclaredSymbol(declarationSyntax, cancellationToken)
                    If symbol IsNot Nothing Then
                        Select Case symbol.Kind
                            Case SymbolKind.Method
                                Return GetParameterSymbol(DirectCast(symbol, MethodSymbol).Parameters, parameter)
                            Case SymbolKind.Event
                                Dim eventSymbol As EventSymbol = DirectCast(symbol, EventSymbol)
                                Dim type = TryCast(eventSymbol.Type, NamedTypeSymbol)

                                If type?.AssociatedSymbol Is eventSymbol Then
                                    Return GetParameterSymbol(type.DelegateInvokeMethod.Parameters, parameter)
                                End If

                                Return Nothing

                            Case SymbolKind.Property
                                Return GetParameterSymbol(DirectCast(symbol, PropertySymbol).Parameters, parameter)
                            Case SymbolKind.NamedType
                                '  check for being delegate 
                                Dim typeSymbol = DirectCast(symbol, NamedTypeSymbol)
                                Debug.Assert(typeSymbol.TypeKind = TYPEKIND.Delegate)
                                If typeSymbol.DelegateInvokeMethod IsNot Nothing Then
                                    Return GetParameterSymbol(typeSymbol.DelegateInvokeMethod.Parameters, parameter)
                                End If
                        End Select

                    ElseIf TypeOf declarationSyntax Is LambdaHeaderSyntax Then
                        ' This could be a lambda parameter.
                        Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(declarationSyntax)

                        If model IsNot Nothing Then
                            Return model.GetDeclaredSymbol(parameter, cancellationToken)
                        End If
                    End If
                End If
            End If

            Return Nothing
        End Function

        ''' <summary>
        ''' Given a type parameter declaration, get the corresponding type parameter symbol.
        ''' </summary>
        ''' <param name="typeParameter">The syntax node that declares a type parameter.</param>
        ''' <returns>The type parameter symbol that was declared.</returns>
        Public Overloads Overrides Function GetDeclaredSymbol(typeParameter As TypeParameterSyntax, Optional cancellationToken As CancellationToken = Nothing) As ITypeParameterSymbol
            If typeParameter Is Nothing Then
                Throw New ArgumentNullException(NameOf(typeParameter))
            End If
            If Not IsInTree(typeParameter) Then
                Throw New ArgumentException(VBResources.TypeParameterNotWithinTree)
            End If

            Dim symbol As ISymbol = Nothing
            Dim typeParamList = TryCast(typeParameter.Parent, TypeParameterListSyntax)
            If typeParamList IsNot Nothing AndAlso typeParamList.Parent IsNot Nothing Then
                If TypeOf typeParamList.Parent Is MethodStatementSyntax Then
                    symbol = GetDeclaredSymbol(DirectCast(typeParamList.Parent, MethodStatementSyntax), cancellationToken)
                ElseIf TypeOf typeParamList.Parent Is TypeStatementSyntax Then
                    symbol = GetDeclaredSymbol(DirectCast(typeParamList.Parent, TypeStatementSyntax), cancellationToken)
                ElseIf TypeOf typeParamList.Parent Is DelegateStatementSyntax Then
                    symbol = GetDeclaredSymbol(DirectCast(typeParamList.Parent, DelegateStatementSyntax), cancellationToken)
                End If

                If symbol IsNot Nothing Then
                    Dim typeSymbol = TryCast(symbol, NamedTypeSymbol)
                    If typeSymbol IsNot Nothing Then
                        Return Me.GetTypeParameterSymbol(typeSymbol.TypeParameters, typeParameter)
                    End If

                    Dim methodSymbol = TryCast(symbol, MethodSymbol)
                    If methodSymbol IsNot Nothing Then
                        Return Me.GetTypeParameterSymbol(methodSymbol.TypeParameters, typeParameter)
                    End If
                End If
            End If

            Return Nothing
        End Function

        ' Get a type parameter symbol from a ROA of TypeParametersSymbols and the syntax for one.
        Private Function GetTypeParameterSymbol(parameters As ImmutableArray(Of TypeParameterSymbol), parameter As TypeParameterSyntax) As TypeParameterSymbol
            For Each symbol In parameters
                For Each location In symbol.Locations
                    If location.IsInSource AndAlso location.SourceTree Is _syntaxTree AndAlso parameter.Span.Contains(location.SourceSpan) Then
                        Return symbol
                    End If
                Next
            Next

            Return Nothing
        End Function

        Public Overrides Function GetDeclaredSymbol(declarationSyntax As EnumMemberDeclarationSyntax, Optional cancellationToken As CancellationToken = Nothing) As IFieldSymbol
            If declarationSyntax Is Nothing Then
                Throw New ArgumentNullException(NameOf(declarationSyntax))
            End If

            If Not IsInTree(declarationSyntax) Then
                Throw New ArgumentException(VBResources.DeclarationSyntaxNotWithinTree)
            End If

            Dim enumBlock As EnumBlockSyntax = DirectCast(declarationSyntax.Parent, EnumBlockSyntax)

            If enumBlock IsNot Nothing Then
                Dim containingType = DirectCast(GetDeclaredSymbol(enumBlock.EnumStatement, cancellationToken), NamedTypeSymbol)
                If containingType IsNot Nothing Then
                    Return DirectCast(SourceFieldSymbol.FindFieldOrWithEventsSymbolFromSyntax(declarationSyntax.Identifier, _syntaxTree, containingType), FieldSymbol)
                End If
            End If

            Return Nothing
        End Function

        ''' <summary>
        ''' Given a variable declaration, get the corresponding  symbol.
        ''' </summary>
        ''' <param name="declarationSyntax">The syntax node that declares a variable.</param>
        ''' <returns>The symbol that was declared.</returns>
        Public Overrides Function GetDeclaredSymbol(declarationSyntax As ModifiedIdentifierSyntax, Optional cancellationToken As CancellationToken = Nothing) As ISymbol
            If declarationSyntax Is Nothing Then
                Throw New ArgumentNullException(NameOf(declarationSyntax))
            End If

            If Not IsInTree(declarationSyntax) Then
                Throw New ArgumentException(VBResources.DeclarationSyntaxNotWithinTree)
            End If

            Dim declarationParent = declarationSyntax.Parent

            ' Possibility 1: Field syntax, could be a Field or WithEvent property
            Dim fieldSyntax As FieldDeclarationSyntax = Nothing
            If declarationParent IsNot Nothing Then
                fieldSyntax = TryCast(declarationParent.Parent, FieldDeclarationSyntax)
            End If

            Dim parentTypeBlock As TypeBlockSyntax = Nothing
            If fieldSyntax IsNot Nothing Then
                parentTypeBlock = TryCast(fieldSyntax.Parent, TypeBlockSyntax)
            Else : End If

            If parentTypeBlock IsNot Nothing Then
                Dim containingType = DirectCast(GetDeclaredSymbol(parentTypeBlock.BlockStatement, cancellationToken), NamedTypeSymbol)
                If containingType IsNot Nothing Then
                    Return SourceFieldSymbol.FindFieldOrWithEventsSymbolFromSyntax(declarationSyntax.Identifier, _syntaxTree, containingType)
                End If
            End If

            ' Possibility 2: Parameter
            Dim parameterSyntax As ParameterSyntax = TryCast(declarationParent, ParameterSyntax)

            If parameterSyntax IsNot Nothing Then
                Return GetDeclaredSymbol(parameterSyntax, cancellationToken)
            End If

            ' Possibility 3: Local variable
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(declarationSyntax)

            If model IsNot Nothing Then
                Return model.GetDeclaredSymbol(declarationSyntax, cancellationToken)
            End If

            Return MyBase.GetDeclaredSymbol(declarationSyntax, cancellationToken)
        End Function

        ''' <summary>
        ''' Given a FieldInitializerSyntax, get the corresponding symbol of anonymous type creation.
        ''' </summary>
        ''' <param name="fieldInitializerSyntax">The anonymous object creation field initializer syntax.</param>
        ''' <returns>The symbol that was declared, or Nothing if no such symbol exists.</returns>
        Public Overrides Function GetDeclaredSymbol(fieldInitializerSyntax As FieldInitializerSyntax, Optional cancellationToken As System.Threading.CancellationToken = Nothing) As IPropertySymbol
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(fieldInitializerSyntax)

            If model IsNot Nothing Then
                Return model.GetDeclaredSymbol(fieldInitializerSyntax, cancellationToken)
            End If

            Return MyBase.GetDeclaredSymbol(fieldInitializerSyntax, cancellationToken)
        End Function

        ''' <summary>
        ''' Given an AnonymousObjectCreationExpressionSyntax, get the corresponding symbol of anonymous type.
        ''' </summary>
        ''' <param name="anonymousObjectCreationExpressionSyntax">The anonymous object creation syntax.</param>
        ''' <returns>The symbol that was declared, or Nothing if no such symbol exists.</returns>
        Public Overrides Function GetDeclaredSymbol(anonymousObjectCreationExpressionSyntax As AnonymousObjectCreationExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As INamedTypeSymbol
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(anonymousObjectCreationExpressionSyntax)

            If model IsNot Nothing Then
                Return model.GetDeclaredSymbol(anonymousObjectCreationExpressionSyntax, cancellationToken)
            End If

            Return MyBase.GetDeclaredSymbol(anonymousObjectCreationExpressionSyntax, cancellationToken)
        End Function

        ''' <summary>
        ''' Given an ExpressionRangeVariableSyntax, get the corresponding symbol.
        ''' </summary>
        ''' <param name="rangeVariableSyntax">The range variable syntax that declares a variable.</param>
        ''' <returns>The symbol that was declared, or Nothing if no such symbol exists.</returns>
        Public Overrides Function GetDeclaredSymbol(rangeVariableSyntax As ExpressionRangeVariableSyntax, Optional cancellationToken As CancellationToken = Nothing) As IRangeVariableSymbol
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(rangeVariableSyntax)

            If model IsNot Nothing Then
                Return model.GetDeclaredSymbol(rangeVariableSyntax, cancellationToken)
            End If

            Return MyBase.GetDeclaredSymbol(rangeVariableSyntax, cancellationToken)
        End Function

        ''' <summary>
        ''' Given a CollectionRangeVariableSyntax, get the corresponding symbol.
        ''' </summary>
        ''' <param name="rangeVariableSyntax">The range variable syntax that declares a variable.</param>
        ''' <returns>The symbol that was declared, or Nothing if no such symbol exists.</returns>
        Public Overrides Function GetDeclaredSymbol(rangeVariableSyntax As CollectionRangeVariableSyntax, Optional cancellationToken As CancellationToken = Nothing) As IRangeVariableSymbol
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(rangeVariableSyntax)

            If model IsNot Nothing Then
                Return model.GetDeclaredSymbol(rangeVariableSyntax, cancellationToken)
            End If

            Return MyBase.GetDeclaredSymbol(rangeVariableSyntax, cancellationToken)
        End Function

        ''' <summary>
        ''' Given an AggregationRangeVariableSyntax, get the corresponding symbol.
        ''' </summary>
        ''' <param name="rangeVariableSyntax">The range variable syntax that declares a variable.</param>
        ''' <returns>The symbol that was declared, or Nothing if no such symbol exists.</returns>
        Public Overrides Function GetDeclaredSymbol(rangeVariableSyntax As AggregationRangeVariableSyntax, Optional cancellationToken As CancellationToken = Nothing) As IRangeVariableSymbol
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(rangeVariableSyntax)

            If model IsNot Nothing Then
                Return model.GetDeclaredSymbol(rangeVariableSyntax, cancellationToken)
            End If

            Return MyBase.GetDeclaredSymbol(rangeVariableSyntax, cancellationToken)
        End Function

        ''' <summary>
        ''' Given an import clause get the corresponding symbol for the import alias that was introduced.
        ''' </summary>
        ''' <param name="declarationSyntax">The import statement syntax node.</param>
        ''' <returns>The alias symbol that was declared or Nothing if no alias symbol was declared.</returns>
        Public Overloads Overrides Function GetDeclaredSymbol(declarationSyntax As SimpleImportsClauseSyntax, Optional cancellationToken As CancellationToken = Nothing) As IAliasSymbol
            If declarationSyntax Is Nothing Then
                Throw New ArgumentNullException(NameOf(declarationSyntax))
            End If

            If Not IsInTree(declarationSyntax) Then
                Throw New ArgumentException(VBResources.DeclarationSyntaxNotWithinTree)
            End If

            If declarationSyntax.Alias Is Nothing Then
                Return Nothing
            End If

            Dim aliasName As String = declarationSyntax.Alias.Identifier.ValueText

            If Not String.IsNullOrEmpty(aliasName) Then
                Dim sourceFile = Me._sourceModule.TryGetSourceFile(Me.SyntaxTree)
                Debug.Assert(sourceFile IsNot Nothing)

                Dim aliasImports As IReadOnlyDictionary(Of String, AliasAndImportsClausePosition) = sourceFile.AliasImportsOpt
                Dim symbol As AliasAndImportsClausePosition = Nothing

                If aliasImports IsNot Nothing AndAlso aliasImports.TryGetValue(aliasName, symbol) Then
                    '  make sure the symbol is declared inside declarationSyntax node
                    For Each location In symbol.Alias.Locations
                        If location.IsInSource AndAlso location.SourceTree Is _syntaxTree AndAlso declarationSyntax.Span.Contains(location.SourceSpan) Then
                            Return symbol.Alias
                        End If
                    Next

                    ' If the alias name was in the map but the location didn't match, then the syntax declares a duplicate alias.
                    ' We'll return a new AliasSymbol to improve the API experience.
                    Dim binder As Binder = GetEnclosingBinder(declarationSyntax.SpanStart)
                    Dim targetSymbol As NamespaceOrTypeSymbol = binder.BindNamespaceOrTypeSyntax(declarationSyntax.Name, BindingDiagnosticBag.Discarded)
                    If targetSymbol IsNot Nothing Then
                        Return New AliasSymbol(binder.Compilation, binder.ContainingNamespaceOrType, aliasName, targetSymbol, declarationSyntax.GetLocation())
                    End If
                End If
            End If

            Return Nothing
        End Function

        ''' <summary>
        ''' Given a field declaration syntax, get the corresponding symbols.
        ''' </summary>
        ''' <param name="declarationSyntax">The syntax node that declares one or more fields.</param>
        ''' <returns>The field symbols that were declared.</returns>
        Friend Overrides Function GetDeclaredSymbols(declarationSyntax As FieldDeclarationSyntax, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of ISymbol)
            If declarationSyntax Is Nothing Then
                Throw New ArgumentNullException(NameOf(declarationSyntax))
            End If

            If Not IsInTree(declarationSyntax) Then
                Throw New ArgumentException(VBResources.DeclarationSyntaxNotWithinTree)
            End If

            Dim builder = New ArrayBuilder(Of ISymbol)

            For Each declarator In declarationSyntax.Declarators
                For Each identifier In declarator.Names
                    Dim field = TryCast(Me.GetDeclaredSymbol(identifier, cancellationToken), IFieldSymbol)
                    If field IsNot Nothing Then
                        builder.Add(field)
                    End If
                Next
            Next

            Return builder.ToImmutableAndFree()
        End Function

        ''' <summary>
        ''' Determines what type of conversion, if any, would be used if a given expression was converted to a given
        ''' type.
        ''' </summary>
        ''' <param name="expression">An expression which much occur within the syntax tree associated with this
        ''' object.</param>
        ''' <param name="destination">The type to attempt conversion to.</param>
        ''' <returns>Returns a Conversion object that summarizes whether the conversion was possible, and if so, what
        ''' kind of conversion it was. If no conversion was possible, a Conversion object with a false "Exists "
        ''' property is returned.</returns>
        ''' <remarks>To determine the conversion between two types (instead of an expression and a type), use
        ''' Compilation.ClassifyConversion.</remarks>
        Public Overrides Function ClassifyConversion(expression As ExpressionSyntax, destination As ITypeSymbol) As Conversion
            CheckSyntaxNode(expression)
            If destination Is Nothing Then
                Throw New ArgumentNullException(NameOf(destination))
            End If

            Dim vbdestination = destination.EnsureVbSymbolOrNothing(Of TypeSymbol)(NameOf(destination))

            ' TODO(cyrusn): Check arguments.  This is a public entrypoint, so we must do appropriate
            ' checks here.  However, no other methods in this type do any checking currently.  So I'm
            ' going to hold off on this until we do a full sweep of the API.
            Dim binding = Me.GetMemberSemanticModel(expression)
            If binding Is Nothing Then
                Return New Conversion(Nothing)  'NoConversion
            End If

            Return binding.ClassifyConversion(expression, vbdestination)
        End Function

        Public Overrides ReadOnly Property IsSpeculativeSemanticModel As Boolean
            Get
                Return False
            End Get
        End Property

        Public Overrides ReadOnly Property OriginalPositionForSpeculation As Integer
            Get
                Return 0
            End Get
        End Property

        Public Overrides ReadOnly Property ParentModel As SemanticModel
            Get
                Return Nothing
            End Get
        End Property

        Friend Overrides Function TryGetSpeculativeSemanticModelForMethodBodyCore(parentModel As SyntaxTreeSemanticModel, position As Integer, method As MethodBlockBaseSyntax, <Out> ByRef speculativeModel As PublicSemanticModel) As Boolean
            Dim memberModel = Me.GetMemberSemanticModel(position)
            If memberModel IsNot Nothing Then
                Return memberModel.TryGetSpeculativeSemanticModelForMethodBodyCore(parentModel, position, method, speculativeModel)
            End If

            speculativeModel = Nothing
            Return False
        End Function

        Friend Overrides Function TryGetSpeculativeSemanticModelCore(parentModel As SyntaxTreeSemanticModel, position As Integer, type As TypeSyntax, bindingOption As SpeculativeBindingOption, <Out> ByRef speculativeModel As PublicSemanticModel) As Boolean
            Dim memberModel = Me.GetMemberSemanticModel(position)
            If memberModel IsNot Nothing Then
                Return memberModel.TryGetSpeculativeSemanticModelCore(parentModel, position, type, bindingOption, speculativeModel)
            End If

            Dim binder As Binder = Me.GetSpeculativeBinderForExpression(position, type, bindingOption)
            If binder IsNot Nothing Then
                speculativeModel = SpeculativeSyntaxTreeSemanticModel.Create(Me, type, binder, position, bindingOption)
                Return True
            End If

            speculativeModel = Nothing
            Return False
        End Function

        Friend Overrides Function TryGetSpeculativeSemanticModelCore(parentModel As SyntaxTreeSemanticModel, position As Integer, rangeArgument As RangeArgumentSyntax, <Out> ByRef speculativeModel As PublicSemanticModel) As Boolean
            Dim memberModel = Me.GetMemberSemanticModel(position)
            If memberModel IsNot Nothing Then
                Return memberModel.TryGetSpeculativeSemanticModelCore(parentModel, position, rangeArgument, speculativeModel)
            End If

            speculativeModel = Nothing
            Return False
        End Function

        Friend Overrides Function TryGetSpeculativeSemanticModelCore(parentModel As SyntaxTreeSemanticModel, position As Integer, statement As ExecutableStatementSyntax, <Out> ByRef speculativeModel As PublicSemanticModel) As Boolean
            Dim memberModel = Me.GetMemberSemanticModel(position)
            If memberModel IsNot Nothing Then
                Return memberModel.TryGetSpeculativeSemanticModelCore(parentModel, position, statement, speculativeModel)
            End If

            speculativeModel = Nothing
            Return False
        End Function

        Friend Overrides Function TryGetSpeculativeSemanticModelCore(parentModel As SyntaxTreeSemanticModel, position As Integer, initializer As EqualsValueSyntax, <Out> ByRef speculativeModel As PublicSemanticModel) As Boolean
            Dim memberModel = Me.GetMemberSemanticModel(position)
            If memberModel IsNot Nothing Then
                Return memberModel.TryGetSpeculativeSemanticModelCore(parentModel, position, initializer, speculativeModel)
            End If

            speculativeModel = Nothing
            Return False
        End Function

        ''' <summary>
        ''' Analyze control-flow within a part of a method body.
        ''' </summary>
        ''' <param name="firstStatement">The first statement to be included in the analysis.</param>
        ''' <param name="lastStatement">The last statement to be included in the analysis.</param>
        ''' <returns>An object that can be used to obtain the result of the control flow analysis.</returns>
        ''' <exception cref="ArgumentException">The two statements are not contained within the same statement list.</exception>
        Public Overrides Function AnalyzeControlFlow(firstStatement As StatementSyntax, lastStatement As StatementSyntax) As ControlFlowAnalysis
            Dim context As RegionAnalysisContext = If(ValidateRegionDefiningStatementsRange(firstStatement, lastStatement),
                                                      CreateRegionAnalysisContext(firstStatement, lastStatement),
                                                      CreateFailedRegionAnalysisContext())

            Dim result = New VisualBasicControlFlowAnalysis(context)

            ' we assume the analysis should only fail if the original context is invalid
            Debug.Assert(result.Succeeded OrElse context.Failed)

            Return result
        End Function

        ''' <summary>
        ''' The first statement to be included in the analysis.
        ''' </summary>
        ''' <param name="firstStatement">The first statement to be included in the analysis.</param>
        ''' <param name="lastStatement">The last statement to be included in the analysis.</param>
        ''' <returns>An object that can be used to obtain the result of the data flow analysis.</returns>
        ''' <exception cref="ArgumentException">The two statements are not contained within the same statement list.</exception>
        Public Overrides Function AnalyzeDataFlow(firstStatement As StatementSyntax, lastStatement As StatementSyntax) As DataFlowAnalysis
            Dim context As RegionAnalysisContext = If(ValidateRegionDefiningStatementsRange(firstStatement, lastStatement),
                                                      CreateRegionAnalysisContext(firstStatement, lastStatement),
                                                      CreateFailedRegionAnalysisContext())

            Dim result = New VisualBasicDataFlowAnalysis(context)

            ' we assume the analysis should only fail if the original context is invalid
            Debug.Assert(result.Succeeded OrElse result.InvalidRegionDetectedInternal OrElse context.Failed)

            Return result
        End Function

        ''' <summary>
        ''' Analyze data-flow within an expression. 
        ''' </summary>
        ''' <param name="expression">The expression within the associated SyntaxTree to analyze.</param>
        ''' <returns>An object that can be used to obtain the result of the data flow analysis.</returns>
        Public Overrides Function AnalyzeDataFlow(expression As ExpressionSyntax) As DataFlowAnalysis
            Dim context As RegionAnalysisContext = If(ValidateRegionDefiningExpression(expression),
                                                      CreateRegionAnalysisContext(expression),
                                                      CreateFailedRegionAnalysisContext())

            Dim result = New VisualBasicDataFlowAnalysis(context)

            ' Assert that we either correctly precalculated succeeded 
            ' flag or we know for sure why we failed to precalculate it
            CheckSucceededFlagInAnalyzeDataFlow(expression, result, context)

            Return result
        End Function

        <Conditional("DEBUG")>
        Private Sub CheckSucceededFlagInAnalyzeDataFlow(expression As ExpressionSyntax, result As VisualBasicDataFlowAnalysis, context As RegionAnalysisContext)
            If result.Succeeded OrElse result.InvalidRegionDetectedInternal OrElse context.Failed Then
                Return
            End If

            ' Some cases of unsucceeded result that cannot be precalculated properly are handled below

            ' CASE 1: If the region flow analysis is performed on the left part of member access like 
            '         on 'a' part of 'a.b.c()' expression AND 'a.b' is a type, we don't create a bound node
            '         to be linked with syntax node 'a'. In this case FirstInRegion/LastInRegion nodes are 
            '         calculated by trying to bind 'a' itself, and in most cases it binds into Namespace
            '         or Type expression. This case is handles in RegionAnalysisContext..ctor.
            '
            '         But in Color/Color case it binds into other symbol kinds and we suppress 
            '         assertion for this case here
            Dim expressionParent As VisualBasicSyntaxNode = expression.Parent
            If expression.Kind = SyntaxKind.IdentifierName AndAlso
                    expressionParent IsNot Nothing AndAlso expressionParent.Kind = SyntaxKind.SimpleMemberAccessExpression AndAlso
                    DirectCast(expressionParent, MemberAccessExpressionSyntax).Expression Is expression Then

                ' Color/Color confusion may only be possible if expression is an IdentifierName
                ' and is nested in member access. This is too wide definition, but it's 
                ' difficult to improve this without doing semantic analysis
                Return
            End If

            ' CASE 2: If the region flow analysis is performed on the arguments of field declaration of array
            '         data type having explicit initializer, like 'Public AnArray(2) = {0, 1}'; 
            '         VB semantics generates an error about specifying both bounds and initializer and ignores them
            If expression.Kind = SyntaxKind.NumericLiteralExpression AndAlso
                    expressionParent IsNot Nothing AndAlso (expressionParent.Kind = SyntaxKind.SimpleArgument AndAlso Not DirectCast(expressionParent, SimpleArgumentSyntax).IsNamed) Then

                '           VariableDeclarator
                '          |                  |
                '  ModifiedIdentifier     EqualsValue
                '          |
                '  ArgumentList
                '      |...|...|
                '  SimpleArgument
                '          |
                '  NumericalLiteral

                Dim argList As VisualBasicSyntaxNode = expressionParent.Parent
                If argList IsNot Nothing AndAlso argList.Kind = SyntaxKind.ArgumentList Then
                    Dim modIdentifier As VisualBasicSyntaxNode = argList.Parent
                    If modIdentifier IsNot Nothing AndAlso modIdentifier.Kind = SyntaxKind.ModifiedIdentifier Then
                        Dim varDeclarator As VisualBasicSyntaxNode = modIdentifier.Parent
                        If varDeclarator IsNot Nothing AndAlso varDeclarator.Kind = SyntaxKind.VariableDeclarator AndAlso
                                DirectCast(varDeclarator, VariableDeclaratorSyntax).Initializer IsNot Nothing Then
                            Return
                        End If
                    End If
                End If
            End If

            Throw ExceptionUtilities.Unreachable
        End Sub

        ''' <summary>
        ''' Checks if the node is inside the attribute arguments 
        ''' </summary>
        Private Shared Function IsNodeInsideAttributeArguments(node As VisualBasicSyntaxNode) As Boolean
            While node IsNot Nothing
                If node.Kind = SyntaxKind.Attribute Then
                    Return True
                End If

                node = node.Parent
            End While
            Return False
        End Function

        ''' <summary>
        ''' Check Expression for being in right context, for example 'For ... Next [x]' 
        ''' is not correct context
        ''' </summary>
        Private Shared Function IsExpressionInValidContext(expression As ExpressionSyntax) As Boolean

            Dim currentNode As VisualBasicSyntaxNode = expression
            Do
                Dim parent As VisualBasicSyntaxNode = currentNode.Parent
                If parent Is Nothing Then Return True

                Dim expressionParent = TryCast(parent, ExpressionSyntax)
                If expressionParent Is Nothing Then
                    Select Case parent.Kind

                        Case SyntaxKind.NextStatement
                            Return False

                        Case SyntaxKind.EqualsValue
                            ' One cannot perform flow analysis on an expression from Enum member declaration
                            parent = parent.Parent
                            If parent Is Nothing Then
                                Return True
                            End If

                            Select Case parent.Kind
                                Case SyntaxKind.EnumMemberDeclaration,
                                     SyntaxKind.Parameter
                                    Return False

                                Case SyntaxKind.VariableDeclarator
                                    Dim localDeclSyntax = TryCast(parent.Parent, LocalDeclarationStatementSyntax)
                                    If localDeclSyntax IsNot Nothing Then
                                        For Each modifier In localDeclSyntax.Modifiers
                                            Select Case modifier.Kind
                                                Case SyntaxKind.ConstKeyword
                                                    Return False
                                            End Select
                                        Next
                                    End If
                                    Return True

                                Case Else
                                    Return True
                            End Select

                        Case SyntaxKind.RaiseEventStatement
                            Return False

                        Case SyntaxKind.NamedFieldInitializer
                            If DirectCast(parent, NamedFieldInitializerSyntax).Name Is currentNode Then
                                Return False
                            End If
                        ' else proceed to the upper-level node

                        Case SyntaxKind.NameColonEquals
                            Return False

                        Case SyntaxKind.RangeArgument
                            If DirectCast(parent, RangeArgumentSyntax).LowerBound Is currentNode Then
                                Return False
                            End If
                        ' proceed to the upper-level node

                        Case SyntaxKind.ArgumentList,
                             SyntaxKind.SimpleArgument,
                             SyntaxKind.ObjectMemberInitializer
                            ' proceed to the upper-level node

                        Case SyntaxKind.GoToStatement
                            Return False

                        Case SyntaxKind.XmlDeclarationOption
                            Return False

                        Case Else
                            Return True

                    End Select

                Else
                    Select Case parent.Kind
                        Case SyntaxKind.XmlElementEndTag
                            Return False
                    End Select
                End If

                ' up one level
                currentNode = parent
            Loop

        End Function

        Private Sub AssertNodeInTree(node As VisualBasicSyntaxNode, argName As String)
            If node Is Nothing Then
                Throw New ArgumentNullException(argName)
            End If

            If Not IsInTree(node) Then
                Throw New ArgumentException(argName & VBResources.NotWithinTree)
            End If
        End Sub

        Private Function ValidateRegionDefiningExpression(expression As ExpressionSyntax) As Boolean
            AssertNodeInTree(expression, NameOf(expression))

            If expression.Kind = SyntaxKind.PredefinedType OrElse SyntaxFacts.IsInNamespaceOrTypeContext(expression) Then
                Return False
            End If

            If SyntaxFactory.GetStandaloneExpression(expression) IsNot expression Then
                Return False
            End If

            ' Check for pseudo-expressions
            Select Case expression.Kind
                Case SyntaxKind.CollectionInitializer
                    Dim parent As VisualBasicSyntaxNode = expression.Parent

                    If parent IsNot Nothing Then
                        Select Case parent.Kind
                            Case SyntaxKind.ObjectCollectionInitializer
                                If DirectCast(parent, ObjectCollectionInitializerSyntax).Initializer Is expression Then
                                    Return False
                                End If

                            Case SyntaxKind.ArrayCreationExpression
                                If DirectCast(parent, ArrayCreationExpressionSyntax).Initializer Is expression Then
                                    Return False
                                End If

                            Case SyntaxKind.CollectionInitializer
                                ' Nested collection initializer is not an expression from the language point of view.
                                ' However, third level collection initializer under ObjectCollectionInitializer should
                                ' be treated as a stand alone expression.
                                Dim possibleSecondLevelInitializer As VisualBasicSyntaxNode = parent
                                parent = parent.Parent

                                If parent IsNot Nothing AndAlso parent.Kind = SyntaxKind.CollectionInitializer Then
                                    Dim possibleFirstLevelInitializer As VisualBasicSyntaxNode = parent
                                    parent = parent.Parent

                                    If parent IsNot Nothing AndAlso parent.Kind = SyntaxKind.ObjectCollectionInitializer AndAlso
                                       DirectCast(parent, ObjectCollectionInitializerSyntax).Initializer Is possibleFirstLevelInitializer Then
                                        Exit Select
                                    End If
                                End If

                                Return False
                        End Select
                    End If

                Case SyntaxKind.NumericLabel,
                     SyntaxKind.IdentifierLabel,
                     SyntaxKind.NextLabel
                    Return False
            End Select

            If Not IsExpressionInValidContext(expression) OrElse IsNodeInsideAttributeArguments(expression) Then
                Return False
            End If

            Return True
        End Function

        Private Function ValidateRegionDefiningStatementsRange(firstStatement As StatementSyntax, lastStatement As StatementSyntax) As Boolean
            AssertNodeInTree(firstStatement, NameOf(firstStatement))
            AssertNodeInTree(lastStatement, NameOf(lastStatement))

            If firstStatement.Parent Is Nothing OrElse firstStatement.Parent IsNot lastStatement.Parent Then
                Throw New ArgumentException("statements not within the same statement list")
            End If

            If firstStatement.SpanStart > lastStatement.SpanStart Then
                Throw New ArgumentException("first statement does not precede last statement")
            End If

            If Not TypeOf firstStatement Is ExecutableStatementSyntax OrElse Not TypeOf lastStatement Is ExecutableStatementSyntax Then
                Return False
            End If

            ' Test for |For ... Next x, y|
            If IsNotUppermostForBlock(firstStatement) Then
                Return False
            End If

            If firstStatement IsNot lastStatement AndAlso IsNotUppermostForBlock(lastStatement) Then
                Return False
            End If

            If IsNodeInsideAttributeArguments(firstStatement) OrElse (firstStatement IsNot lastStatement AndAlso IsNodeInsideAttributeArguments(lastStatement)) Then
                Return False
            End If

            Return True
        End Function

        ''' <summary>
        ''' Check ForBlockSyntax for being the uppermost For block. By uppermost 
        ''' For block we mean that if Next clause contains several control variables,
        ''' the uppermost block is the one which includes all the For blocks ending with 
        ''' the same Next clause
        ''' </summary>
        Private Function IsNotUppermostForBlock(forBlockOrStatement As VisualBasicSyntaxNode) As Boolean
            Debug.Assert(forBlockOrStatement.Kind <> SyntaxKind.ForStatement)
            Debug.Assert(forBlockOrStatement.Kind <> SyntaxKind.ForEachStatement)

            Dim forBlock = TryCast(forBlockOrStatement, ForOrForEachBlockSyntax)
            If forBlock Is Nothing Then
                Return False
            End If

            Dim endNode As NextStatementSyntax = forBlock.NextStatement

            If endNode IsNot Nothing Then
                ' The only case where the statement is valid is this case is 
                ' that the Next clause contains one single control variable (or none)
                Return endNode.ControlVariables.Count > 1
            End If

            ' go down the For statements chain until the last and ensure it has as many 
            ' variables as there were nested For statements
            Dim nesting As Integer = 1
            Do
                If forBlock.Statements.Count = 0 Then
                    Return True
                End If

                Dim lastStatement = TryCast(forBlock.Statements.Last(), ForOrForEachBlockSyntax)
                If lastStatement Is Nothing Then
                    Return True
                End If

                nesting += 1
                endNode = lastStatement.NextStatement

                If endNode IsNot Nothing Then
                    Return endNode.ControlVariables.Count <> nesting
                End If

                ' Else - next level
                forBlock = lastStatement
            Loop
        End Function

        ''' <summary>
        ''' Gets the semantic information of a for each statement.
        ''' </summary>
        ''' <param name="node">The for each syntax node.</param>
        Friend Overrides Function GetForEachStatementInfoWorker(node As ForEachBlockSyntax) As ForEachStatementInfo
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(node)

            If model IsNot Nothing Then
                Return model.GetForEachStatementInfoWorker(node)
            Else
                Return Nothing
            End If
        End Function

        Friend Overrides Function GetAwaitExpressionInfoWorker(awaitExpression As AwaitExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As AwaitExpressionInfo
            Dim model As MemberSemanticModel = Me.GetMemberSemanticModel(awaitExpression)

            If model IsNot Nothing Then
                Return model.GetAwaitExpressionInfoWorker(awaitExpression, cancellationToken)
            Else
                Return Nothing
            End If
        End Function

#Region "Region Analysis Context Creation"

        ''' <summary> Used to create a region analysis context 
        ''' with failed flag set to be used in 'failed' scenarios </summary>
        Private Function CreateFailedRegionAnalysisContext() As RegionAnalysisContext
            Return New RegionAnalysisContext(Me.Compilation)
        End Function

        Private Function CreateRegionAnalysisContext(expression As ExpressionSyntax) As RegionAnalysisContext
            Dim region As TextSpan = expression.Span

            Dim memberModel As MemberSemanticModel = GetMemberSemanticModel(expression)
            If memberModel Is Nothing Then
                ' Recover from error cases
                Dim node As BoundBadStatement = New BoundBadStatement(expression, ImmutableArray(Of BoundNode).Empty)
                Return New RegionAnalysisContext(Compilation, Nothing, node, node, node, region)
            End If

            Dim boundNode As BoundNode = memberModel.GetBoundRoot()
            Dim boundExpression As BoundNode = memberModel.GetUpperBoundNode(expression)

            Return New RegionAnalysisContext(Compilation, memberModel.MemberSymbol, boundNode, boundExpression, boundExpression, region)
        End Function

        Private Function CreateRegionAnalysisContext(firstStatement As StatementSyntax, lastStatement As StatementSyntax) As RegionAnalysisContext
            Dim region As TextSpan = TextSpan.FromBounds(firstStatement.SpanStart, lastStatement.Span.End)

            Dim memberModel As MemberSemanticModel = GetMemberSemanticModel(firstStatement)
            If memberModel Is Nothing Then
                ' Recover from error cases
                Dim node As BoundBadStatement = New BoundBadStatement(firstStatement, ImmutableArray(Of BoundNode).Empty)
                Return New RegionAnalysisContext(Compilation, Nothing, node, node, node, region)
            End If

            Dim boundNode As BoundNode = memberModel.GetBoundRoot()
            Dim firstBoundNode As BoundNode = memberModel.GetUpperBoundNode(firstStatement)
            Dim lastBoundNode As BoundNode = memberModel.GetUpperBoundNode(lastStatement)

            Return New RegionAnalysisContext(Compilation, memberModel.MemberSymbol, boundNode, firstBoundNode, lastBoundNode, region)
        End Function

#End Region

    End Class
End Namespace
