File: Operations\VisualBasicOperationFactory_Methods.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.AnonymousTypeManager
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.Operations
    Partial Friend NotInheritable Class VisualBasicOperationFactory
        Private Shared Function IsMidStatement(node As BoundNode) As Boolean
            If node.Kind = BoundKind.Conversion Then
                node = DirectCast(node, BoundConversion).Operand
 
                If node.Kind = BoundKind.UserDefinedConversion Then
                    node = DirectCast(node, BoundUserDefinedConversion).Operand
                End If
            End If
 
            Return node.Kind = BoundKind.MidResult
        End Function
 
        Friend Function CreateCompoundAssignmentRightOperand(boundAssignment As BoundAssignmentOperator) As IOperation
            Dim binaryOperator As BoundExpression = Nothing
            Select Case boundAssignment.Right.Kind
                Case BoundKind.Conversion
                    Dim inConversionNode = DirectCast(boundAssignment.Right, BoundConversion)
                    binaryOperator = GetConversionOperand(inConversionNode)
                Case BoundKind.UserDefinedBinaryOperator, BoundKind.BinaryOperator
                    binaryOperator = boundAssignment.Right
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(boundAssignment.Kind)
            End Select
 
            Dim operatorInfo As BinaryOperatorInfo
            Select Case binaryOperator.Kind
                Case BoundKind.BinaryOperator
                    operatorInfo = GetBinaryOperatorInfo(DirectCast(binaryOperator, BoundBinaryOperator))
                    Return Create(operatorInfo.RightOperand)
                Case BoundKind.UserDefinedBinaryOperator
                    Dim userDefinedOperator = DirectCast(binaryOperator, BoundUserDefinedBinaryOperator)
                    operatorInfo = GetUserDefinedBinaryOperatorInfo(userDefinedOperator)
                    Return GetUserDefinedBinaryOperatorChild(userDefinedOperator, operatorInfo.RightOperand)
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(boundAssignment.Kind)
            End Select
        End Function
 
        Private Function CreateCompoundAssignment(boundAssignment As BoundAssignmentOperator) As ICompoundAssignmentOperation
            Debug.Assert(boundAssignment.LeftOnTheRightOpt IsNot Nothing)
            Dim inConversion = New Conversion(Conversions.Identity)
            Dim outConversion As Conversion = inConversion
            Dim binaryOperator As BoundExpression = Nothing
            Select Case boundAssignment.Right.Kind
                Case BoundKind.Conversion
                    Dim inConversionNode = DirectCast(boundAssignment.Right, BoundConversion)
                    outConversion = CreateConversion(inConversionNode)
                    binaryOperator = GetConversionOperand(inConversionNode)
                Case BoundKind.UserDefinedBinaryOperator, BoundKind.BinaryOperator
                    binaryOperator = boundAssignment.Right
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(boundAssignment.Kind)
            End Select
 
            Dim operatorInfo As BinaryOperatorInfo
            Select Case binaryOperator.Kind
                Case BoundKind.BinaryOperator
                    operatorInfo = GetBinaryOperatorInfo(DirectCast(binaryOperator, BoundBinaryOperator))
                Case BoundKind.UserDefinedBinaryOperator
                    Dim userDefinedOperator = DirectCast(binaryOperator, BoundUserDefinedBinaryOperator)
                    operatorInfo = GetUserDefinedBinaryOperatorInfo(userDefinedOperator)
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(boundAssignment.Kind)
            End Select
 
            Dim leftOnTheRight As BoundExpression = operatorInfo.LeftOperand
            If leftOnTheRight.Kind = BoundKind.Conversion Then
                Dim outConversionNode = DirectCast(leftOnTheRight, BoundConversion)
                inConversion = CreateConversion(outConversionNode)
                leftOnTheRight = GetConversionOperand(outConversionNode)
            End If
 
            Debug.Assert(leftOnTheRight Is boundAssignment.LeftOnTheRightOpt)
 
            Dim target As IOperation = Create(boundAssignment.Left)
            Dim value As IOperation = CreateCompoundAssignmentRightOperand(boundAssignment)
            Dim syntax As SyntaxNode = boundAssignment.Syntax
            Dim type As ITypeSymbol = boundAssignment.Type
            Dim isImplicit As Boolean = boundAssignment.WasCompilerGenerated
 
            Return New CompoundAssignmentOperation(inConversion, outConversion, operatorInfo.OperatorKind, operatorInfo.IsLifted,
                                                   operatorInfo.IsChecked, operatorInfo.OperatorMethod, constrainedToType:=Nothing, target, value,
                                                   _semanticModel, syntax, type, isImplicit)
        End Function
 
        Private Structure BinaryOperatorInfo
            Public Sub New(leftOperand As BoundExpression,
                           rightOperand As BoundExpression,
                           binaryOperatorKind As BinaryOperatorKind,
                           operatorMethod As MethodSymbol,
                           isLifted As Boolean,
                           isChecked As Boolean,
                           isCompareText As Boolean)
                Me.LeftOperand = leftOperand
                Me.RightOperand = rightOperand
                Me.OperatorKind = binaryOperatorKind
                Me.OperatorMethod = operatorMethod
                Me.IsLifted = isLifted
                Me.IsChecked = isChecked
                Me.IsCompareText = isCompareText
            End Sub
 
            Public ReadOnly LeftOperand As BoundExpression
            Public ReadOnly RightOperand As BoundExpression
            Public ReadOnly OperatorKind As BinaryOperatorKind
            Public ReadOnly OperatorMethod As MethodSymbol
            Public ReadOnly IsLifted As Boolean
            Public ReadOnly IsChecked As Boolean
            Public ReadOnly IsCompareText As Boolean
        End Structure
 
        Private Shared Function GetBinaryOperatorInfo(boundBinaryOperator As BoundBinaryOperator) As BinaryOperatorInfo
            Return New BinaryOperatorInfo(
                leftOperand:=boundBinaryOperator.Left,
                rightOperand:=boundBinaryOperator.Right,
                binaryOperatorKind:=Helper.DeriveBinaryOperatorKind(boundBinaryOperator.OperatorKind, boundBinaryOperator.Left),
                operatorMethod:=Nothing,
                isLifted:=(boundBinaryOperator.OperatorKind And VisualBasic.BinaryOperatorKind.Lifted) <> 0,
                isChecked:=boundBinaryOperator.Checked,
                isCompareText:=(boundBinaryOperator.OperatorKind And VisualBasic.BinaryOperatorKind.CompareText) <> 0)
        End Function
 
        Private Shared Function GetUserDefinedBinaryOperatorInfo(boundUserDefinedBinaryOperator As BoundUserDefinedBinaryOperator) As BinaryOperatorInfo
            Return New BinaryOperatorInfo(
                leftOperand:=GetUserDefinedBinaryOperatorChildBoundNode(boundUserDefinedBinaryOperator, 0),
                rightOperand:=GetUserDefinedBinaryOperatorChildBoundNode(boundUserDefinedBinaryOperator, 1),
                binaryOperatorKind:=Helper.DeriveBinaryOperatorKind(boundUserDefinedBinaryOperator.OperatorKind, leftOpt:=Nothing),
                operatorMethod:=If(boundUserDefinedBinaryOperator.UnderlyingExpression.Kind = BoundKind.Call, boundUserDefinedBinaryOperator.Call.Method, Nothing),
                isLifted:=(boundUserDefinedBinaryOperator.OperatorKind And VisualBasic.BinaryOperatorKind.Lifted) <> 0,
                isChecked:=boundUserDefinedBinaryOperator.Checked,
                isCompareText:=False)
        End Function
 
        Private Function GetUserDefinedBinaryOperatorChild([operator] As BoundUserDefinedBinaryOperator, child As BoundExpression) As IOperation
            If child IsNot Nothing Then
                Return Create(child)
            End If
            Dim isImplicit As Boolean = [operator].UnderlyingExpression.WasCompilerGenerated
            Return OperationFactory.CreateInvalidOperation(_semanticModel, [operator].UnderlyingExpression.Syntax, ImmutableArray(Of IOperation).Empty, isImplicit)
        End Function
 
        Private Shared Function GetUserDefinedBinaryOperatorChildBoundNode([operator] As BoundUserDefinedBinaryOperator, index As Integer) As BoundExpression
            If [operator].UnderlyingExpression.Kind = BoundKind.Call Then
                If index = 0 Then
                    Return [operator].Left
                ElseIf index = 1 Then
                    Return [operator].Right
                Else
                    Throw ExceptionUtilities.UnexpectedValue(index)
                End If
            End If
 
            Return GetChildOfBadExpressionBoundNode([operator].UnderlyingExpression, index)
        End Function
 
        Friend Function DeriveArguments(boundNode As BoundNode) As ImmutableArray(Of IArgumentOperation)
            Select Case boundNode.Kind
                Case BoundKind.Call
                    Dim boundCall = DirectCast(boundNode, BoundCall)
                    Return DeriveArguments(boundCall.Arguments, boundCall.Method.Parameters, boundCall.DefaultArguments)
                Case BoundKind.ObjectCreationExpression
                    Dim boundCreation = DirectCast(boundNode, BoundObjectCreationExpression)
                    If boundCreation.Arguments.IsDefault Then
                        Return ImmutableArray(Of IArgumentOperation).Empty
                    End If
                    Return If(boundCreation.ConstructorOpt Is Nothing, ImmutableArray(Of IArgumentOperation).Empty, DeriveArguments(boundCreation.Arguments, boundCreation.ConstructorOpt.Parameters, boundCreation.DefaultArguments))
                Case BoundKind.PropertyAccess
                    Dim boundProperty = DirectCast(boundNode, BoundPropertyAccess)
                    Return If(boundProperty.Arguments.Length = 0, ImmutableArray(Of IArgumentOperation).Empty, DeriveArguments(boundProperty.Arguments, boundProperty.PropertySymbol.Parameters, boundProperty.DefaultArguments))
                Case BoundKind.RaiseEventStatement
                    Dim boundRaiseEvent = DirectCast(boundNode, BoundRaiseEventStatement)
                    Return DeriveArguments(DirectCast(boundRaiseEvent.EventInvocation, BoundCall))
                Case BoundKind.Attribute
                    Dim boundAttribute = DirectCast(boundNode, BoundAttribute)
                    Return DeriveArguments(boundAttribute.ConstructorArguments, boundAttribute.Constructor.Parameters, boundAttribute.ConstructorDefaultArguments)
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(boundNode.Kind)
            End Select
        End Function
 
        Friend Function DeriveArguments(boundArguments As ImmutableArray(Of BoundExpression), parameters As ImmutableArray(Of VisualBasic.Symbols.ParameterSymbol), ByRef defaultArguments As BitVector) As ImmutableArray(Of IArgumentOperation)
            Dim argumentsLength As Integer = boundArguments.Length
            Debug.Assert(argumentsLength = parameters.Length)
 
            Dim arguments As ArrayBuilder(Of IArgumentOperation) = ArrayBuilder(Of IArgumentOperation).GetInstance(argumentsLength)
            For index As Integer = 0 To argumentsLength - 1 Step 1
                arguments.Add(DeriveArgument(index, boundArguments(index), parameters, defaultArguments(index)))
            Next
 
            Return arguments.ToImmutableAndFree()
        End Function
 
        Private Function DeriveArgument(
            index As Integer,
            argument As BoundExpression,
            parameters As ImmutableArray(Of VisualBasic.Symbols.ParameterSymbol),
            isDefault As Boolean
        ) As IArgumentOperation
            Dim isImplicit As Boolean = argument.WasCompilerGenerated AndAlso argument.Syntax.Kind <> SyntaxKind.OmittedArgument
            Select Case argument.Kind
                Case BoundKind.ByRefArgumentWithCopyBack
                    Dim byRefArgument = DirectCast(argument, BoundByRefArgumentWithCopyBack)
                    Dim parameter = parameters(index)
 
                    Return CreateArgumentOperation(
                        ArgumentKind.Explicit,
                        parameter,
                        byRefArgument.OriginalArgument,
                        CreateConversion(byRefArgument.InConversion),
                        CreateConversion(byRefArgument.OutConversion),
                        isImplicit)
                Case Else
                    Dim lastParameterIndex = parameters.Length - 1
                    Dim kind As ArgumentKind = ArgumentKind.Explicit
 
                    If argument.WasCompilerGenerated Then
                        If isDefault Then
                            kind = ArgumentKind.DefaultValue
                        ElseIf argument.Kind = BoundKind.ArrayCreation AndAlso DirectCast(argument, BoundArrayCreation).IsParamArrayArgument Then
                            kind = ArgumentKind.ParamArray
                        End If
                    End If
 
                    Return CreateArgumentOperation(
                        kind,
                        parameters(index),
                        argument,
                        New Conversion(Conversions.Identity),
                        New Conversion(Conversions.Identity),
                        isImplicit)
            End Select
        End Function
 
        Private Function CreateArgumentOperation(
            kind As ArgumentKind,
            parameter As IParameterSymbol,
            valueNode As BoundNode,
            inConversion As Conversion,
            outConversion As Conversion,
            isImplicit As Boolean) As IArgumentOperation
 
            ' put argument syntax to argument operation
            Dim syntax = If(valueNode.Syntax.Kind = SyntaxKind.OmittedArgument, valueNode.Syntax, TryCast(valueNode.Syntax?.Parent, ArgumentSyntax))
            Dim value = Create(valueNode)
 
            If syntax Is Nothing Then
                syntax = value.Syntax
                isImplicit = True
            End If
 
            Return New ArgumentOperation(
                kind,
                parameter,
                value,
                inConversion,
                outConversion,
                _semanticModel,
                syntax,
                isImplicit)
        End Function
 
        Friend Function CreateReceiverOperation(node As BoundNode, symbol As ISymbol) As IOperation
            If node Is Nothing OrElse node.Kind = BoundKind.TypeExpression Then
                Return Nothing
            End If
 
            If symbol IsNot Nothing AndAlso
               node.WasCompilerGenerated AndAlso
               symbol.IsStatic AndAlso
               (node.Kind = BoundKind.MeReference OrElse
                node.Kind = BoundKind.WithLValueExpressionPlaceholder OrElse
                node.Kind = BoundKind.WithRValueExpressionPlaceholder) Then
                Return Nothing
            End If
 
            Return Create(node)
        End Function
 
        Private Function GetChildOfBadExpression(parent As BoundNode, index As Integer) As IOperation
            Dim child = Create(GetChildOfBadExpressionBoundNode(parent, index))
            If child IsNot Nothing Then
                Return child
            End If
            Dim isImplicit As Boolean = parent.WasCompilerGenerated
            Return OperationFactory.CreateInvalidOperation(_semanticModel, parent.Syntax, ImmutableArray(Of IOperation).Empty, isImplicit)
        End Function
 
        Private Shared Function GetChildOfBadExpressionBoundNode(parent As BoundNode, index As Integer) As BoundExpression
            Dim badParent As BoundBadExpression = TryCast(parent, BoundBadExpression)
            If badParent?.ChildBoundNodes.Length > index Then
                Return badParent.ChildBoundNodes(index)
            End If
 
            Return Nothing
        End Function
 
        Friend Function GetAnonymousTypeCreationInitializers(expression As BoundAnonymousTypeCreationExpression) As ImmutableArray(Of IOperation)
            ' For error cases and non-assignment initializers, the binder generates only the argument.
            Debug.Assert(expression.Arguments.Length >= expression.Declarations.Length)
 
            Dim properties = DirectCast(expression.Type, AnonymousTypePublicSymbol).Properties
            Debug.Assert(properties.Length = expression.Arguments.Length)
 
            Dim builder = ArrayBuilder(Of IOperation).GetInstance(expression.Arguments.Length)
            Dim currentDeclarationIndex = 0
            For i As Integer = 0 To expression.Arguments.Length - 1
                Dim [property] As IPropertySymbol = properties(i)
                Dim value As IOperation = Create(expression.Arguments(i))
 
                Dim target As IOperation
                Dim isImplicitAssignment As Boolean
 
                ' Find matching declaration for the current argument
                If currentDeclarationIndex >= expression.Declarations.Length OrElse
                   i <> expression.Declarations(currentDeclarationIndex).PropertyIndex Then
                    ' No matching declaration, synthesize a property reference with an implicit receiver to be assigned.
                    Dim instance As IInstanceReferenceOperation = CreateAnonymousTypePropertyAccessImplicitReceiverOperation([property], expression.Syntax)
                    target = New PropertyReferenceOperation(
                        [property], constrainedToType:=Nothing,
                        ImmutableArray(Of IArgumentOperation).Empty,
                        instance,
                        _semanticModel,
                        value.Syntax,
                        [property].Type,
                        isImplicit:=True)
                    isImplicitAssignment = True
                Else
                    Debug.Assert(i = expression.Declarations(currentDeclarationIndex).PropertyIndex)
                    target = CreateBoundAnonymousTypePropertyAccessOperation(expression.Declarations(currentDeclarationIndex), [property])
                    currentDeclarationIndex = currentDeclarationIndex + 1
                    isImplicitAssignment = expression.WasCompilerGenerated
                End If
 
                Dim isRef As Boolean = False
                Dim syntax As SyntaxNode = If(value.Syntax?.Parent, expression.Syntax)
                Dim type As ITypeSymbol = target.Type
                Dim constantValue As ConstantValue = value.GetConstantValue()
                Dim assignment = New SimpleAssignmentOperation(isRef, target, value, _semanticModel, syntax, type, constantValue, isImplicitAssignment)
                builder.Add(assignment)
            Next i
 
            Debug.Assert(currentDeclarationIndex = expression.Declarations.Length)
            Return builder.ToImmutableAndFree()
        End Function
 
        Private Shared Function GetSingleValueCaseClauseValue(clause As BoundSingleValueCaseClause) As BoundExpression
            Return GetCaseClauseValue(clause.ValueOpt, clause.ConditionOpt)
        End Function
 
        Friend Shared Function GetCaseClauseValue(valueOpt As BoundExpression, conditionOpt As BoundExpression) As BoundExpression
            If valueOpt IsNot Nothing Then
                Return valueOpt
            End If
 
            Select Case conditionOpt.Kind
                Case BoundKind.BinaryOperator
                    Dim binaryOp As BoundBinaryOperator = DirectCast(conditionOpt, BoundBinaryOperator)
                    Return binaryOp.Right
 
                Case BoundKind.UserDefinedBinaryOperator
                    Dim binaryOp As BoundUserDefinedBinaryOperator = DirectCast(conditionOpt, BoundUserDefinedBinaryOperator)
                    Return GetUserDefinedBinaryOperatorChildBoundNode(binaryOp, 1)
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(conditionOpt.Kind)
            End Select
        End Function
 
        Friend Function GetVariableDeclarationStatementVariables(declarations As ImmutableArray(Of BoundLocalDeclarationBase)) As ImmutableArray(Of IVariableDeclarationOperation)
            ' Group the declarations by their VariableDeclaratorSyntaxes. The issue we're compensating for here is that the
            ' the declarations that are BoundLocalDeclaration nodes have a ModifiedIdentifierSyntax as their syntax nodes,
            ' not a VariableDeclaratorSyntax. We want to group BoundLocalDeclarations by their parent VariableDeclaratorSyntax
            ' nodes, and deduplicate based on that. As an example:
            '
            ' Dim x, y = 1
            '
            ' This is an error scenario, but if we just use the BoundLocalDeclaration.Syntax.Parent directly, without deduplicating,
            ' we'll end up with two IVariableDeclarators that have the same syntax node. So, we group by VariableDeclaratorSyntax
            ' to put x and y in the same IMultiVariableDeclaration
            Dim groupedDeclarations = declarations.GroupBy(Function(declaration)
                                                               If declaration.Kind = BoundKind.LocalDeclaration AndAlso
                                                                  declaration.Syntax.IsKind(SyntaxKind.ModifiedIdentifier) Then
                                                                   Debug.Assert(declaration.Syntax.Parent.IsKind(SyntaxKind.VariableDeclarator))
                                                                   Return declaration.Syntax.Parent
                                                               Else
                                                                   Return declaration.Syntax
                                                               End If
                                                           End Function)
 
            Dim builder = ArrayBuilder(Of IVariableDeclarationOperation).GetInstance()
            For Each declarationGroup In groupedDeclarations
                Dim first = declarationGroup.First()
                Dim declarators As ImmutableArray(Of IVariableDeclaratorOperation) = Nothing
                Dim initializer As IVariableInitializerOperation = Nothing
                If first.Kind = BoundKind.LocalDeclaration Then
                    declarators = declarationGroup.Cast(Of BoundLocalDeclaration).SelectAsArray(AddressOf GetVariableDeclarator)
 
                    ' The initializer we use for this group is the initializer attached to the last declaration in this declarator, as that's
                    ' where it will be parsed in an error case.
                    ' Initializer is only created if it's not the array initializer for the variable. That initializer is the initializer
                    ' of the VariableDeclarator child.
                    Dim last = DirectCast(declarationGroup.Last(), BoundLocalDeclaration)
                    If last.DeclarationInitializerOpt IsNot Nothing Then
                        Debug.Assert(last.Syntax.IsKind(SyntaxKind.ModifiedIdentifier))
                        Dim declaratorSyntax = DirectCast(last.Syntax.Parent, VariableDeclaratorSyntax)
                        Dim initializerSyntax As SyntaxNode = declaratorSyntax.Initializer
 
                        ' As New clauses with a single variable are bound as BoundLocalDeclarations, so adjust appropriately
                        Dim isImplicit As Boolean = False
                        If last.InitializedByAsNew Then
                            initializerSyntax = declaratorSyntax.AsClause
                        ElseIf initializerSyntax Is Nothing Then
                            ' There is no explicit syntax for the initializer, so we use the initializerValue's syntax and mark the operation as implicit.
                            initializerSyntax = last.InitializerOpt.Syntax
                            isImplicit = True
                        End If
                        Debug.Assert(last.InitializerOpt IsNot Nothing)
                        Dim value = Create(last.InitializerOpt)
                        initializer = New VariableInitializerOperation(locals:=ImmutableArray(Of ILocalSymbol).Empty, value, _semanticModel, initializerSyntax, isImplicit)
                    End If
                Else
                    Dim asNewDeclarations = DirectCast(first, BoundAsNewLocalDeclarations)
                    declarators = asNewDeclarations.LocalDeclarations.SelectAsArray(AddressOf GetVariableDeclarator)
                    Dim initializerSyntax As AsClauseSyntax = DirectCast(asNewDeclarations.Syntax, VariableDeclaratorSyntax).AsClause
                    Dim initializerValue As IOperation = Create(asNewDeclarations.Initializer)
                    Debug.Assert(asNewDeclarations.Initializer IsNot Nothing)
                    Dim value = Create(asNewDeclarations.Initializer)
                    initializer = New VariableInitializerOperation(locals:=ImmutableArray(Of ILocalSymbol).Empty, value, _semanticModel, initializerSyntax, isImplicit:=False)
                End If
 
                builder.Add(New VariableDeclarationOperation(declarators,
                                                             initializer,
                                                             ImmutableArray(Of IOperation).Empty,
                                                             _semanticModel,
                                                             declarationGroup.Key,
                                                             isImplicit:=False))
            Next
 
            Return builder.ToImmutableAndFree()
        End Function
 
        Private Function GetVariableDeclarator(boundLocalDeclaration As BoundLocalDeclaration) As IVariableDeclaratorOperation
            Dim initializer As IVariableInitializerOperation = Nothing
            If boundLocalDeclaration.IdentifierInitializerOpt IsNot Nothing Then
                Dim syntax = boundLocalDeclaration.Syntax
                Dim initializerValue As IOperation = Create(boundLocalDeclaration.IdentifierInitializerOpt)
                initializer = New VariableInitializerOperation(locals:=ImmutableArray(Of ILocalSymbol).Empty, initializerValue, _semanticModel, syntax, isImplicit:=True)
            End If
 
            Dim ignoredArguments = ImmutableArray(Of IOperation).Empty
 
            Return New VariableDeclaratorOperation(boundLocalDeclaration.LocalSymbol, initializer, ignoredArguments, _semanticModel, boundLocalDeclaration.Syntax, isImplicit:=boundLocalDeclaration.WasCompilerGenerated)
        End Function
 
        Private Function GetUsingStatementDeclaration(resourceList As ImmutableArray(Of BoundLocalDeclarationBase), syntax As SyntaxNode) As IVariableDeclarationGroupOperation
            Return New VariableDeclarationGroupOperation(
                            GetVariableDeclarationStatementVariables(resourceList),
                            _semanticModel,
                            syntax,
                            isImplicit:=False) ' Declaration is always explicit
        End Function
 
        Friend Function GetAddRemoveHandlerStatementExpression(statement As BoundAddRemoveHandlerStatement) As IOperation
            Dim eventReference As IOperation = Create(statement.EventAccess)
            Dim handlerValue As IOperation = Create(statement.Handler)
            Dim adds = statement.Kind = BoundKind.AddHandlerStatement
            Return New EventAssignmentOperation(eventReference, handlerValue, adds, _semanticModel, statement.Syntax, type:=Nothing, isImplicit:=True)
        End Function
 
