/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Binding/MemberSemanticModel.vb
2 480 строк
123 KB
Tomáš Matoušek
ReferenceEqualityComparer (#78350)
28 апр 2025, 21:25
Не верифицирован
28 апр 2025, 21:25
0ec9acf
Код
Авторство
О чём код?
' 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 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