|
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.Collections
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
Imports ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer
Namespace Microsoft.CodeAnalysis.VisualBasic
''' <summary>
''' Binding info for expressions and statements that are part of a member declaration.
''' Instances of this class should not be exposed to external consumers.
''' </summary>
Partial Friend MustInherit Class MemberSemanticModel
Inherits VBSemanticModel
Private ReadOnly _root As SyntaxNode
Private ReadOnly _rootBinder As Binder
Private ReadOnly _containingPublicSemanticModel As PublicSemanticModel
Private ReadOnly _operationFactory As Lazy(Of VisualBasicOperationFactory)
Friend Sub New(root As SyntaxNode,
rootBinder As Binder,
containingPublicSemanticModel As PublicSemanticModel)
Debug.Assert(containingPublicSemanticModel IsNot Nothing)
_root = root
_rootBinder = SemanticModelBinder.Mark(rootBinder, containingPublicSemanticModel.IgnoresAccessibility)
_containingPublicSemanticModel = containingPublicSemanticModel
_operationFactory = New Lazy(Of VisualBasicOperationFactory)(Function() New VisualBasicOperationFactory(Me))
End Sub
Friend ReadOnly Property RootBinder As Binder
Get
Return _rootBinder
End Get
End Property
Friend NotOverridable Overrides ReadOnly Property Root As SyntaxNode
Get
Return _root
End Get
End Property
Public NotOverridable Overrides ReadOnly Property IsSpeculativeSemanticModel As Boolean
Get
Return _containingPublicSemanticModel.IsSpeculativeSemanticModel
End Get
End Property
Public NotOverridable Overrides ReadOnly Property OriginalPositionForSpeculation As Integer
Get
' This property is not meaningful for member semantic models.
' An external consumer should never be able to access them directly.
Throw ExceptionUtilities.Unreachable()
End Get
End Property
Public NotOverridable Overrides ReadOnly Property ParentModel As SemanticModel
Get
' This property is not meaningful for member semantic models.
' An external consumer should never be able to access them directly.
Throw ExceptionUtilities.Unreachable()
End Get
End Property
Friend NotOverridable Overrides ReadOnly Property ContainingPublicModelOrSelf As SemanticModel
Get
Return _containingPublicSemanticModel
End Get
End Property
Public NotOverridable Overrides ReadOnly Property IgnoresAccessibility As Boolean
Get
Return _containingPublicSemanticModel.IgnoresAccessibility
End Get
End Property
Friend NotOverridable Overloads Overrides Function GetEnclosingBinder(position As Integer) As Binder
Dim binder = GetEnclosingBinderInternal(Me.RootBinder, Me.Root, FindInitialNodeFromPosition(position), position)
Debug.Assert(binder IsNot Nothing)
Return SemanticModelBinder.Mark(binder, IgnoresAccessibility)
End Function
Private Overloads Function GetEnclosingBinder(node As SyntaxNode) As Binder
Dim binder = GetEnclosingBinderInternal(Me.RootBinder, Me.Root, node, node.SpanStart)
Debug.Assert(binder IsNot Nothing)
Return SemanticModelBinder.Mark(binder, IgnoresAccessibility)
End Function
' Get the bound node corresponding to the root.
Friend Overridable Function GetBoundRoot() As BoundNode
Return GetUpperBoundNode(Me.Root)
End Function
Public Overrides Function ClassifyConversion(expression As ExpressionSyntax, destination As ITypeSymbol) As Conversion
CheckSyntaxNode(expression)
expression = SyntaxFactory.GetStandaloneExpression(expression)
If destination Is Nothing Then
Throw New ArgumentNullException(NameOf(destination))
End If
Dim vbDestination = destination.EnsureVbSymbolOrNothing(Of TypeSymbol)(NameOf(destination))
Dim boundExpression = TryCast(Me.GetLowerBoundNode(expression), BoundExpression)
If boundExpression Is Nothing OrElse vbDestination.IsErrorType() Then
Return New Conversion(Nothing) ' NoConversion
End If
Select Case boundExpression.Kind
Case BoundKind.Lambda
' Switch back to the unbound lambda node since bound lambda represents a lambda already
' converted to whatever target type was provided by the context within the statement.
' NOTE: Using the UnboundLambda in this way might result in new entries in its trial-binding
' cache, but that won't affect future semantic model queries because it will already have
' been bound (possibly for error recovery) on insertion into the syntax-to-bound-node map
' and the result of that binding is also cached. That is, even though the list of trial-bindings
' may change, it will never again be consumed for error recovery (only as a cache), so there
' should be no problems.
Dim sourceLambda = TryCast(DirectCast(boundExpression, BoundLambda).LambdaSymbol, SourceLambdaSymbol)
' Can we get a synthesized lambda here? Handle the case that we do just in case.
Debug.Assert(sourceLambda IsNot Nothing)
If sourceLambda IsNot Nothing Then
boundExpression = sourceLambda.UnboundLambda
End If
Case BoundKind.ArrayCreation
' Switch back to the array literal node when we have it
Dim arrayLiteral = DirectCast(boundExpression, BoundArrayCreation).ArrayLiteralOpt
If arrayLiteral IsNot Nothing Then
boundExpression = arrayLiteral
End If
End Select
Return New Conversion(Conversions.ClassifyConversion(boundExpression, vbDestination, GetEnclosingBinder(boundExpression.Syntax), CompoundUseSiteInfo(Of AssemblySymbol).Discarded))
End Function
''' <summary>
''' Get the highest bound node in the tree associated with a particular syntax node.
''' </summary>
Friend Function GetUpperBoundNode(node As SyntaxNode) As BoundNode
' The bound nodes are stored in the map from highest to lowest, so the first bound node is the highest.
Dim boundNodes = GetBoundNodes(node)
If boundNodes.Length = 0 Then
Return Nothing
Else
Return boundNodes(0)
End If
End Function
''' <summary>
''' Get the lowest bound node in the tree associated with a particular syntax node. Lowest is defined as last
''' in a pre-order traversal of the bound tree.
''' </summary>
Friend Function GetLowerBoundNode(node As VisualBasicSyntaxNode) As BoundNode
' The bound nodes are stored in the map from highest to lowest, so the last bound node is the lowest.
Dim boundNodes = GetBoundNodes(node)
If boundNodes.Length = 0 Then
Return Nothing
Else
Return boundNodes(boundNodes.Length - 1)
End If
End Function
''' <summary>
''' If node has an immediate parent that is an expression or statement or attribute, return
''' that (making sure it can be bound on its own). Otherwise return Nothing.
''' </summary>
Protected Function GetBindableParent(node As VisualBasicSyntaxNode) As VisualBasicSyntaxNode
Dim parent As VisualBasicSyntaxNode = node.Parent
If parent Is Nothing OrElse node Is Me.Root Then
Return Nothing
End If
Dim expressionSyntax = TryCast(parent, ExpressionSyntax)
If expressionSyntax IsNot Nothing Then
Return SyntaxFactory.GetStandaloneExpression(expressionSyntax)
End If
Dim statementSyntax = TryCast(parent, StatementSyntax)
If statementSyntax IsNot Nothing AndAlso IsStandaloneStatement(statementSyntax) Then
Return statementSyntax
End If
Dim attributeSyntax = TryCast(parent, AttributeSyntax)
If attributeSyntax IsNot Nothing Then
Return attributeSyntax
End If
Return Nothing
End Function
''' <summary>
''' Get a summary of the bound nodes associated with a particular syntax nodes,
''' and its parent. This is what the rest of the semantic model uses to determine
''' what to return back.
''' </summary>
Friend Function GetBoundNodeSummary(node As VisualBasicSyntaxNode) As BoundNodeSummary
' TODO: Should these two call be merged into a single one for efficiency?
Dim upperBound = GetUpperBoundNode(node)
Dim lowerBound = GetLowerBoundNode(node)
Dim parentSyntax As VisualBasicSyntaxNode = GetBindableParent(node)
Dim lowerBoundOfParent = If(parentSyntax Is Nothing, Nothing, GetLowerBoundNode(parentSyntax))
Return New BoundNodeSummary(lowerBound, upperBound, lowerBoundOfParent)
End Function
''' <summary>
''' Gets a summary of the bound nodes associated with an underlying
''' bound call node for a raiseevent statement.
''' </summary>
Friend Overrides Function GetInvokeSummaryForRaiseEvent(node As RaiseEventStatementSyntax) As BoundNodeSummary
Dim upperBound = UnwrapRaiseEvent(GetUpperBoundNode(node))
Dim lowerBound = UnwrapRaiseEvent(GetLowerBoundNode(node))
Dim parentSyntax As VisualBasicSyntaxNode = GetBindableParent(node)
Dim lowerBoundOfParent = If(parentSyntax Is Nothing, Nothing, UnwrapRaiseEvent(GetLowerBoundNode(parentSyntax)))
Return New BoundNodeSummary(lowerBound, upperBound, lowerBoundOfParent)
End Function
''' <summary>
''' if "node" argument is a BoundRaiseEvent, returns its underlying boundcall instead.
''' Otherwise returns "node" unchanged.
''' </summary>
Private Shared Function UnwrapRaiseEvent(node As BoundNode) As BoundNode
Dim asRaiseEvent = TryCast(node, BoundRaiseEventStatement)
If asRaiseEvent IsNot Nothing Then
Return asRaiseEvent.EventInvocation
End If
Return node
End Function
''' <summary>
''' Return True if the statement can be bound by a Binder on its own.
''' For example Catch statement cannot be bound on its own, only
''' as part of Try block. Similarly, Next statement cannot be bound on its own,
''' only as part of For statement.
'''
''' Only handles statements that are in executable code.
''' </summary>
Private Shared Function IsStandaloneStatement(node As StatementSyntax) As Boolean
Select Case node.Kind
Case SyntaxKind.EmptyStatement,
SyntaxKind.SimpleAssignmentStatement,
SyntaxKind.AddAssignmentStatement,
SyntaxKind.SubtractAssignmentStatement,
SyntaxKind.MultiplyAssignmentStatement,
SyntaxKind.DivideAssignmentStatement,
SyntaxKind.IntegerDivideAssignmentStatement,
SyntaxKind.ExponentiateAssignmentStatement,
SyntaxKind.LeftShiftAssignmentStatement,
SyntaxKind.RightShiftAssignmentStatement,
SyntaxKind.ConcatenateAssignmentStatement,
SyntaxKind.CallStatement,
SyntaxKind.GoToStatement,
SyntaxKind.LabelStatement,
SyntaxKind.SingleLineIfStatement,
SyntaxKind.MidAssignmentStatement,
SyntaxKind.MultiLineIfBlock,
SyntaxKind.SelectBlock,
SyntaxKind.UsingBlock,
SyntaxKind.SyncLockBlock,
SyntaxKind.LocalDeclarationStatement,
SyntaxKind.SimpleDoLoopBlock,
SyntaxKind.DoWhileLoopBlock,
SyntaxKind.DoUntilLoopBlock,
SyntaxKind.DoLoopWhileBlock,
SyntaxKind.DoLoopUntilBlock,
SyntaxKind.WhileBlock,
SyntaxKind.ForBlock,
SyntaxKind.ForEachBlock,
SyntaxKind.TryBlock,
SyntaxKind.WithBlock,
SyntaxKind.ExitDoStatement,
SyntaxKind.ExitForStatement,
SyntaxKind.ExitSelectStatement,
SyntaxKind.ExitTryStatement,
SyntaxKind.ExitWhileStatement,
SyntaxKind.ExitFunctionStatement,
SyntaxKind.ExitSubStatement,
SyntaxKind.ExitOperatorStatement,
SyntaxKind.ExitPropertyStatement,
SyntaxKind.ContinueDoStatement,
SyntaxKind.ContinueForStatement,
SyntaxKind.ContinueWhileStatement,
SyntaxKind.ReturnStatement,
SyntaxKind.ThrowStatement,
SyntaxKind.SubBlock,
SyntaxKind.FunctionBlock,
SyntaxKind.ConstructorBlock,
SyntaxKind.GetAccessorBlock,
SyntaxKind.SetAccessorBlock,
SyntaxKind.OperatorBlock,
SyntaxKind.AddHandlerAccessorBlock, SyntaxKind.RemoveHandlerAccessorBlock, SyntaxKind.RaiseEventAccessorBlock,
SyntaxKind.ReDimStatement,
SyntaxKind.ReDimPreserveStatement,
SyntaxKind.EraseStatement,
SyntaxKind.ErrorStatement,
SyntaxKind.OnErrorGoToZeroStatement,
SyntaxKind.OnErrorGoToMinusOneStatement,
SyntaxKind.OnErrorGoToLabelStatement,
SyntaxKind.OnErrorResumeNextStatement,
SyntaxKind.ResumeStatement,
SyntaxKind.ResumeLabelStatement,
SyntaxKind.ResumeNextStatement,
SyntaxKind.EndStatement,
SyntaxKind.StopStatement,
SyntaxKind.AddHandlerStatement,
SyntaxKind.RemoveHandlerStatement,
SyntaxKind.RaiseEventStatement,
SyntaxKind.ExpressionStatement,
SyntaxKind.YieldStatement,
SyntaxKind.PrintStatement,
SyntaxKind.OptionStatement
Return True
Case SyntaxKind.IfStatement,
SyntaxKind.ElseStatement,
SyntaxKind.ElseIfStatement,
SyntaxKind.EndIfStatement,
SyntaxKind.WithStatement,
SyntaxKind.EndWithStatement,
SyntaxKind.SelectStatement,
SyntaxKind.CaseElseStatement,
SyntaxKind.CaseStatement,
SyntaxKind.EndSelectStatement,
SyntaxKind.EndSubStatement,
SyntaxKind.EndFunctionStatement,
SyntaxKind.EndOperatorStatement,
SyntaxKind.WhileStatement,
SyntaxKind.EndWhileStatement,
SyntaxKind.TryStatement,
SyntaxKind.CatchStatement,
SyntaxKind.FinallyStatement,
SyntaxKind.EndTryStatement,
SyntaxKind.SyncLockStatement,
SyntaxKind.EndSyncLockStatement,
SyntaxKind.ForStatement,
SyntaxKind.ForEachStatement,
SyntaxKind.NextStatement,
SyntaxKind.SimpleDoStatement, SyntaxKind.DoWhileStatement, SyntaxKind.DoUntilStatement,
SyntaxKind.SimpleLoopStatement, SyntaxKind.LoopWhileStatement, SyntaxKind.LoopUntilStatement,
SyntaxKind.UsingStatement,
SyntaxKind.EndUsingStatement,
SyntaxKind.SubLambdaHeader,
SyntaxKind.FunctionLambdaHeader,
SyntaxKind.SubStatement, SyntaxKind.FunctionStatement,
SyntaxKind.FieldDeclaration,
SyntaxKind.SubNewStatement,
SyntaxKind.DeclareSubStatement, SyntaxKind.DeclareFunctionStatement,
SyntaxKind.DelegateFunctionStatement, SyntaxKind.DelegateSubStatement,
SyntaxKind.EventStatement,
SyntaxKind.OperatorStatement,
SyntaxKind.PropertyStatement,
SyntaxKind.GetAccessorStatement, SyntaxKind.SetAccessorStatement,
SyntaxKind.AddHandlerAccessorStatement, SyntaxKind.RemoveHandlerAccessorStatement, SyntaxKind.RaiseEventAccessorStatement,
SyntaxKind.EndNamespaceStatement,
SyntaxKind.EndModuleStatement,
SyntaxKind.EndClassStatement,
SyntaxKind.EndStructureStatement,
SyntaxKind.EndInterfaceStatement,
SyntaxKind.EndEnumStatement,
SyntaxKind.EndSubStatement,
SyntaxKind.EndFunctionStatement,
SyntaxKind.EndOperatorStatement,
SyntaxKind.EndPropertyStatement,
SyntaxKind.EndGetStatement,
SyntaxKind.EndSetStatement,
SyntaxKind.EndEventStatement,
SyntaxKind.EndAddHandlerStatement,
SyntaxKind.EndRemoveHandlerStatement,
SyntaxKind.EndRaiseEventStatement,
SyntaxKind.IncompleteMember,
SyntaxKind.InheritsStatement,
SyntaxKind.ImplementsStatement,
SyntaxKind.ImportsStatement,
SyntaxKind.EnumMemberDeclaration
Return False
Case Else
' Unexpected statement kind; add to either stand-alone or non-standalone list.
Throw ExceptionUtilities.UnexpectedValue(node.Kind)
End Select
Return True
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 NotOverridable Overrides Function GetSyntaxDiagnostics(Optional span As TextSpan? = Nothing, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Diagnostic)
Throw New NotSupportedException()
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 NotOverridable Overrides Function GetDeclarationDiagnostics(Optional span As TextSpan? = Nothing, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Diagnostic)
Throw New NotSupportedException()
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 NotOverridable Overrides Function GetMethodBodyDiagnostics(Optional span As TextSpan? = Nothing, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Diagnostic)
Throw New NotSupportedException()
End Function
''' <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.
''' </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 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 NotOverridable Overrides Function GetDiagnostics(Optional span As TextSpan? = Nothing, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Diagnostic)
Throw New NotSupportedException()
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
' Can't define type inside member
Return Nothing
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
' Can't define enum inside member
Return Nothing
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
' Can't define namespace inside member
Return Nothing
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
' Can't define method inside member
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
' This could be a lambda parameter.
Dim parent As VisualBasicSyntaxNode = parameter.Parent
Dim paramList As ParameterListSyntax = TryCast(parameter.Parent, ParameterListSyntax)
If parent IsNot Nothing AndAlso parent.Kind = SyntaxKind.ParameterList Then
Dim lambdaHeader = TryCast(parent.Parent, LambdaHeaderSyntax)
If lambdaHeader IsNot Nothing Then
Dim lambdaSyntax = TryCast(lambdaHeader.Parent, LambdaExpressionSyntax)
If lambdaSyntax IsNot Nothing Then
' We should always be able to get at least an error binding for a lambda, so assert
' if this isn't true.
Dim boundlambda = TryCast(GetLowerBoundNode(lambdaSyntax), BoundLambda)
Debug.Assert(boundlambda IsNot Nothing)
If boundlambda IsNot Nothing Then
For Each symbol In boundlambda.LambdaSymbol.Parameters
For Each location In symbol.Locations
If parameter.Span.Contains(location.SourceSpan) Then
Return symbol
End If
Next
Next
End If
End If
End If
End If
Return Nothing
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
' Can't define alias inside member
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
' Can't define type parameter inside member
Return Nothing
End Function
Public Overrides Function GetDeclaredSymbol(declarationSyntax As EnumMemberDeclarationSyntax, Optional cancellationToken As CancellationToken = Nothing) As IFieldSymbol
' Can't define enum member inside member
Return Nothing
End Function
Public Overrides Function GetDeclaredSymbol(identifierSyntax As ModifiedIdentifierSyntax, Optional cancellationToken As CancellationToken = Nothing) As ISymbol
If identifierSyntax Is Nothing Then
Throw New ArgumentNullException(NameOf(identifierSyntax))
End If
If Not IsInTree(identifierSyntax) Then
Throw New ArgumentException(VBResources.IdentifierSyntaxNotWithinSyntaxTree)
End If
Dim parent As VisualBasicSyntaxNode = identifierSyntax.Parent
If parent IsNot Nothing Then
Select Case parent.Kind
Case SyntaxKind.CollectionRangeVariable
Return GetDeclaredSymbol(DirectCast(parent, CollectionRangeVariableSyntax), cancellationToken)
Case SyntaxKind.VariableNameEquals
parent = parent.Parent
If parent IsNot Nothing Then
Select Case parent.Kind
Case SyntaxKind.ExpressionRangeVariable
Return GetDeclaredSymbol(DirectCast(parent, ExpressionRangeVariableSyntax), cancellationToken)
Case SyntaxKind.AggregationRangeVariable
Return GetDeclaredSymbol(DirectCast(parent, AggregationRangeVariableSyntax), cancellationToken)
End Select
End If
End Select
End If
Return MyBase.GetDeclaredSymbol(identifierSyntax, cancellationToken)
End Function
Public Overrides Function GetDeclaredSymbol(anonymousObjectCreationExpressionSyntax As AnonymousObjectCreationExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As INamedTypeSymbol
If anonymousObjectCreationExpressionSyntax Is Nothing Then
Throw New ArgumentNullException(NameOf(anonymousObjectCreationExpressionSyntax))
End If
If Not IsInTree(anonymousObjectCreationExpressionSyntax) Then
Throw New ArgumentException(VBResources.AnonymousObjectCreationExpressionSyntaxNotWithinTree)
End If
Dim boundExpression = TryCast(GetLowerBoundNode(anonymousObjectCreationExpressionSyntax), BoundExpression)
If boundExpression Is Nothing Then
Return Nothing
End If
Return TryCast(boundExpression.Type, AnonymousTypeManager.AnonymousTypePublicSymbol)
End Function
Public Overrides Function GetDeclaredSymbol(fieldInitializerSyntax As FieldInitializerSyntax, Optional cancellationToken As System.Threading.CancellationToken = Nothing) As IPropertySymbol
If fieldInitializerSyntax Is Nothing Then
Throw New ArgumentNullException(NameOf(fieldInitializerSyntax))
End If
If Not IsInTree(fieldInitializerSyntax) Then
Throw New ArgumentException(VBResources.FieldInitializerSyntaxNotWithinSyntaxTree)
End If
Dim parentInitializer = TryCast(fieldInitializerSyntax.Parent, ObjectMemberInitializerSyntax)
If parentInitializer Is Nothing Then
Return Nothing
End If
Dim anonymousObjectCreation = TryCast(parentInitializer.Parent, AnonymousObjectCreationExpressionSyntax)
If anonymousObjectCreation Is Nothing Then
Return Nothing
End If
Dim boundExpression = TryCast(GetLowerBoundNode(anonymousObjectCreation), BoundExpression)
If boundExpression Is Nothing Then
Return Nothing
End If
Dim anonymousType = TryCast(boundExpression.Type, AnonymousTypeManager.AnonymousTypePublicSymbol)
If anonymousType Is Nothing Then
Return Nothing
End If
Dim index = parentInitializer.Initializers.IndexOf(fieldInitializerSyntax)
Debug.Assert(index >= 0)
Debug.Assert(index < parentInitializer.Initializers.Count)
Debug.Assert(index < anonymousType.Properties.Length)
Return anonymousType.Properties(index)
End Function
Public Overrides Function GetDeclaredSymbol(rangeVariableSyntax As CollectionRangeVariableSyntax, Optional cancellationToken As CancellationToken = Nothing) As IRangeVariableSymbol
If rangeVariableSyntax Is Nothing Then
Throw New ArgumentNullException(NameOf(rangeVariableSyntax))
End If
If Not IsInTree(rangeVariableSyntax) Then
Throw New ArgumentException(VBResources.IdentifierSyntaxNotWithinSyntaxTree)
End If
Dim bound As BoundNode = GetLowerBoundNode(rangeVariableSyntax)
If bound IsNot Nothing AndAlso bound.Kind = BoundKind.QueryableSource Then
Dim queryableSource = DirectCast(bound, BoundQueryableSource)
If queryableSource.RangeVariableOpt IsNot Nothing Then
Return queryableSource.RangeVariableOpt
End If
End If
Return MyBase.GetDeclaredSymbol(rangeVariableSyntax, cancellationToken)
End Function
Public Overrides Function GetDeclaredSymbol(rangeVariableSyntax As ExpressionRangeVariableSyntax, Optional cancellationToken As CancellationToken = Nothing) As IRangeVariableSymbol
If rangeVariableSyntax Is Nothing Then
Throw New ArgumentNullException(NameOf(rangeVariableSyntax))
End If
If Not IsInTree(rangeVariableSyntax) Then
Throw New ArgumentException(VBResources.IdentifierSyntaxNotWithinSyntaxTree)
End If
Dim bound As BoundNode = GetLowerBoundNode(rangeVariableSyntax)
If bound IsNot Nothing AndAlso bound.Kind = BoundKind.RangeVariableAssignment Then
Return DirectCast(bound, BoundRangeVariableAssignment).RangeVariable
End If
Return MyBase.GetDeclaredSymbol(rangeVariableSyntax, cancellationToken)
End Function
Public Overrides Function GetDeclaredSymbol(rangeVariableSyntax As AggregationRangeVariableSyntax, Optional cancellationToken As CancellationToken = Nothing) As IRangeVariableSymbol
If rangeVariableSyntax Is Nothing Then
Throw New ArgumentNullException(NameOf(rangeVariableSyntax))
End If
If Not IsInTree(rangeVariableSyntax) Then
Throw New ArgumentException(VBResources.IdentifierSyntaxNotWithinSyntaxTree)
End If
Dim bound As BoundNode = GetLowerBoundNode(rangeVariableSyntax)
If bound IsNot Nothing AndAlso bound.Kind = BoundKind.RangeVariableAssignment Then
Return DirectCast(bound, BoundRangeVariableAssignment).RangeVariable
End If
Return MyBase.GetDeclaredSymbol(rangeVariableSyntax, cancellationToken)
End Function
Friend Overrides Function GetDeclaredSymbols(declarationSyntax As FieldDeclarationSyntax, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of ISymbol)
' Can't define field inside member
Return ImmutableArray.Create(Of ISymbol)()
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 boundForEach = DirectCast(GetUpperBoundNode(node), BoundForEachStatement)
If boundForEach IsNot Nothing Then
Return GetForEachStatementInfo(boundForEach, Compilation,
getEnumeratorArguments:=Nothing,
getEnumeratorDefaultArguments:=Nothing,
moveNextArguments:=Nothing,
moveNextDefaultArguments:=Nothing,
currentArguments:=Nothing,
currentDefaultArguments:=Nothing)
Else
Return Nothing
End If
End Function
Friend Overloads Shared Function GetForEachStatementInfo(
boundForEach As BoundForEachStatement,
compilation As VisualBasicCompilation,
<Out> ByRef getEnumeratorArguments As ImmutableArray(Of BoundExpression),
<Out> ByRef getEnumeratorDefaultArguments As BitVector,
<Out> ByRef moveNextArguments As ImmutableArray(Of BoundExpression),
<Out> ByRef moveNextDefaultArguments As BitVector,
<Out> ByRef currentArguments As ImmutableArray(Of BoundExpression),
<Out> ByRef currentDefaultArguments As BitVector
) As ForEachStatementInfo
getEnumeratorArguments = Nothing
moveNextArguments = Nothing
currentArguments = Nothing
Dim enumeratorInfo = boundForEach.EnumeratorInfo
Dim getEnumerator As MethodSymbol = Nothing
If enumeratorInfo.GetEnumerator IsNot Nothing AndAlso enumeratorInfo.GetEnumerator.Kind = BoundKind.Call Then
Dim getEnumeratorCall As BoundCall = DirectCast(enumeratorInfo.GetEnumerator, BoundCall)
getEnumerator = getEnumeratorCall.Method
getEnumeratorArguments = getEnumeratorCall.Arguments
getEnumeratorDefaultArguments = getEnumeratorCall.DefaultArguments
End If
Dim moveNext As MethodSymbol = Nothing
If enumeratorInfo.MoveNext IsNot Nothing AndAlso enumeratorInfo.MoveNext.Kind = BoundKind.Call Then
Dim moveNextCall As BoundCall = DirectCast(enumeratorInfo.MoveNext, BoundCall)
moveNext = moveNextCall.Method
moveNextArguments = moveNextCall.Arguments
moveNextDefaultArguments = moveNextCall.DefaultArguments
End If
Dim current As PropertySymbol = Nothing
If enumeratorInfo.Current IsNot Nothing AndAlso enumeratorInfo.Current.Kind = BoundKind.PropertyAccess Then
Dim currentProperty As BoundPropertyAccess = DirectCast(enumeratorInfo.Current, BoundPropertyAccess)
current = currentProperty.PropertySymbol
currentArguments = currentProperty.Arguments
currentDefaultArguments = currentProperty.DefaultArguments
End If
' The batch compiler doesn't actually use this conversion, so we'll just compute it here.
' It will usually be an identity conversion.
Dim currentConversion As Conversion = Nothing
Dim elementConversion As Conversion = Nothing
Dim elementType As TypeSymbol = enumeratorInfo.ElementType
If elementType IsNot Nothing AndAlso Not elementType.IsErrorType() Then
If current IsNot Nothing AndAlso Not current.Type.IsErrorType() Then
currentConversion = New Conversion(Conversions.ClassifyConversion(current.Type, elementType, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded))
End If
Dim boundCurrentConversion As BoundExpression = enumeratorInfo.CurrentConversion
If boundCurrentConversion IsNot Nothing AndAlso Not boundCurrentConversion.Type.IsErrorType() Then
' NOTE: What VB calls the current conversion is used to convert the current placeholder to the iteration
' variable type. In the terminology of the public API, this is a conversion from the element type to the
' iteration variable type, and is referred to as the element conversion.
elementConversion = New Conversion(Conversions.ClassifyConversion(elementType, boundCurrentConversion.Type, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded))
End If
End If
Dim originalCollection As BoundExpression = boundForEach.Collection
If originalCollection.Kind = BoundKind.Conversion Then
Dim conversion = DirectCast(originalCollection, BoundConversion)
If Not conversion.ExplicitCastInCode Then
originalCollection = conversion.Operand
End If
End If
Return New ForEachStatementInfo(getEnumerator,
moveNext,
current,
If(enumeratorInfo.NeedToDispose OrElse (originalCollection.Type IsNot Nothing AndAlso originalCollection.Type.IsArrayType()),
DirectCast(compilation.GetSpecialTypeMember(SpecialMember.System_IDisposable__Dispose), MethodSymbol),
Nothing),
elementType,
elementConversion,
currentConversion)
End Function
Friend Overrides Function GetAttributeSymbolInfo(attribute As AttributeSyntax, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
Return GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, GetBoundNodeSummary(attribute), binderOpt:=Nothing)
End Function
Friend Overrides Function GetAttributeTypeInfo(attribute As AttributeSyntax, Optional cancellationToken As CancellationToken = Nothing) As VisualBasicTypeInfo
Return GetTypeInfoForNode(GetBoundNodeSummary(attribute))
End Function
Friend Overrides Function GetAttributeMemberGroup(attribute As AttributeSyntax, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Symbol)
Return GetMemberGroupForNode(GetBoundNodeSummary(attribute), binderOpt:=Nothing)
End Function
Friend Overrides Function GetExpressionSymbolInfo(node As ExpressionSyntax, options As SymbolInfoOptions, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
ValidateSymbolInfoOptions(options)
If Me.IsSpeculativeSemanticModel Then
' For speculative model, we need to get the standalone expression when this is invoked via public GetSymbolInfo API
node = SyntaxFactory.GetStandaloneExpression(node)
End If
If node.EnclosingStructuredTrivia IsNot Nothing Then
Return SymbolInfo.None
End If
Return GetSymbolInfoForNode(options, GetBoundNodeSummary(node), binderOpt:=Nothing)
End Function
Friend Overrides Function GetOperationWorker(node As VisualBasicSyntaxNode, cancellationToken As CancellationToken) As IOperation
Dim result As IOperation = Nothing
Try
_rwLock.EnterReadLock()
If _guardedIOperationNodeMap.Count > 0 Then
Return If(_guardedIOperationNodeMap.TryGetValue(node, result), result, Nothing)
End If
Finally
_rwLock.ExitReadLock()
End Try
Dim rootNode As BoundNode = GetBoundRoot()
Dim rootOperation As IOperation = _operationFactory.Value.Create(rootNode)
Try
_rwLock.EnterWriteLock()
If _guardedIOperationNodeMap.Count > 0 Then
Return If(_guardedIOperationNodeMap.TryGetValue(node, result), result, Nothing)
End If
Operation.SetParentOperation(rootOperation, Nothing)
OperationMapBuilder.AddToMap(rootOperation, _guardedIOperationNodeMap)
Return If(_guardedIOperationNodeMap.TryGetValue(node, result), result, Nothing)
Finally
_rwLock.ExitWriteLock()
End Try
End Function
Friend Overrides Function GetExpressionTypeInfo(node As ExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As VisualBasicTypeInfo
If Me.IsSpeculativeSemanticModel Then
' For speculative model, we need to get the standalone expression when this is invoked via public GetTypeInfo API
node = SyntaxFactory.GetStandaloneExpression(node)
End If
If node.EnclosingStructuredTrivia IsNot Nothing Then
Return VisualBasicTypeInfo.None
End If
Return GetTypeInfoForNode(GetBoundNodeSummary(node))
End Function
Friend Overrides Function GetExpressionMemberGroup(node As ExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of Symbol)
If Me.IsSpeculativeSemanticModel Then
' For speculative model, we need to get the standalone expression when this is invoked via public GetMemberGroup API
node = SyntaxFactory.GetStandaloneExpression(node)
End If
If node.EnclosingStructuredTrivia IsNot Nothing Then
Return ImmutableArray(Of Symbol).Empty
End If
Return GetMemberGroupForNode(GetBoundNodeSummary(node), binderOpt:=Nothing)
End Function
Friend Overrides Function GetExpressionConstantValue(node As ExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As ConstantValue
If Me.IsSpeculativeSemanticModel Then
' For speculative model, we need to get the standalone expression when this is invoked via public GetConstantValue API
node = SyntaxFactory.GetStandaloneExpression(node)
End If
If node.EnclosingStructuredTrivia IsNot Nothing Then
Return Nothing
End If
Return GetConstantValueForNode(GetBoundNodeSummary(node))
End Function
Friend Overrides Function GetCollectionInitializerAddSymbolInfo(collectionInitializer As ObjectCreationExpressionSyntax, node As ExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
Dim boundCollectionInitializer = TryCast(GetLowerBoundNode(collectionInitializer.Initializer), BoundCollectionInitializerExpression)
If boundCollectionInitializer IsNot Nothing Then
Dim boundAdd = boundCollectionInitializer.Initializers(DirectCast(collectionInitializer.Initializer, ObjectCollectionInitializerSyntax).Initializer.Initializers.IndexOf(node))
If boundAdd.WasCompilerGenerated Then
Return GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, New BoundNodeSummary(boundAdd, boundAdd, Nothing), binderOpt:=Nothing)
End If
End If
Return SymbolInfo.None
End Function
Friend Overrides Function GetCrefReferenceSymbolInfo(crefReference As CrefReferenceSyntax, options As VBSemanticModel.SymbolInfoOptions, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
Return SymbolInfo.None
End Function
Friend Overrides Function GetQueryClauseSymbolInfo(node As QueryClauseSyntax, Optional cancellationToken As System.Threading.CancellationToken = Nothing) As SymbolInfo
Dim nodeKind As SyntaxKind = node.Kind
Debug.Assert(nodeKind <> SyntaxKind.LetClause AndAlso nodeKind <> SyntaxKind.OrderByClause AndAlso nodeKind <> SyntaxKind.AggregateClause)
Dim bound As BoundNode
If nodeKind = SyntaxKind.FromClause Then
If DirectCast(node, FromClauseSyntax).Variables.Count < 2 AndAlso
node.Parent IsNot Nothing AndAlso node.Parent.Kind = SyntaxKind.QueryExpression Then
Dim query = DirectCast(node.Parent, QueryExpressionSyntax)
If query.Clauses.Count = 1 AndAlso query.Clauses(0) Is node Then
' From needs an implicit Select call.
bound = GetLowerBoundNode(query)
Debug.Assert(bound Is Nothing OrElse bound.Kind = BoundKind.QueryExpression)
If bound IsNot Nothing AndAlso bound.Kind = BoundKind.QueryExpression Then
Dim boundQuery = DirectCast(bound, BoundQueryExpression)
If boundQuery.LastOperator.Syntax Is node Then
Debug.Assert(boundQuery.LastOperator.WasCompilerGenerated)
Return GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions,
New BoundNodeSummary(boundQuery.LastOperator, boundQuery.LastOperator, Nothing),
binderOpt:=Nothing)
End If
End If
End If
End If
Return SymbolInfo.None
End If
bound = GetLowerBoundNode(node)
If bound IsNot Nothing AndAlso bound.Kind = BoundKind.QueryClause Then
If nodeKind = SyntaxKind.SelectClause AndAlso
DirectCast(bound, BoundQueryClause).UnderlyingExpression.Kind = BoundKind.QueryClause Then
' Select was absorbed by the previous operator.
Return SymbolInfo.None
End If
Return GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, New BoundNodeSummary(bound, bound, Nothing), binderOpt:=Nothing)
End If
Return SymbolInfo.None
End Function
Friend Overrides Function GetLetClauseSymbolInfo(node As ExpressionRangeVariableSyntax, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
Debug.Assert(node.Parent IsNot Nothing AndAlso node.Parent.Kind = SyntaxKind.LetClause)
Dim bound As BoundNode = GetUpperBoundNode(node)
If bound IsNot Nothing AndAlso bound.Kind = BoundKind.QueryClause Then
If DirectCast(bound, BoundQueryClause).UnderlyingExpression.Kind = BoundKind.QueryClause Then
' Let was absorbed by the previous operator.
Return SymbolInfo.None
End If
Return GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, New BoundNodeSummary(bound, bound, Nothing), binderOpt:=Nothing)
End If
Return SymbolInfo.None
End Function
Friend Overrides Function GetOrderingSymbolInfo(node As OrderingSyntax, Optional cancellationToken As CancellationToken = Nothing) As SymbolInfo
Dim bound As BoundNode = GetLowerBoundNode(node)
If bound IsNot Nothing AndAlso bound.Kind = BoundKind.Ordering Then
Return GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, New BoundNodeSummary(bound, bound, Nothing), binderOpt:=Nothing)
End If
Return SymbolInfo.None
End Function
Friend Overrides Function GetAggregateClauseSymbolInfoWorker(node As AggregateClauseSyntax, Optional cancellationToken As CancellationToken = Nothing) As AggregateClauseSymbolInfo
Dim bound As BoundNode = GetLowerBoundNode(node)
If bound IsNot Nothing AndAlso bound.Kind = BoundKind.AggregateClause Then
Dim aggregateClause = DirectCast(bound, BoundAggregateClause)
If TypeOf aggregateClause.UnderlyingExpression Is BoundQueryClauseBase Then
' The Aggregate clause was completely absorbed by the previous join.
Return New AggregateClauseSymbolInfo(SymbolInfo.None, SymbolInfo.None)
End If
Dim select2 As SymbolInfo = GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, New BoundNodeSummary(bound, bound, Nothing), binderOpt:=Nothing)
' Now let's check if there is another Select call preceding this one.
Dim select1Node = DirectCast(CompilerGeneratedNodeFinder.FindIn(bound, node, BoundKind.QueryClause), BoundQueryClause)
If select1Node IsNot Nothing Then
If TypeOf select1Node.UnderlyingExpression Is BoundQueryClauseBase Then
' The first Select was absorbed by the previous join.
Return New AggregateClauseSymbolInfo(SymbolInfo.None, select2)
End If
Return New AggregateClauseSymbolInfo(GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, New BoundNodeSummary(select1Node, select1Node, Nothing), binderOpt:=Nothing),
select2)
End If
Return New AggregateClauseSymbolInfo(select2)
End If
Return New AggregateClauseSymbolInfo(SymbolInfo.None, SymbolInfo.None)
End Function
Friend Overrides Function GetCollectionRangeVariableSymbolInfoWorker(node As CollectionRangeVariableSyntax, Optional cancellationToken As CancellationToken = Nothing) As CollectionRangeVariableSymbolInfo
' Find the upper most bound node that is a QueryClause or QueryableSource
Dim boundNodes As ImmutableArray(Of BoundNode) = GetBoundNodes(node)
Dim bound As BoundNode = Nothing
For i = 0 To boundNodes.Length - 1
If boundNodes(i).Kind = BoundKind.QueryClause OrElse boundNodes(i).Kind = BoundKind.QueryableSource Then
bound = boundNodes(i)
Exit For
End If
Next
If bound Is Nothing Then
Return CollectionRangeVariableSymbolInfo.None
End If
Dim toQueryableCollectionConversion As SymbolInfo = SymbolInfo.None
Dim asClauseConversion As SymbolInfo = SymbolInfo.None
Dim selectMany As SymbolInfo = SymbolInfo.None
If bound.Kind = BoundKind.QueryClause Then
' This must be SelectMany operator.
selectMany = GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, New BoundNodeSummary(bound, bound, Nothing), binderOpt:=Nothing)
' Look for queryable source.
bound = GetLowerBoundNode(node)
End If
If bound IsNot Nothing AndAlso bound.Kind = BoundKind.QueryableSource Then
Dim queryableSource = DirectCast(bound, BoundQueryableSource)
Select Case queryableSource.Source.Kind
Case BoundKind.QueryClause
' This must be an implicit Select for an AsClause conversion.
asClauseConversion = GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, New BoundNodeSummary(queryableSource.Source, queryableSource.Source, Nothing), binderOpt:=Nothing)
' See if there is also ToQueryableSource conversion.
Dim toQueryable = DirectCast(CompilerGeneratedNodeFinder.FindIn(DirectCast(queryableSource.Source, BoundQueryClause).UnderlyingExpression,
node.Expression, BoundKind.ToQueryableCollectionConversion),
BoundToQueryableCollectionConversion)
If toQueryable Is Nothing Then
toQueryableCollectionConversion = SymbolInfo.None
Else
toQueryableCollectionConversion = GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, New BoundNodeSummary(toQueryable, toQueryable, Nothing), binderOpt:=Nothing)
End If
Case BoundKind.ToQueryableCollectionConversion
asClauseConversion = SymbolInfo.None
toQueryableCollectionConversion = GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, New BoundNodeSummary(queryableSource.Source, queryableSource.Source, Nothing), binderOpt:=Nothing)
Case BoundKind.QuerySource
asClauseConversion = SymbolInfo.None
toQueryableCollectionConversion = SymbolInfo.None
Case Else
Throw ExceptionUtilities.UnexpectedValue(queryableSource.Source.Kind)
End Select
Else
' Failed to locate corresponding QueryableSource, something went wrong.
selectMany = SymbolInfo.None
End If
Return New CollectionRangeVariableSymbolInfo(toQueryableCollectionConversion, asClauseConversion, selectMany)
End Function
Friend NotOverridable Overrides Function TryGetSpeculativeSemanticModelCore(parentModel As SyntaxTreeSemanticModel, position As Integer, type As TypeSyntax, bindingOption As SpeculativeBindingOption, <Out> ByRef speculativeModel As PublicSemanticModel) As Boolean
Dim binder As Binder = Me.GetSpeculativeBinderForExpression(position, type, bindingOption)
If binder Is Nothing Then
speculativeModel = Nothing
Return False
End If
speculativeModel = New SpeculativeSemanticModelWithMemberModel(parentModel, position, type, binder)
Return True
End Function
Friend NotOverridable Overrides Function TryGetSpeculativeSemanticModelCore(parentModel As SyntaxTreeSemanticModel, position As Integer, rangeArgument As RangeArgumentSyntax, <Out> ByRef speculativeModel As PublicSemanticModel) As Boolean
Dim binder = Me.GetEnclosingBinder(position)
If binder Is Nothing Then
speculativeModel = Nothing
Return False
End If
' Add speculative binder to bind speculatively.
binder = SpeculativeBinder.Create(binder)
speculativeModel = New SpeculativeSemanticModelWithMemberModel(parentModel, position, rangeArgument, binder)
Return True
End Function
Friend Sub CacheBoundNodes(boundNode As BoundNode, Optional thisSyntaxNodeOnly As SyntaxNode = Nothing)
_rwLock.EnterWriteLock()
Try
SemanticModelMapsBuilder.GuardedCacheBoundNodes(boundNode, Me, Me._guardedBoundNodeMap, thisSyntaxNodeOnly)
Finally
_rwLock.ExitWriteLock()
End Try
End Sub
Private Class CompilerGeneratedNodeFinder
Inherits BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
Private ReadOnly _targetSyntax As VisualBasicSyntaxNode
Private ReadOnly _targetBoundKind As BoundKind
Private _found As BoundNode
Private Sub New(targetSyntax As VisualBasicSyntaxNode, targetBoundKind As BoundKind)
_targetSyntax = targetSyntax
_targetBoundKind = targetBoundKind
End Sub
Public Shared Function FindIn(context As BoundNode, targetSyntax As VisualBasicSyntaxNode, targetBoundKind As BoundKind) As BoundNode
Debug.Assert(targetBoundKind <> BoundKind.BinaryOperator) ' Otherwise VisitBinaryOperator should be adjusted
Dim finder As New CompilerGeneratedNodeFinder(targetSyntax, targetBoundKind)
finder.Visit(context)
Return finder._found
End Function
Public Overrides Function Visit(node As BoundNode) As BoundNode
If node Is Nothing OrElse _found IsNot Nothing Then
Return Nothing
End If
If node.WasCompilerGenerated AndAlso
node.Syntax Is _targetSyntax AndAlso
node.Kind = _targetBoundKind Then
_found = node
Return Nothing
End If
Return MyBase.Visit(node)
End Function
Protected Overrides Function ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException() As Boolean
Return False
End Function
End Class
Public Overrides ReadOnly Property Compilation As VisualBasicCompilation
Get
Return RootBinder.Compilation
End Get
End Property
Friend ReadOnly Property MemberSymbol As Symbol
Get
Return RootBinder.ContainingMember
End Get
End Property
''' <summary>
''' The SyntaxTree that is bound
''' </summary>
Public Overrides ReadOnly Property SyntaxTree As SyntaxTree
Get
Return Root.SyntaxTree
End Get
End Property
Private ReadOnly _rwLock As ReaderWriterLockSlim = New ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion)
'' This class manages a cache of bound nodes and binders for all the executable code under the root SyntaxNode
'' of this SemanticModel.
''
'' The basic strategy is that a mapping from SyntaxNode -> ImmutableArray(Of BoundNode) is maintained, where
'' the bound nodes are in top-down order. If we need to find the bound nodes associated with a syntax node, we
'' first check the cache. If its not there, then we bind the enclosing statement which is NOT inside a lambda
'' (statements inside lambda may not have type information inferred for them). We then do a walk over the resulting
'' bound statement, placing all bound nodes into the mapping. We also place binders for lambda and queries into a
'' map, so that we can answer GetEnclosingBinder questions.
' The bound nodes associated with syntaxnode, from highest in the tree to lowest.
Private ReadOnly _guardedBoundNodeMap As New SmallDictionary(Of SyntaxNode, ImmutableArray(Of BoundNode))(ReferenceEqualityComparer.Instance)
Private ReadOnly _guardedIOperationNodeMap As New Dictionary(Of SyntaxNode, IOperation)
Private ReadOnly _guardedQueryBindersMap As New Dictionary(Of SyntaxNode, ImmutableArray(Of Binder))()
Private ReadOnly _guardedAnonymousTypeBinderMap As New Dictionary(Of FieldInitializerSyntax, Binder.AnonymousTypeFieldInitializerBinder)()
' If implicit variable declaration is in play, then we must bind everything
' up front in order to get all implicit local variables declared.
' Because order of declaration is important, and any expression could declare
' an implicit local, we have to bind the whole method body from start to finish.
Private Sub EnsureFullyBoundIfImplicitVariablesAllowed()
If Me.RootBinder.ImplicitVariableDeclarationAllowed AndAlso Not Me.RootBinder.AllImplicitVariableDeclarationsAreHandled Then
_rwLock.EnterWriteLock()
Try
' To prevent races, we must check again under the lock.
If Not Me.RootBinder.AllImplicitVariableDeclarationsAreHandled Then
' bind everything, so all implicit variables are declared, and processed in the right order.
Me.GuardedIncrementalBind(Me.Root, Me.RootBinder)
' after this call, RootBinder.AllImplicitVariableDeclarationsAreHandled = True
Me.RootBinder.DisallowFurtherImplicitVariableDeclaration(BindingDiagnosticBag.Discarded)
End If
Finally
_rwLock.ExitWriteLock()
End Try
End If
End Sub
Private Function GuardedGetBoundNodesFromMap(node As SyntaxNode) As ImmutableArray(Of BoundNode)
Debug.Assert(_rwLock.IsReadLockHeld OrElse _rwLock.IsWriteLockHeld)
Dim result As ImmutableArray(Of BoundNode) = Nothing
Return If(Me._guardedBoundNodeMap.TryGetValue(node, result), result, Nothing)
End Function
''' <summary>
''' Get the correct enclosing binder for the given position, taking into account
''' block constructs and lambdas.
''' </summary>
''' <param name="memberBinder">Binder for the method body, lambda body, or field initializer. The
''' returned binder will be nested inside the binder, or be this binder.</param>
''' <param name="binderRoot">Syntax node that is the root of the construct associated with "memberBinder".</param>
''' <param name="node">Syntax node that position is in.</param>
''' <param name="position">Position we are finding the enclosing binder for.</param>
''' <returns>The enclosing binder within "memberBinder" for the given position.</returns>
''' <remarks>
''' WARN WARN WARN: The result is not guaranteed to have IsSemanticModelBinder set.
''' </remarks>
Private Function GetEnclosingBinderInternal(
memberBinder As Binder,
binderRoot As SyntaxNode,
node As SyntaxNode,
position As Integer
) As Binder
Dim binder As Binder = Nothing
EnsureFullyBoundIfImplicitVariablesAllowed()
Dim current As SyntaxNode = node
Do
Dim body As SyntaxList(Of StatementSyntax) = Nothing
If current.Kind = SyntaxKind.DocumentationCommentTrivia Then
Dim trivia As SyntaxTrivia = DirectCast(current, DocumentationCommentTriviaSyntax).ParentTrivia
Debug.Assert(trivia.Kind <> SyntaxKind.None)
Debug.Assert(trivia.Token.Kind <> SyntaxKind.None)
Return GetEnclosingBinderInternal(memberBinder, binderRoot, DirectCast(trivia.Token.Parent, VisualBasicSyntaxNode), position)
ElseIf SyntaxFacts.InBlockInterior(current, position, body) Then
' We are in the interior of a block statement.
binder = memberBinder.GetBinder(body)
If binder IsNot Nothing Then
Return binder
End If
ElseIf SyntaxFacts.InLambdaInterior(current, position) Then
If current IsNot binderRoot Then
Dim lambdaBinder As LambdaBodyBinder =
Me.GetLambdaBodyBinder(DirectCast(current, LambdaExpressionSyntax))
If lambdaBinder IsNot Nothing Then
Debug.Assert(lambdaBinder.Root Is current)
If current.Kind = SyntaxKind.MultiLineFunctionLambdaExpression OrElse current.Kind = SyntaxKind.MultiLineSubLambdaExpression Then
Dim multiLineLambda = DirectCast(current, MultiLineLambdaExpressionSyntax)
If multiLineLambda.SubOrFunctionHeader.FullSpan.Contains(position) Then
Return lambdaBinder
End If
End If
binder = GetEnclosingBinderInternal(lambdaBinder, lambdaBinder.Root, node, position)
If binder IsNot Nothing Then
Return binder
End If
End If
ElseIf current.Kind = SyntaxKind.MultiLineFunctionLambdaExpression OrElse current.Kind = SyntaxKind.MultiLineSubLambdaExpression Then
' We reached the lambda node, get binder for the whole body.
binder = memberBinder.GetBinder(DirectCast(current, MultiLineLambdaExpressionSyntax).Statements)
If binder IsNot Nothing Then
Return binder
End If
ElseIf current.Kind = SyntaxKind.SingleLineSubLambdaExpression Then
' Even though single line sub lambdas only have a single statement. Get a binder for
' a statement list so that locals can be bound. Note, while locals are not allowed at the top
' level it is useful in the semantic model to bind them.
binder = memberBinder.GetBinder(DirectCast(current, SingleLineLambdaExpressionSyntax).Statements)
If binder IsNot Nothing Then
Return binder
End If
End If
ElseIf InQueryInterior(current, position, binder) Then
' We are in context of a query expression binder.
Debug.Assert(binder IsNot Nothing)
Return binder
ElseIf InAnonymousTypeInitializerInterior(current, position, binder) Then
' We are in context of an initializer expression binder.
Debug.Assert(binder IsNot Nothing)
Return binder
ElseIf InWithStatementExpressionInterior(current) Then
' Expression from With statement is supposed to be bound using
' the binder for the syntax node enclosing With statement
Debug.Assert(current.Parent.Kind = SyntaxKind.WithStatement)
Debug.Assert(current.Parent.Parent.Kind = SyntaxKind.WithBlock)
current = current.Parent.Parent
' If we are speculating on the With block, we might have reached our root,
' return memberBinder in this case.
If current Is binderRoot Then
Return memberBinder
End If
current = current.Parent
' Proceed to the end of If statement
End If
binder = memberBinder.GetBinder(current)
If binder IsNot Nothing Then
Return binder
End If
If current Is binderRoot Then
Return memberBinder
End If
current = current.Parent
Loop
End Function
''' <summary>
''' If answer is True, the binder is returned via [binder] parameter.
''' </summary>
Private Function InQueryInterior(
node As SyntaxNode,
position As Integer,
<Out()> ByRef binder As Binder
) As Boolean
binder = Nothing
Select Case node.Kind
Case SyntaxKind.WhereClause
Dim where = DirectCast(node, WhereClauseSyntax)
binder = GetSingleLambdaClauseLambdaBinder(where, where.WhereKeyword, position)
Case SyntaxKind.SkipWhileClause, SyntaxKind.TakeWhileClause
Dim partitionWhile = DirectCast(node, PartitionWhileClauseSyntax)
binder = GetSingleLambdaClauseLambdaBinder(partitionWhile, partitionWhile.WhileKeyword, position)
Case SyntaxKind.SelectClause
Dim [select] = DirectCast(node, SelectClauseSyntax)
binder = GetSingleLambdaClauseLambdaBinder([select], [select].SelectKeyword, position)
Case SyntaxKind.LetClause
binder = GetLetClauseLambdaBinder(DirectCast(node, LetClauseSyntax), position)
Case SyntaxKind.FromClause
binder = GetFromClauseLambdaBinder(DirectCast(node, FromClauseSyntax), position)
Case SyntaxKind.GroupByClause
binder = GetGroupByClauseLambdaBinder(DirectCast(node, GroupByClauseSyntax), position)
Case SyntaxKind.OrderByClause
Dim orderBy = DirectCast(node, OrderByClauseSyntax)
binder = GetSingleLambdaClauseLambdaBinder(orderBy, If(orderBy.ByKeyword.IsMissing, orderBy.OrderKeyword, orderBy.ByKeyword), position)
Case SyntaxKind.SimpleJoinClause
binder = GetJoinClauseLambdaBinder(DirectCast(node, SimpleJoinClauseSyntax), position)
Case SyntaxKind.GroupJoinClause
binder = GetGroupJoinClauseLambdaBinder(DirectCast(node, GroupJoinClauseSyntax), position)
Case SyntaxKind.AggregateClause
binder = GetAggregateClauseLambdaBinder(DirectCast(node, AggregateClauseSyntax), position)
Case SyntaxKind.FunctionAggregation
binder = GetFunctionAggregationLambdaBinder(DirectCast(node, FunctionAggregationSyntax), position)
End Select
Return binder IsNot Nothing
End Function
Private Function GetAggregateClauseLambdaBinder(aggregate As AggregateClauseSyntax, position As Integer) As Binder
Dim binder As Binder = Nothing
' If position were in context of an additional query operator that operator would have handled it, unless there were
' no need for a special binder.
' We only need to worry about Variables and the Into clause.
If SyntaxFacts.InSpanOrEffectiveTrailingOfNode(aggregate, position) Then
If Not aggregate.IntoKeyword.IsMissing AndAlso aggregate.IntoKeyword.SpanStart <= position Then
' Should return binder for the Into clause - the last one associated with the node.
Dim binders As ImmutableArray(Of Binder) = GetQueryClauseLambdaBinders(aggregate)
#If DEBUG Then
Debug.Assert(Not binders.IsDefault OrElse Not ShouldHaveFound(aggregate, guard:=True))
Debug.Assert(binders.IsDefault OrElse (binders.Length > 0 AndAlso binders.Length < 3))
#End If
If Not binders.IsEmpty Then
binder = binders.Last
Debug.Assert(binder IsNot Nothing)
End If
ElseIf aggregate.AggregateKeyword.SpanStart <= position Then
binder = GetCollectionRangeVariablesLambdaBinder(aggregate.Variables, position)
If binder Is Nothing Then
' Must be in context of the very first collection variable or an additional operator
' that inherits the binder from parent context.
' If this Aggregate clause doesn't begin the query, it has two binders:
' - parent context binder, the one we should return;
' - Into clause binder.
' If this Aggregate begins the query, it has only one binder - the Into clause binder.
Dim binders As ImmutableArray(Of Binder) = GetQueryClauseLambdaBinders(aggregate)
#If DEBUG Then
Debug.Assert(Not binders.IsDefault OrElse Not ShouldHaveFound(aggregate, guard:=True))
Debug.Assert(binders.IsDefault OrElse (binders.Length > 0 AndAlso binders.Length < 3 AndAlso binders(0) IsNot Nothing))
#End If
If Not binders.IsDefault AndAlso binders.Length = 2 Then
binder = binders(0)
End If
End If
End If
End If
Return binder
End Function
Private Function GetGroupJoinClauseLambdaBinder(join As GroupJoinClauseSyntax, position As Integer) As Binder
Dim binder As Binder = Nothing
If SyntaxFacts.InSpanOrEffectiveTrailingOfNode(join, position) Then
If Not join.IntoKeyword.IsMissing AndAlso join.IntoKeyword.SpanStart <= position Then
' Should return binder to lookup aggregate functions.
Dim binders As ImmutableArray(Of Binder) = GetQueryClauseLambdaBinders(join)
#If DEBUG Then
Debug.Assert(Not binders.IsDefault OrElse Not ShouldHaveFound(join, guard:=True))
Debug.Assert(binders.IsDefault OrElse binders.Length = 3)
#End If
If Not binders.IsDefault AndAlso binders.Length = 3 Then
binder = binders(2)
End If
Else
' Handle all parts, but [Into] clause.
binder = GetJoinClauseLambdaBinder(join, position)
End If
End If
Return binder
End Function
Private Function GetJoinClauseLambdaBinder(join As JoinClauseSyntax, position As Integer) As Binder
Dim binder As Binder = Nothing
' If position were in context of an additional join that join would have handled it, unless there were
' no need for a special binder.
' If position is in context of the collection range variable, we don't need a special binder.
' If position is in context of an 'On' clause, there is a binder that we need to return.
If Not join.OnKeyword.IsMissing AndAlso join.OnKeyword.SpanStart <= position AndAlso SyntaxFacts.InSpanOrEffectiveTrailingOfNode(join, position) Then
Dim binders As ImmutableArray(Of Binder) = GetQueryClauseLambdaBinders(join)
#If DEBUG Then
Debug.Assert(Not binders.IsDefault OrElse Not ShouldHaveFound(join, guard:=True))
Debug.Assert(binders.IsDefault OrElse (binders.Length > 1 AndAlso binders.Length < 4 AndAlso binders(0) IsNot Nothing))
#End If
If Not binders.IsEmpty Then
' The first two binders are outerkey and innerkey binders. Both have the same symbols in scope.
' It is safe to always use the outerkey binder.
binder = binders(0)
End If
End If
Return binder
End Function
Private Function GetFromClauseLambdaBinder(from As FromClauseSyntax, position As Integer) As Binder
Dim binder As Binder = Nothing
If SyntaxFacts.InSpanOrEffectiveTrailingOfNode(from, position) Then
binder = GetCollectionRangeVariablesLambdaBinder(from.Variables, position)
End If
Return binder
End Function
Private Function GetCollectionRangeVariablesLambdaBinder(variables As SeparatedSyntaxList(Of CollectionRangeVariableSyntax), position As Integer) As Binder
Dim binder As Binder = Nothing
For i As Integer = 0 To variables.Count - 1
Dim item As CollectionRangeVariableSyntax = variables(i)
If SyntaxFacts.InSpanOrEffectiveTrailingOfNode(item, position) OrElse position < item.SpanStart Then
' The first collection variable in a query or in an Aggregate clause doesn't have special binder
' stored for it in the bound tree, the binder is inherited from outer context in that case.
If i > 0 OrElse
(item.Parent.Kind <> SyntaxKind.AggregateClause AndAlso
item.Parent.Parent IsNot Nothing AndAlso
Not (item.Parent.Parent.Kind = SyntaxKind.QueryExpression AndAlso
DirectCast(item.Parent.Parent, QueryExpressionSyntax).Clauses.FirstOrDefault Is item.Parent)) Then
Dim binders As ImmutableArray(Of Binder) = GetQueryClauseLambdaBinders(item)
#If DEBUG Then
Debug.Assert(Not binders.IsDefault OrElse Not ShouldHaveFound(item, guard:=True))
Debug.Assert(binders.IsDefault OrElse (binders.Length > 0 AndAlso binders.Length < 3 AndAlso binders(0) IsNot Nothing))
#End If
If Not binders.IsEmpty Then
' Return manySelector binder.
binder = binders(0)
End If
End If
Exit For
End If
Next
Return binder
End Function
Private Function GetLetClauseLambdaBinder([let] As LetClauseSyntax, position As Integer) As Binder
Dim binder As Binder = Nothing
If SyntaxFacts.InSpanOrEffectiveTrailingOfNode([let], position) Then
For Each item As ExpressionRangeVariableSyntax In [let].Variables
If SyntaxFacts.InSpanOrEffectiveTrailingOfNode(item, position) OrElse position < item.SpanStart Then
Dim binders As ImmutableArray(Of Binder) = GetQueryClauseLambdaBinders(item)
#If DEBUG Then
Debug.Assert(Not binders.IsDefault OrElse Not ShouldHaveFound([let], guard:=True))
Debug.Assert(binders.IsDefault OrElse (binders.Length = 1 AndAlso binders(0) IsNot Nothing))
#End If
If Not binders.IsEmpty Then
binder = binders(0)
End If
Exit For
End If
Next
Debug.Assert(binder IsNot Nothing)
End If
Return binder
End Function
Private Function GetGroupByClauseLambdaBinder(groupBy As GroupByClauseSyntax, position As Integer) As Binder
Dim binder As Binder = Nothing
If SyntaxFacts.InSpanOrEffectiveTrailingOfNode(groupBy, position) Then
Dim binders As ImmutableArray(Of Binder) = GetQueryClauseLambdaBinders(groupBy)
#If DEBUG Then
Debug.Assert(Not binders.IsDefault OrElse Not ShouldHaveFound(groupBy, guard:=True))
Debug.Assert(binders.IsDefault OrElse (binders.Length = 2 OrElse binders.Length = 3))
#End If
If Not binders.IsEmpty Then
If position < groupBy.ByKeyword.SpanStart Then
If binders.Length <= 2 Then
' If we didn't create a binder for items, which is the case if there were no items,
' it is safe to grab the keys binder, because the scope is the same for both.
binder = binders(0)
Else
binder = binders(1)
End If
ElseIf position < groupBy.IntoKeyword.SpanStart Then
' Binder for keys.
binder = binders(0)
Else
' Binder for Into.
binder = binders.Last
End If
Debug.Assert(binder IsNot Nothing)
End If
End If
Return binder
End Function
Private Function GetFunctionAggregationLambdaBinder(func As FunctionAggregationSyntax, position As Integer) As Binder
Dim binder As Binder = Nothing
If Not func.OpenParenToken.IsMissing AndAlso func.OpenParenToken.SpanStart <= position AndAlso
((func.CloseParenToken.IsMissing AndAlso SyntaxFacts.InSpanOrEffectiveTrailingOfNode(func, position)) OrElse position < func.CloseParenToken.SpanStart) Then
Dim binders As ImmutableArray(Of Binder) = GetQueryClauseLambdaBinders(func)
#If DEBUG Then
Debug.Assert(Not binders.IsDefault OrElse Not ShouldHaveFound(func, guard:=True))
Debug.Assert(binders.IsDefault OrElse (binders.Length = 1 AndAlso binders(0) IsNot Nothing))
#End If
If Not binders.IsDefaultOrEmpty Then
binder = binders(0)
End If
End If
Return binder
End Function
Private Function GetSingleLambdaClauseLambdaBinder(
operatorSyntax As QueryClauseSyntax,
operatorKeyWord As SyntaxToken,
position As Integer
) As Binder
If operatorKeyWord.SpanStart <= position AndAlso SyntaxFacts.InSpanOrEffectiveTrailingOfNode(operatorSyntax, position) Then
Dim binders As ImmutableArray(Of Binder) = GetQueryClauseLambdaBinders(operatorSyntax)
#If DEBUG Then
Debug.Assert(Not binders.IsDefault OrElse Not ShouldHaveFound(operatorSyntax, guard:=True))
Debug.Assert(binders.IsDefault OrElse (binders.Length = 1 AndAlso binders(0) IsNot Nothing))
#End If
If Not binders.IsDefaultOrEmpty Then
Return binders(0)
End If
End If
Return Nothing
End Function
Private Function GetQueryClauseLambdaBinders(node As VisualBasicSyntaxNode) As ImmutableArray(Of Binder)
Debug.Assert(TypeOf node Is QueryClauseSyntax OrElse node.Kind = SyntaxKind.FunctionAggregation OrElse
(node.Kind = SyntaxKind.ExpressionRangeVariable AndAlso node.Parent.Kind = SyntaxKind.LetClause) OrElse
node.Kind = SyntaxKind.CollectionRangeVariable)
Dim binders As ImmutableArray(Of Binder) = Nothing
_rwLock.EnterReadLock()
Try
If Me._guardedQueryBindersMap.TryGetValue(node, binders) Then
Return binders
End If
Finally
_rwLock.ExitReadLock()
End Try
' Calling GetUpperBoundNode for the expression will force the
' query binders map for the whole immediate query expression
' to be generated.
Dim boundNode = GetUpperBoundNode(node)
_rwLock.EnterWriteLock()
Try
If Me._guardedQueryBindersMap.TryGetValue(node, binders) Then
Return binders
End If
' NOTE: this is a fix for the case when we cannot find a bound node
' because the syntax is under unsupported construction
If boundNode Is Nothing OrElse boundNode.Kind <> BoundKind.NoOpStatement OrElse Not boundNode.HasErrors Then
AssertIfShouldHaveFound(node)
End If
Me._guardedQueryBindersMap.Add(node, Nothing)
Return Nothing
Finally
_rwLock.ExitWriteLock()
End Try
End Function
''' <summary>
''' If answer is True, the binder is returned via [binder] parameter.
''' </summary>
Private Function InAnonymousTypeInitializerInterior(
node As SyntaxNode,
position As Integer,
<Out()> ByRef binder As Binder
) As Boolean
binder = Nothing
If (node.Kind = SyntaxKind.InferredFieldInitializer OrElse node.Kind = SyntaxKind.NamedFieldInitializer) AndAlso
node.Parent IsNot Nothing AndAlso node.Parent.Kind = SyntaxKind.ObjectMemberInitializer AndAlso
node.Parent.Parent IsNot Nothing AndAlso node.Parent.Parent.Kind = SyntaxKind.AnonymousObjectCreationExpression Then
Dim initialization = DirectCast(node, FieldInitializerSyntax)
If SyntaxFacts.InSpanOrEffectiveTrailingOfNode(initialization, position) Then
Dim cachedBinder As Binder.AnonymousTypeFieldInitializerBinder = Nothing
_rwLock.EnterReadLock()
Try
If Me._guardedAnonymousTypeBinderMap.TryGetValue(initialization, cachedBinder) Then
binder = cachedBinder
Return binder IsNot Nothing
End If
Finally
_rwLock.ExitReadLock()
End Try
' Get bound node for the whole AnonymousType initializer expression.
' This will build required maps for it.
Dim boundNode As BoundNode = GetUpperBoundNode(initialization.Parent.Parent)
_rwLock.EnterReadLock()
Try
If Me._guardedAnonymousTypeBinderMap.TryGetValue(initialization, cachedBinder) Then
binder = cachedBinder
Return binder IsNot Nothing
End If
' NOTE: this is a fix for the case when we cannot find a bound node
' because the syntax is under unsupported construction
If boundNode Is Nothing OrElse boundNode.Kind <> BoundKind.NoOpStatement OrElse Not boundNode.HasErrors Then
AssertIfShouldHaveFound(initialization)
End If
Finally
_rwLock.ExitReadLock()
End Try
End If
End If
Return False
End Function
Private Shared Function InWithStatementExpressionInterior(node As SyntaxNode) As Boolean
Dim expression = TryCast(node, ExpressionSyntax)
If expression IsNot Nothing Then
Dim parent As VisualBasicSyntaxNode = expression.Parent
If parent IsNot Nothing AndAlso parent.Kind = SyntaxKind.WithStatement Then
parent = parent.Parent
Return parent IsNot Nothing AndAlso parent.Kind = SyntaxKind.WithBlock AndAlso parent.Parent IsNot Nothing
End If
End If
Return False
End Function
Private Function GetLambdaBodyBinder(lambda As LambdaExpressionSyntax) As LambdaBodyBinder
Dim boundLambda As BoundLambda = GetBoundLambda(lambda)
If boundLambda IsNot Nothing Then
Return boundLambda.LambdaBinderOpt
End If
Return Nothing
End Function
Private Function GetBoundLambda(lambda As LambdaExpressionSyntax) As BoundLambda
Dim boundNode = GetLowerBoundNode(lambda)
Debug.Assert(boundNode Is Nothing OrElse boundNode.Kind = BoundKind.Lambda, "all lambdas should be converted to bound lambdas now")
Return DirectCast(boundNode, BoundLambda)
End Function
<Conditional("DEBUG")>
Private Sub AssertIfShouldHaveFound(node As VisualBasicSyntaxNode)
#If DEBUG Then
Debug.Assert(Not ShouldHaveFound(node))
#End If
End Sub
#If DEBUG Then
Private Function ShouldHaveFound(node As VisualBasicSyntaxNode, Optional guard As Boolean = False) As Boolean
If guard Then
_rwLock.EnterReadLock()
Try
Return ShouldHaveFound(node, guard:=False)
Finally
_rwLock.ExitReadLock()
End Try
End If
Dim child As VisualBasicSyntaxNode = node
Dim parent As VisualBasicSyntaxNode = node.Parent
' We will not be able to find an expression used in an array bounds within a parameter declaration.
' We will not be able to find an expression used as a lower array bound.
While parent IsNot Nothing
Select Case parent.Kind
Case SyntaxKind.RangeArgument
If DirectCast(parent, RangeArgumentSyntax).LowerBound Is child Then
Exit While
End If
Case SyntaxKind.ModifiedIdentifier
If parent.Parent IsNot Nothing AndAlso parent.Parent.Kind = SyntaxKind.Parameter Then
Exit While
End If
End Select
child = parent
parent = parent.Parent
End While
Return (parent Is Nothing)
End Function
#End If
''' <summary>
''' Get all bound nodes associated with a node, ordered from highest to lowest in the bound tree.
''' Strictly speaking, the order is that of a pre-order traversal of the bound tree.
''' As a side effect, caches nodes and binders.
''' </summary>
Friend Function GetBoundNodes(node As SyntaxNode) As ImmutableArray(Of BoundNode)
Dim bound As ImmutableArray(Of BoundNode) = Nothing
EnsureFullyBoundIfImplicitVariablesAllowed()
' First, look in the cached bounds nodes.
_rwLock.EnterReadLock()
Try
bound = GuardedGetBoundNodesFromMap(node)
Finally
_rwLock.ExitReadLock()
End Try
If Not bound.IsDefault Then
Return bound
End If
If IsNonExpressionCollectionInitializer(node) Then
Return ImmutableArray(Of BoundNode).Empty
End If
' If we didn't find in the cached bound nodes, find a binding root and bind it.
' This will cache bound nodes under the binding root.
Dim bindingRoot = Me.GetBindingRoot(node)
Dim bindingRootBinder = GetEnclosingBinder(bindingRoot)
_rwLock.EnterWriteLock()
Try
bound = GuardedGetBoundNodesFromMap(node)
If bound.IsDefault Then
GuardedIncrementalBind(bindingRoot, bindingRootBinder)
End If
bound = GuardedGetBoundNodesFromMap(node)
If Not bound.IsDefault Then
Return bound
End If
Finally
_rwLock.ExitWriteLock()
End Try
' If we still didn't find it, its still possible we could bind it directly.
' For example, types are usually not represented by bound nodes, and some error conditions and
' not yet implemented features do not create bound nodes for everything underneath them.
'
' In this case, however, we only add the single bound node we found to the map, not any child bound nodes,
' to avoid duplicates in the map if a parent of this node comes through this code path also.
If TypeOf node Is ExpressionSyntax OrElse TypeOf node Is StatementSyntax Then
Dim binder = New IncrementalBinder(Me, GetEnclosingBinder(node))
_rwLock.EnterWriteLock()
Try
bound = GuardedGetBoundNodesFromMap(node)
If bound.IsDefault Then
' Bind the node and cache any associated bound nodes we find.
Dim boundNode = Me.Bind(binder, node, BindingDiagnosticBag.Discarded)
SemanticModelMapsBuilder.GuardedCacheBoundNodes(boundNode, Me, _guardedBoundNodeMap, node)
End If
bound = GuardedGetBoundNodesFromMap(node)
If Not bound.IsDefault Then
Return bound
End If
Finally
_rwLock.ExitWriteLock()
End Try
End If
' Nothing to return.
Return ImmutableArray(Of BoundNode).Empty
End Function
''' <summary>
''' A collection initializer syntax node is not always treated as a VB expression syntax node
''' in case it's part of a CollectionInitializer (outer most or top level initializer).
''' </summary>
''' <param name="syntax">The syntax node to check.</param>
''' <returns><c>True</c> if the syntax node represents an expression syntax, but it's not
''' an expression from the VB language point of view; otherwise <c>False</c>.</returns>
Private Shared Function IsNonExpressionCollectionInitializer(syntax As SyntaxNode) As Boolean
Dim parent As SyntaxNode = syntax.Parent
If syntax.Kind = SyntaxKind.CollectionInitializer AndAlso parent IsNot Nothing Then
If parent.Kind = SyntaxKind.ObjectCollectionInitializer Then
Return True
ElseIf parent.Kind = SyntaxKind.CollectionInitializer Then
parent = parent.Parent
Return parent IsNot Nothing AndAlso parent.Kind = SyntaxKind.ObjectCollectionInitializer
End If
End If
Return False
End Function
''' <summary>
''' Incrementally bind bindingRoot (which is always a non-lambda enclosed statement, or the
''' root of this model). Side effect is to store nodes into the guarded node map.
''' </summary>
Private Sub GuardedIncrementalBind(bindingRoot As SyntaxNode, enclosingBinder As Binder)
Debug.Assert(_rwLock.IsWriteLockHeld)
If _guardedBoundNodeMap.ContainsKey(bindingRoot) Then
' We've already bound this. No need to bind it again (saves a bit of
' work below).
Return
End If
Debug.Assert(enclosingBinder.IsSemanticModelBinder)
Dim binder = New IncrementalBinder(Me, enclosingBinder)
Dim boundRoot As BoundNode = Me.Bind(binder, bindingRoot, BindingDiagnosticBag.Discarded)
' if the node could not be bound, there's nothing more to do.
If boundRoot Is Nothing Then
Return
End If
SemanticModelMapsBuilder.GuardedCacheBoundNodes(boundRoot, Me, _guardedBoundNodeMap)
If Not _guardedBoundNodeMap.ContainsKey(bindingRoot) Then
' Generally 'bindingRoot' is supposed to be found in node map at this point,
' but it will not happen in some scenarios such as for field or property
' initializers, let's add it to prevent re-binding
Debug.Assert(bindingRoot.Kind = SyntaxKind.FieldDeclaration OrElse
bindingRoot.Kind = SyntaxKind.PropertyStatement OrElse
bindingRoot.Kind = SyntaxKind.Parameter OrElse
bindingRoot.Kind = SyntaxKind.EnumMemberDeclaration OrElse
bindingRoot Is Me.Root AndAlso Me.IsSpeculativeSemanticModel)
_guardedBoundNodeMap.Add(bindingRoot, ImmutableArray.Create(Of BoundNode)(boundRoot))
End If
End Sub
''' <summary>
''' In order that any expression level special binders are used, lambdas are fully resolved,
''' and that any other binding context is correctly handled, we only use the binder to create bound
''' nodes for:
''' a) The root syntax of this semantic model (because there's nothing more outer to bind)
''' b) A stand-alone statement is that is not inside a lambda.
''' </summary>
Private Function GetBindingRoot(node As SyntaxNode) As SyntaxNode
Dim enclosingStatement As StatementSyntax = Nothing
' Walk all the way up to the root syntax, so that we see any enclosing lambdas.
While node IsNot Me.Root
If enclosingStatement Is Nothing Then
Dim statementNode = TryCast(node, StatementSyntax)
If statementNode IsNot Nothing AndAlso IsStandaloneStatement(statementNode) Then
enclosingStatement = statementNode
End If
End If
If node.Kind = SyntaxKind.DocumentationCommentTrivia Then
Dim trivia As SyntaxTrivia = DirectCast(node, DocumentationCommentTriviaSyntax).ParentTrivia
Debug.Assert(trivia.Kind <> SyntaxKind.None)
Debug.Assert(trivia.Token.Kind <> SyntaxKind.None)
node = DirectCast(trivia.Token.Parent, VisualBasicSyntaxNode)
Continue While
ElseIf node.IsLambdaExpressionSyntax Then
' We can't use a statement that is inside a lambda.
enclosingStatement = Nothing
End If
node = node.Parent
End While
If enclosingStatement IsNot Nothing Then
Return enclosingStatement
Else
Return Me.Root
End If
End Function
''' <summary>
''' The incremental binder is used when binding statements. Whenever a statement
''' is bound, it checks the bound node cache to see if that statement was bound,
''' and returns it instead of rebinding it.
'''
''' FOr example, we might have:
''' While x > goo()
''' y = y * x
''' z = z + y
''' End While
'''
''' We might first get semantic info about "z", and thus bind just the statement
''' "z = z + y". Later, we might bind the entire While block. While binding the while
''' block, we can reuse the binding we did of "z = z + y".
''' </summary>
Friend Class IncrementalBinder
Inherits Binder
Private ReadOnly _binding As MemberSemanticModel
Friend Sub New(binding As MemberSemanticModel, [next] As Binder)
MyBase.New([next])
_binding = binding
End Sub
''' <summary>
''' We override GetBinder so that the BindStatement override is still
''' in effect on nested binders.
''' </summary>
Public Overrides Function GetBinder(node As SyntaxNode) As Binder
Dim binder As Binder = Me.ContainingBinder.GetBinder(node)
If binder IsNot Nothing Then
Debug.Assert(Not (TypeOf binder Is IncrementalBinder))
Return New IncrementalBinder(_binding, binder)
End If
Return Nothing
End Function
''' <summary>
''' We override GetBinder so that the BindStatement override is still
''' in effect on nested binders.
''' </summary>
Public Overrides Function GetBinder(list As SyntaxList(Of StatementSyntax)) As Binder
Dim binder As Binder = Me.ContainingBinder.GetBinder(list)
If binder IsNot Nothing Then
Debug.Assert(Not (TypeOf binder Is IncrementalBinder))
Return New IncrementalBinder(_binding, binder)
End If
Return Nothing
End Function
Public Overrides Function BindStatement(node As StatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement
' Check the bound node cache to see if the statement was already bound.
Dim boundNodes As ImmutableArray(Of BoundNode) = _binding.GuardedGetBoundNodesFromMap(node)
If boundNodes.IsDefault Then
' Not bound already. Bind it. It will get added to the cache later by the SemanticModelMapsBuilder.
Dim boundStmt = MyBase.BindStatement(node, diagnostics)
Debug.Assert((TypeOf boundStmt Is BoundStatement))
Return boundStmt
Else
' Already bound. Return the top-most bound node associated with the statement.
Return DirectCast(boundNodes.First, BoundStatement)
End If
End Function
End Class
Friend Overrides Function GetAwaitExpressionInfoWorker(awaitExpression As AwaitExpressionSyntax, Optional cancellationToken As CancellationToken = Nothing) As AwaitExpressionInfo
Dim bound As BoundNode = GetLowerBoundNode(awaitExpression)
If bound IsNot Nothing AndAlso bound.Kind = BoundKind.AwaitOperator Then
Dim boundAwait = DirectCast(bound, BoundAwaitOperator)
Return New AwaitExpressionInfo(TryCast(boundAwait.GetAwaiter.ExpressionSymbol, MethodSymbol),
TryCast(boundAwait.IsCompleted.ExpressionSymbol, PropertySymbol),
TryCast(boundAwait.GetResult.ExpressionSymbol, MethodSymbol))
End If
Return Nothing
End Function
''' <summary>
''' Traverse a tree of bound nodes, and update the following maps inside the SemanticModel:
'''
''' guardedNodeMap - a map from syntax node to bound nodes. Bound nodes are added in the order they are bound
''' traversing the tree, so they will be in order from upper to lower node.
'''
''' guardedQueryBindersMap - a map from query-specific syntax node to an array of binders used to
''' bind various children of the node.
'''
''' guardedAnonymousTypeBinderMap - a map from Anonymous Type initializer's FieldInitializerSyntax to
''' Binder.AnonymousTypeFieldInitializerBinder used to bind its expression.
'''</summary>
Private Class SemanticModelMapsBuilder
Inherits BoundTreeWalkerWithStackGuard
Private ReadOnly _semanticModel As MemberSemanticModel
Private ReadOnly _thisSyntaxNodeOnly As SyntaxNode ' If not Nothing, record nodes for this syntax node only.
Private _placeholderReplacementMap As Dictionary(Of BoundValuePlaceholderBase, BoundExpression)
Private ReadOnly _nodeCache As OrderPreservingMultiDictionary(Of SyntaxNode, BoundNode)
Private Sub New(semanticModel As MemberSemanticModel, thisSyntaxNodeOnly As SyntaxNode, nodeCache As OrderPreservingMultiDictionary(Of SyntaxNode, BoundNode))
_semanticModel = semanticModel
_thisSyntaxNodeOnly = thisSyntaxNodeOnly
_nodeCache = nodeCache
End Sub
Public Shared Sub GuardedCacheBoundNodes(
root As BoundNode,
semanticModel As MemberSemanticModel,
nodeCache As SmallDictionary(Of SyntaxNode, ImmutableArray(Of BoundNode)),
Optional thisSyntaxNodeOnly As SyntaxNode = Nothing
)
Debug.Assert(semanticModel._rwLock.IsWriteLockHeld)
Dim additionalNodes = OrderPreservingMultiDictionary(Of SyntaxNode, BoundNode).GetInstance()
Dim walker = New SemanticModelMapsBuilder(semanticModel, thisSyntaxNodeOnly, additionalNodes)
walker.Visit(root)
For Each key In additionalNodes.Keys
If Not nodeCache.ContainsKey(key) Then
nodeCache(key) = additionalNodes(key)
Else
#If DEBUG Then
' It's possible that GuardedIncrementalBind was previously called with a subtree of bindingRoot. If
' this is the case, then we'll see an entry in the map. Since the incremental binder should also have seen the
' pre-existing map entry, the entry in addition map should be identical.
' Another, more unfortunate, possibility is that we've had to re-bind the syntax and the new bound
' nodes are equivalent, but not identical, to the existing ones. In such cases, we prefer the
' existing nodes so that the cache will always return the same bound node for a given syntax node.
' EXAMPLE: Suppose we have the statement P.M(1)
' First, we ask for semantic info about "P". We'll walk up to the statement level and bind that.
' We'll end up with map entries for "1", "P" and "P.M(1)".
' Next, we ask for semantic info about "P.M". That isn't in our map, so we walk up to the statement
' level - again - and bind that - again.
' Once again, we'll end up with map entries for "1", "P" and "P.M(1)". They will
' have the same structure as the original map entries, but will not be ReferenceEquals.
Dim existing = nodeCache(key)
Dim added = additionalNodes(key)
Debug.Assert(existing.Length = added.Length)
For i = 0 To existing.Length - 1
Debug.Assert(existing(i).Kind = added(i).Kind, "New bound node does not match existing bound node")
Next
#End If
End If
Next
additionalNodes.Free()
End Sub
''' <summary>
''' Should we record bound node mapping for this node? Generally, we ignore compiler generated, but optionally can
''' allow.
''' </summary>
Public Function RecordNode(node As BoundNode, Optional allowCompilerGenerated As Boolean = False) As Boolean
If Not allowCompilerGenerated AndAlso node.WasCompilerGenerated Then
' Don't cache compiler generated nodes
Return False
End If
Select Case node.Kind
Case BoundKind.UnboundLambda
' Don't cache unbound lambdas (unbound lambda are converted into bound lambdas in VisitUnboundLambda.)
Return False
Case BoundKind.Conversion
If Not allowCompilerGenerated Then
Dim conversion = DirectCast(node, BoundConversion)
If Not conversion.ExplicitCastInCode AndAlso conversion.Operand.WasCompilerGenerated Then
Select Case conversion.Operand.Kind
Case BoundKind.RValuePlaceholder,
BoundKind.LValuePlaceholder,
BoundKind.WithLValueExpressionPlaceholder,
BoundKind.WithRValueExpressionPlaceholder
' Don't cache compiler generated nodes
Return False
End Select
End If
End If
End Select
If _thisSyntaxNodeOnly IsNot Nothing AndAlso node.Syntax IsNot _thisSyntaxNodeOnly Then
' Didn't match the syntax node we're trying to handle
Return False
End If
Return True
End Function
Public Overrides Function Visit(node As BoundNode) As BoundNode
If node Is Nothing Then
Return Nothing
End If
If RecordNode(node) Then
_nodeCache.Add(node.Syntax, node)
End If
Return MyBase.Visit(node)
End Function
Protected Overrides Function ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException() As Boolean
Return False
End Function
Public Overrides Function VisitBinaryOperator(node As BoundBinaryOperator) As BoundNode
If node.Left.Kind <> BoundKind.BinaryOperator Then
Return MyBase.VisitBinaryOperator(node)
End If
Dim rightOperands = ArrayBuilder(Of BoundExpression).GetInstance()
rightOperands.Push(node.Right)
Dim binary = DirectCast(node.Left, BoundBinaryOperator)
If RecordNode(binary) Then
_nodeCache.Add(binary.Syntax, binary)
End If
rightOperands.Push(binary.Right)
Dim current As BoundExpression = binary.Left
While current.Kind = BoundKind.BinaryOperator
binary = DirectCast(current, BoundBinaryOperator)
If RecordNode(binary) Then
_nodeCache.Add(binary.Syntax, binary)
End If
rightOperands.Push(binary.Right)
current = binary.Left
End While
Me.Visit(current)
While rightOperands.Count > 0
Me.Visit(rightOperands.Pop())
End While
rightOperands.Free()
Return Nothing
End Function
Public Overrides Function VisitUnboundLambda(node As UnboundLambda) As BoundNode
Return Visit(node.BindForErrorRecovery())
End Function
Public Overrides Function VisitCall(node As BoundCall) As BoundNode
Dim receiver As BoundExpression = node.ReceiverOpt
Debug.Assert(receiver Is Nothing OrElse Not node.Method.IsShared OrElse receiver.HasErrors)
Me.Visit(receiver)
Dim boundGroup As BoundMethodGroup = node.MethodGroupOpt
If boundGroup IsNot Nothing Then
If boundGroup.Syntax IsNot node.Syntax Then
Debug.Assert(boundGroup.ReceiverOpt Is Nothing OrElse receiver Is Nothing)
Me.Visit(boundGroup)
ElseIf node.Method.IsShared Then
' NOTE: in this case the receiver is nothing, but we still
' want to visit it if we find it in the method group
Me.Visit(boundGroup.ReceiverOpt)
End If
End If
Me.VisitList(node.Arguments)
Return Nothing
End Function
Public Overrides Function VisitPropertyAccess(node As BoundPropertyAccess) As BoundNode
Dim receiver As BoundExpression = node.ReceiverOpt
Debug.Assert(receiver Is Nothing OrElse Not node.PropertySymbol.IsShared OrElse receiver.HasErrors)
Me.Visit(receiver)
Dim boundGroup As BoundPropertyGroup = node.PropertyGroupOpt
If boundGroup IsNot Nothing Then
If boundGroup.Syntax IsNot node.Syntax Then
Debug.Assert(boundGroup.ReceiverOpt Is Nothing OrElse receiver Is Nothing)
Me.Visit(node.PropertyGroupOpt)
ElseIf node.PropertySymbol.IsShared Then
' NOTE: in this case the receiver is nothing but we still
' want to visit it if we find it in the property group
Me.Visit(boundGroup.ReceiverOpt)
End If
End If
Me.VisitList(node.Arguments)
Return Nothing
End Function
Public Overrides Function VisitTypeExpression(node As BoundTypeExpression) As BoundNode
Me.Visit(node.UnevaluatedReceiverOpt)
Return MyBase.VisitTypeExpression(node)
End Function
Public Overrides Function VisitAttribute(node As BoundAttribute) As BoundNode
Me.VisitList(node.ConstructorArguments)
For Each namedArg In node.NamedArguments
Me.Visit(namedArg)
Next
Return Nothing
End Function
Public Overrides Function VisitQueryClause(node As BoundQueryClause) As BoundNode
If RecordNode(node) Then
#If DEBUG Then
Dim haveBindersInTheMap As ImmutableArray(Of Binder) = Nothing
Debug.Assert(Not _semanticModel._guardedQueryBindersMap.TryGetValue(node.Syntax, haveBindersInTheMap) OrElse haveBindersInTheMap.Equals(node.Binders))
#End If
_semanticModel._guardedQueryBindersMap(node.Syntax) = node.Binders
End If
Return MyBase.VisitQueryClause(node)
End Function
Public Overrides Function VisitAggregateClause(node As BoundAggregateClause) As BoundNode
If RecordNode(node) Then
#If DEBUG Then
Dim haveBindersInTheMap As ImmutableArray(Of Binder) = Nothing
Debug.Assert(Not _semanticModel._guardedQueryBindersMap.TryGetValue(node.Syntax, haveBindersInTheMap) OrElse haveBindersInTheMap.Equals(node.Binders))
#End If
_semanticModel._guardedQueryBindersMap(node.Syntax) = node.Binders
End If
Return MyBase.VisitAggregateClause(node)
End Function
Public Overrides Function VisitAnonymousTypeFieldInitializer(node As BoundAnonymousTypeFieldInitializer) As BoundNode
If RecordNode(node, allowCompilerGenerated:=True) Then
Dim initialization = TryCast(node.Syntax, FieldInitializerSyntax)
If initialization IsNot Nothing Then
#If DEBUG Then
Dim haveBindersInTheMap As Binder.AnonymousTypeFieldInitializerBinder = Nothing
' The assert below is disabled due to https://github.com/dotnet/roslyn/issues/27533, need to follow up
'Debug.Assert(Not _semanticModel._guardedAnonymousTypeBinderMap.TryGetValue(initialization, haveBindersInTheMap) OrElse haveBindersInTheMap Is node.Binder)
#End If
_semanticModel._guardedAnonymousTypeBinderMap(initialization) = node.Binder
End If
End If
Return MyBase.VisitAnonymousTypeFieldInitializer(node)
End Function
Public Overrides Function VisitConversion(node As BoundConversion) As BoundNode
' Shouldn't visit RelaxationLambda here.
Return Visit(node.Operand)
End Function
Public Overrides Function VisitDirectCast(node As BoundDirectCast) As BoundNode
' Shouldn't visit RelaxationLambda here.
Return Visit(node.Operand)
End Function
Public Overrides Function VisitTryCast(node As BoundTryCast) As BoundNode
' Shouldn't visit RelaxationLambda here.
Return Visit(node.Operand)
End Function
Public Overrides Function VisitDelegateCreationExpression(node As BoundDelegateCreationExpression) As BoundNode
' Shouldn't visit RelaxationLambda here.
Dim receiver As BoundExpression = node.ReceiverOpt
Me.Visit(receiver)
Dim boundGroup As BoundMethodGroup = node.MethodGroupOpt
If boundGroup IsNot Nothing Then
If boundGroup.Syntax IsNot node.Syntax Then
Debug.Assert(boundGroup.ReceiverOpt Is Nothing OrElse receiver Is Nothing)
Me.Visit(boundGroup)
ElseIf node.Method.IsShared Then
' NOTE: in this case the receiver is nothing, but we still
' want to visit it if we find it in the method group
Me.Visit(boundGroup.ReceiverOpt)
End If
End If
Return Nothing
End Function
Public Overrides Function VisitAssignmentOperator(node As BoundAssignmentOperator) As BoundNode
If node.LeftOnTheRightOpt Is Nothing Then
Return MyBase.VisitAssignmentOperator(node)
End If
' This is a compound assignment.
' Don't cache the left node now, in order to provide accurate type information,
' it should be cached when we visit its placeholder instead.
' Visiting the right side should take care of this.
If _placeholderReplacementMap Is Nothing Then
_placeholderReplacementMap = New Dictionary(Of BoundValuePlaceholderBase, BoundExpression)()
End If
_placeholderReplacementMap.Add(node.LeftOnTheRightOpt, node.Left)
Visit(node.Right)
_placeholderReplacementMap.Remove(node.LeftOnTheRightOpt)
Return Nothing
End Function
Public Overrides Function VisitCompoundAssignmentTargetPlaceholder(node As BoundCompoundAssignmentTargetPlaceholder) As BoundNode
Dim replacement As BoundExpression = Nothing
If _placeholderReplacementMap IsNot Nothing AndAlso _placeholderReplacementMap.TryGetValue(node, replacement) Then
Return Visit(replacement)
End If
Return MyBase.VisitCompoundAssignmentTargetPlaceholder(node)
End Function
Public Overrides Function VisitByRefArgumentPlaceholder(node As BoundByRefArgumentPlaceholder) As BoundNode
Dim replacement As BoundExpression = Nothing
If _placeholderReplacementMap IsNot Nothing AndAlso _placeholderReplacementMap.TryGetValue(node, replacement) Then
Return Visit(replacement)
End If
Return MyBase.VisitByRefArgumentPlaceholder(node)
End Function
Public Overrides Function VisitByRefArgumentWithCopyBack(node As BoundByRefArgumentWithCopyBack) As BoundNode
' Don't cache the OriginalArgument node now, in order to provide accurate type information,
' it should be cached when we visit its InPlaceholder instead.
' Visiting the InConversion should take care of this.
If _placeholderReplacementMap Is Nothing Then
_placeholderReplacementMap = New Dictionary(Of BoundValuePlaceholderBase, BoundExpression)()
End If
_placeholderReplacementMap.Add(node.InPlaceholder, node.OriginalArgument)
Visit(node.InConversion)
_placeholderReplacementMap.Remove(node.InPlaceholder)
Return Nothing
End Function
Private Function VisitObjectInitializerExpressionBase(node As BoundObjectInitializerExpressionBase) As BoundNode
Me.VisitList(node.Initializers)
Return Nothing
End Function
Public Overrides Function VisitCollectionInitializerExpression(node As BoundCollectionInitializerExpression) As BoundNode
Return Me.VisitObjectInitializerExpressionBase(node)
End Function
Public Overrides Function VisitObjectInitializerExpression(node As BoundObjectInitializerExpression) As BoundNode
Return Me.VisitObjectInitializerExpressionBase(node)
End Function
Public Overrides Function VisitLateInvocation(node As BoundLateInvocation) As BoundNode
MyBase.VisitLateInvocation(node)
Dim member = TryCast(node.Member, BoundLateMemberAccess)
If member IsNot Nothing AndAlso member.ReceiverOpt Is Nothing AndAlso node.MethodOrPropertyGroupOpt IsNot Nothing Then
' The semantic model needs to see the method or property group's receiver if its member's receiver is Nothing.
Visit(node.MethodOrPropertyGroupOpt.ReceiverOpt)
End If
Return Nothing
End Function
End Class
End Class
End Namespace
|