#Region "Conversions"
 
        ''' <summary>
        ''' Creates the Lazy IOperation from a delegate creation operand or a bound conversion operand, handling when the conversion
        ''' is actually a delegate creation.
        ''' </summary>
        Private Function GetConversionInfo(boundConversion As BoundConversionOrCast
                                          ) As (Operation As IOperation, Conversion As Conversion, IsDelegateCreation As Boolean)
            Dim conversion = CreateConversion(boundConversion)
            Dim boundOperand = GetConversionOperand(boundConversion)
            If conversion.IsIdentity AndAlso boundConversion.ExplicitCastInCode Then
                Dim adjustedInfo = TryGetAdjustedConversionInfo(boundConversion, boundOperand)
 
                If adjustedInfo.Operation IsNot Nothing Then
                    Return adjustedInfo
                End If
            End If
 
            If IsDelegateCreation(boundConversion.Syntax, boundOperand, boundConversion.Type) Then
                Return (CreateDelegateCreationConversionOperand(boundOperand),
                    conversion, IsDelegateCreation:=True)
            Else
                Return (Create(boundOperand), conversion, IsDelegateCreation:=False)
            End If
        End Function
 
        Private Function TryGetAdjustedConversionInfo(topLevelConversion As BoundConversionOrCast, boundOperand As BoundExpression
                                                     ) As (Operation As IOperation, Conversion As Conversion, IsDelegateCreation As Boolean)
            ' Dig through the bound tree to see if this is an artificial nested conversion. If so, let's erase the nested conversion.
            ' Artificial conversions are added on top of BoundConvertedTupleLiteral in Binder.ReclassifyTupleLiteral, or in
            ' ReclassifyUnboundLambdaExpression and the like. We need to use conversion information from the nested conversion
            ' because that is where the real conversion information is stored.
            If boundOperand.Kind = BoundKind.Parenthesized Then
                Dim adjustedInfo = TryGetAdjustedConversionInfo(topLevelConversion, DirectCast(boundOperand, BoundParenthesized).Expression)
                If adjustedInfo.Operation IsNot Nothing Then
                    Return (Operation:=New ParenthesizedOperation(adjustedInfo.Operation,
                                                                   _semanticModel,
                                                                   boundOperand.Syntax,
                                                                   adjustedInfo.Operation.Type,
                                                                   boundOperand.ConstantValueOpt,
                                                                   boundOperand.WasCompilerGenerated),
                            adjustedInfo.Conversion,
                            adjustedInfo.IsDelegateCreation)
                End If
            ElseIf boundOperand.Kind = topLevelConversion.Kind Then
                Dim nestedConversion = DirectCast(boundOperand, BoundConversionOrCast)
                Dim nestedOperand As BoundExpression = GetConversionOperand(nestedConversion)
 
                If nestedConversion.Syntax Is nestedOperand.Syntax AndAlso
                   Not TypeSymbol.Equals(nestedConversion.Type, nestedOperand.Type, TypeCompareKind.ConsiderEverything) AndAlso
                   nestedConversion.ExplicitCastInCode AndAlso
                   TypeSymbol.Equals(topLevelConversion.Type, nestedConversion.Type, TypeCompareKind.ConsiderEverything) Then
 
                    Return GetConversionInfo(nestedConversion)
                End If
            ElseIf boundOperand.Syntax.IsKind(SyntaxKind.AddressOfExpression) AndAlso
                   TypeSymbol.Equals(topLevelConversion.Type, boundOperand.Type, TypeCompareKind.ConsiderEverything) AndAlso
                   IsDelegateCreation(topLevelConversion.Syntax, boundOperand, boundOperand.Type) Then
 
                Return (CreateDelegateCreationConversionOperand(boundOperand), Conversion:=Nothing, IsDelegateCreation:=True)
            End If
 
            Return Nothing
        End Function
 
        ''' <summary>
        ''' Gets the operand from a BoundConversion, compensating for if the conversion is a user-defined conversion
        ''' </summary>
        Private Shared Function GetConversionOperand(boundConversion As BoundConversionOrCast) As BoundExpression
            If (boundConversion.ConversionKind And ConversionKind.UserDefined) = ConversionKind.UserDefined Then
                Dim userDefinedConversion = DirectCast(boundConversion.Operand, BoundUserDefinedConversion)
                Return userDefinedConversion.Operand
            Else
                Return boundConversion.Operand
            End If
        End Function
 
        Private Function CreateDelegateCreationConversionOperand(operand As BoundExpression) As IOperation
            If operand.Kind = BoundKind.DelegateCreationExpression Then
                ' If the child is a BoundDelegateCreationExpression, we don't want to generate a nested IDelegateCreationExpression.
                ' So, the operand for the conversion will be the child of the BoundDelegateCreationExpression.
                ' We see this in this syntax: Dim x = New Action(AddressOf M2)
                ' This should be semantically equivalent to: Dim x = AddressOf M2
                ' However, if we didn't fix this up, we would have nested IDelegateCreationExpressions here for the former case.
                Return CreateBoundDelegateCreationExpressionChildOperation(DirectCast(operand, BoundDelegateCreationExpression))
            Else
                Return Create(operand)
            End If
        End Function
 
        Private Shared Function CreateConversion(expression As BoundExpression) As Conversion
            If expression.Kind = BoundKind.Conversion Then
                Dim conversion = DirectCast(expression, BoundConversion)
                Dim conversionKind = conversion.ConversionKind
                Dim method As MethodSymbol = Nothing
                If conversionKind.HasFlag(VisualBasic.ConversionKind.UserDefined) AndAlso conversion.Operand.Kind = BoundKind.UserDefinedConversion Then
                    method = DirectCast(conversion.Operand, BoundUserDefinedConversion).Call.Method
                End If
                Return New Conversion(KeyValuePairUtil.Create(conversionKind, method))
            ElseIf expression.Kind = BoundKind.TryCast OrElse expression.Kind = BoundKind.DirectCast Then
                Return New Conversion(KeyValuePairUtil.Create(Of ConversionKind, MethodSymbol)(DirectCast(expression, BoundConversionOrCast).ConversionKind, Nothing))
            End If
            Return New Conversion(Conversions.Identity)
        End Function
 
        Private Shared Function IsDelegateCreation(conversionSyntax As SyntaxNode, operand As BoundNode, targetType As TypeSymbol) As Boolean
            If Not targetType.IsDelegateType() Then
                Return False
            End If
 
            ' Any of the explicit cast types, as well as New DelegateType(AddressOf Method)
            ' Additionally, AddressOf, if the child AddressOf is the same SyntaxNode (ie, an implicit delegate creation)
            ' In the case of AddressOf, the operand can be a BoundDelegateCreationExpression, a BoundAddressOfOperator, or
            ' a BoundBadExpression. For simplicity, we just do a syntax check to make sure it's an AddressOfExpression so
            ' we don't have to compare against all 3 BoundKinds
            Dim validAddressOfConversionSyntax = operand.Syntax.Kind() = SyntaxKind.AddressOfExpression AndAlso
                                                 (conversionSyntax.Kind() = SyntaxKind.CTypeExpression OrElse
                                                  conversionSyntax.Kind() = SyntaxKind.DirectCastExpression OrElse
                                                  conversionSyntax.Kind() = SyntaxKind.TryCastExpression OrElse
                                                  conversionSyntax.Kind() = SyntaxKind.ObjectCreationExpression OrElse
                                                  (conversionSyntax.Kind() = SyntaxKind.AddressOfExpression AndAlso
                                                   conversionSyntax Is operand.Syntax))
 
            Dim validLambdaConversionNode = operand.Kind = BoundKind.Lambda OrElse
                                              operand.Kind = BoundKind.QueryLambda OrElse
                                              operand.Kind = BoundKind.UnboundLambda
 
            Return validAddressOfConversionSyntax OrElse validLambdaConversionNode
        End Function
 
#End Region
 
        Friend Class Helper
            Friend Shared Function DeriveUnaryOperatorKind(operatorKind As VisualBasic.UnaryOperatorKind) As UnaryOperatorKind
                Select Case operatorKind And VisualBasic.UnaryOperatorKind.OpMask
                    Case VisualBasic.UnaryOperatorKind.Plus
                        Return UnaryOperatorKind.Plus
                    Case VisualBasic.UnaryOperatorKind.Minus
                        Return UnaryOperatorKind.Minus
                    Case VisualBasic.UnaryOperatorKind.Not
                        Return UnaryOperatorKind.Not
                    Case VisualBasic.UnaryOperatorKind.IsTrue
                        Return UnaryOperatorKind.True
                    Case VisualBasic.UnaryOperatorKind.IsFalse
                        Return UnaryOperatorKind.False
                    Case Else
                        Return UnaryOperatorKind.None
                End Select
            End Function
 
            Friend Shared Function DeriveBinaryOperatorKind(operatorKind As VisualBasic.BinaryOperatorKind, leftOpt As BoundExpression) As BinaryOperatorKind
                Select Case operatorKind And VisualBasic.BinaryOperatorKind.OpMask
                    Case VisualBasic.BinaryOperatorKind.Add
                        Return BinaryOperatorKind.Add
                    Case VisualBasic.BinaryOperatorKind.Subtract
                        Return BinaryOperatorKind.Subtract
                    Case VisualBasic.BinaryOperatorKind.Multiply
                        Return BinaryOperatorKind.Multiply
                    Case VisualBasic.BinaryOperatorKind.Divide
                        Return BinaryOperatorKind.Divide
                    Case VisualBasic.BinaryOperatorKind.IntegerDivide
                        Return BinaryOperatorKind.IntegerDivide
                    Case VisualBasic.BinaryOperatorKind.Modulo
                        Return BinaryOperatorKind.Remainder
                    Case VisualBasic.BinaryOperatorKind.And
                        Return BinaryOperatorKind.And
                    Case VisualBasic.BinaryOperatorKind.Or
                        Return BinaryOperatorKind.Or
                    Case VisualBasic.BinaryOperatorKind.Xor
                        Return BinaryOperatorKind.ExclusiveOr
                    Case VisualBasic.BinaryOperatorKind.AndAlso
                        Return BinaryOperatorKind.ConditionalAnd
                    Case VisualBasic.BinaryOperatorKind.OrElse
                        Return BinaryOperatorKind.ConditionalOr
                    Case VisualBasic.BinaryOperatorKind.LeftShift
                        Return BinaryOperatorKind.LeftShift
                    Case VisualBasic.BinaryOperatorKind.RightShift
                        Return BinaryOperatorKind.RightShift
                    Case VisualBasic.BinaryOperatorKind.LessThan
                        Return BinaryOperatorKind.LessThan
                    Case VisualBasic.BinaryOperatorKind.LessThanOrEqual
                        Return BinaryOperatorKind.LessThanOrEqual
                    Case VisualBasic.BinaryOperatorKind.Equals
                        Return If(leftOpt?.Type?.SpecialType = SpecialType.System_Object, BinaryOperatorKind.ObjectValueEquals, BinaryOperatorKind.Equals)
                    Case VisualBasic.BinaryOperatorKind.NotEquals
                        Return If(leftOpt?.Type?.SpecialType = SpecialType.System_Object, BinaryOperatorKind.ObjectValueNotEquals, BinaryOperatorKind.NotEquals)
                    Case VisualBasic.BinaryOperatorKind.Is
                        Return BinaryOperatorKind.Equals
                    Case VisualBasic.BinaryOperatorKind.IsNot
                        Return BinaryOperatorKind.NotEquals
                    Case VisualBasic.BinaryOperatorKind.GreaterThanOrEqual
                        Return BinaryOperatorKind.GreaterThanOrEqual
                    Case VisualBasic.BinaryOperatorKind.GreaterThan
                        Return BinaryOperatorKind.GreaterThan
                    Case VisualBasic.BinaryOperatorKind.Power
                        Return BinaryOperatorKind.Power
                    Case VisualBasic.BinaryOperatorKind.Like
                        Return BinaryOperatorKind.Like
                    Case VisualBasic.BinaryOperatorKind.Concatenate
                        Return BinaryOperatorKind.Concatenate
                    Case Else
                        Return BinaryOperatorKind.None
                End Select
            End Function
        End Class
    End Class
End Namespace