File: Lowering\Diagnostics\DiagnosticsPass_ExpressionLambdas.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
Imports System.Collections.Generic
Imports System.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    Partial Friend Class DiagnosticsPass
 
        Private ReadOnly _expressionTreePlaceholders As New HashSet(Of BoundNode)(ReferenceEqualityComparer.Instance)
 
        Public Overrides Function VisitObjectCreationExpression(node As BoundObjectCreationExpression) As BoundNode
            If Me.IsInExpressionLambda Then
                Dim initializer As BoundObjectInitializerExpressionBase = node.InitializerOpt
                If initializer IsNot Nothing AndAlso initializer.Kind = BoundKind.ObjectInitializerExpression AndAlso node.ConstantValueOpt Is Nothing Then
                    ' report an error for the cases where ExpressionLambdaRewriter is going to emit
                    ' a call to a value type constructor with arguments, it would require creating 
                    ' a temp and calling the constructor on this temp
                    If initializer.Type.IsValueType AndAlso node.ConstructorOpt IsNot Nothing AndAlso node.Arguments.Length > 0 Then
                        GenerateExpressionTreeNotSupportedDiagnostic(initializer)
                    End If
                End If
            End If
 
            Return MyBase.VisitObjectCreationExpression(node)
        End Function
 
        Public Overrides Function VisitUserDefinedUnaryOperator(node As BoundUserDefinedUnaryOperator) As BoundNode
            If Me.IsInExpressionLambda Then
                Dim opKind As UnaryOperatorKind = node.OperatorKind And UnaryOperatorKind.OpMask
                Dim isLifted As Boolean = (node.OperatorKind And UnaryOperatorKind.Lifted) <> 0
 
                Select Case opKind
                    Case UnaryOperatorKind.Minus,
                         UnaryOperatorKind.Plus,
                         UnaryOperatorKind.Not
 
                        If isLifted Then
                            Dim method As MethodSymbol = node.Call.Method
                            If method.ReturnType.IsNullableType Then
                                ' TODO: There is a bug in Dev11 when the resulting expression tree fails to build in 
                                '       case the binary operator is lifted, but the method has nullable return type.
                                ' MORE: bug#18100
                                GenerateExpressionTreeNotSupportedDiagnostic(node)
                            End If
                        End If
                End Select
            End If
 
            Return MyBase.VisitUserDefinedUnaryOperator(node)
        End Function
 
        Public Overrides Function VisitAnonymousTypePropertyAccess(node As BoundAnonymousTypePropertyAccess) As BoundNode
            If Me.IsInExpressionLambda Then
                ' we do not allow anonymous objects which use one field to initialize another one
                GenerateDiagnostic(ERRID.ERR_BadAnonymousTypeForExprTree, node)
            End If
 
            Return MyBase.VisitAnonymousTypePropertyAccess(node)
        End Function
 
        Public Overrides Function VisitAnonymousTypeCreationExpression(node As BoundAnonymousTypeCreationExpression) As BoundNode
            ' Don't really care for declarations
            'Me.VisitList(node.Declarations)
            Debug.Assert(node.Declarations.All(Function(d) d.Kind = BoundKind.AnonymousTypePropertyAccess))
 
            Me.VisitList(node.Arguments)
            Return Nothing
        End Function
 
        Public Overrides Function VisitSequence(node As BoundSequence) As BoundNode
            If Not node.Locals.IsEmpty AndAlso Me.IsInExpressionLambda Then
                ' All such cases are not supported, note that some cases of invalid
                ' sequences are handled in DiagnosticsPass, but we still want to catch
                ' here those sequences created in lowering
                GenerateExpressionTreeNotSupportedDiagnostic(node)
            End If
 
            Return MyBase.VisitSequence(node)
        End Function
 
        Public Overrides Function VisitUserDefinedBinaryOperator(node As BoundUserDefinedBinaryOperator) As BoundNode
            If Me.IsInExpressionLambda Then
                Dim opKind As BinaryOperatorKind = node.OperatorKind And BinaryOperatorKind.OpMask
 
                Select Case opKind
                    Case BinaryOperatorKind.Like,
                         BinaryOperatorKind.Concatenate
                        'Do Nothing
 
                    Case Else
                        If (node.OperatorKind And BinaryOperatorKind.Lifted) <> 0 Then
                            Dim method As MethodSymbol = node.Call.Method
                            If method.ReturnType.IsNullableType Then
                                ' TODO: There is a bug in Dev11 when the resulting expression tree fails to build in 
                                '       case the binary operator is lifted, but the method has nullable return type.
                                ' MORE: bug#18096
                                GenerateExpressionTreeNotSupportedDiagnostic(node)
                            End If
                        End If
                End Select
            End If
 
            Return MyBase.VisitUserDefinedBinaryOperator(node)
        End Function
 
        Public Overrides Function VisitObjectInitializerExpression(node As BoundObjectInitializerExpression) As BoundNode
            If Not Me.IsInExpressionLambda Then
                Return MyBase.VisitObjectInitializerExpression(node)
            End If
 
            Dim placeholder As BoundWithLValueExpressionPlaceholder = node.PlaceholderOpt
            Debug.Assert(placeholder IsNot Nothing)
            Me.Visit(placeholder)
 
            ' Initializers cannot reference placeholder
            Me._expressionTreePlaceholders.Add(placeholder)
 
            For Each initializer In node.Initializers
                ' Ignore assignments in object initializers, only reference the value
                Dim boundExpression As BoundExpression = initializer
                If boundExpression.Kind = BoundKind.AssignmentOperator Then
                    Dim assignment = DirectCast(initializer, BoundAssignmentOperator)
                    Debug.Assert(assignment.LeftOnTheRightOpt Is Nothing)
 
                    boundExpression = assignment.Right
                    Dim propertyAccess = TryCast(assignment.Left, BoundPropertyAccess)
                    If propertyAccess IsNot Nothing Then
                        CheckRefReturningPropertyAccess(propertyAccess)
                    End If
                End If
 
                Me.Visit(boundExpression)
            Next
 
            Me._expressionTreePlaceholders.Remove(placeholder)
 
            Return Nothing
        End Function
 
        Public Overrides Function VisitWithLValueExpressionPlaceholder(node As BoundWithLValueExpressionPlaceholder) As BoundNode
            If Me._expressionTreePlaceholders.Contains(node) Then
                GenerateExpressionTreeNotSupportedDiagnostic(node)
            End If
 
            CheckMeAccessInWithExpression(node)
 
            Return MyBase.VisitWithLValueExpressionPlaceholder(node)
        End Function
 
        Public Overrides Function VisitAssignmentOperator(node As BoundAssignmentOperator) As BoundNode
            'COMPAT: old compiler used to allow assignments to properties
            '        we will continue allowing that too
            'NOTE:   native vbc also allows compound assignments like += but generates incorrect code.
            '        we are not going to support += assuming that it is not likely to be used in real scenarios.
            If Me.IsInExpressionLambda AndAlso
                    Not (node.Left.Kind = BoundKind.PropertyAccess AndAlso node.LeftOnTheRightOpt Is Nothing) Then
 
                ' Do not support explicit assignments
                GenerateExpressionTreeNotSupportedDiagnostic(node)
            End If
 
            Return MyBase.VisitAssignmentOperator(node)
        End Function
 
        Public Overrides Function VisitFieldAccess(node As BoundFieldAccess) As BoundNode
            Dim field As FieldSymbol = node.FieldSymbol
            If Not field.IsShared Then
                Me.Visit(node.ReceiverOpt)
            End If
            Return Nothing
        End Function
 
        Public Overrides Function VisitArrayCreation(node As BoundArrayCreation) As BoundNode
            If Me.IsInExpressionLambda Then
                If Not DirectCast(node.Type, ArrayTypeSymbol).IsSZArray Then
                    Dim initializer As BoundArrayInitialization = node.InitializerOpt
                    If initializer IsNot Nothing AndAlso Not initializer.Initializers.IsEmpty Then
                        GenerateDiagnostic(ERRID.ERR_ExprTreeNoMultiDimArrayCreation, node)
                    End If
                End If
            End If
 
            Return MyBase.VisitArrayCreation(node)
        End Function
 
        Public Overrides Function VisitLambda(node As BoundLambda) As BoundNode
            If Me.IsInExpressionLambda Then
                Dim lambda As LambdaSymbol = node.LambdaSymbol
 
                If lambda.IsAsync OrElse lambda.IsIterator Then
                    GenerateDiagnostic(ERRID.ERR_ResumableLambdaInExpressionTree, node)
 
                ElseIf Not node.WasCompilerGenerated AndAlso Not node.IsSingleLine Then
                    GenerateDiagnostic(ERRID.ERR_StatementLambdaInExpressionTree, node)
 
                Else
                    Select Case lambda.Syntax.Kind
                        Case SyntaxKind.MultiLineFunctionLambdaExpression,
                             SyntaxKind.MultiLineSubLambdaExpression
                            GenerateDiagnostic(ERRID.ERR_StatementLambdaInExpressionTree, node)
 
                        Case SyntaxKind.SingleLineSubLambdaExpression,
                             SyntaxKind.SingleLineFunctionLambdaExpression
 
                            Dim needDiagnostics As Boolean = True
                            Dim block As BoundBlock = node.Body
                            If block.Statements.Length = 1 OrElse
                                    (block.Statements.Length = 2 AndAlso
                                     block.Statements(1).Kind = BoundKind.ReturnStatement AndAlso
                                     DirectCast(block.Statements(1), BoundReturnStatement).ExpressionOpt Is Nothing) OrElse
                                    (block.Statements.Length = 3 AndAlso
                                     block.Statements(1).Kind = BoundKind.LabelStatement AndAlso
                                     block.Statements(2).Kind = BoundKind.ReturnStatement) Then
 
                                Dim stmt = block.Statements(0)
lSelect:
                                Select Case stmt.Kind
                                    Case BoundKind.ReturnStatement
                                        If (DirectCast(stmt, BoundReturnStatement)).ExpressionOpt IsNot Nothing Then
                                            needDiagnostics = False
                                        End If
 
                                    Case BoundKind.ExpressionStatement,
                                         BoundKind.AddHandlerStatement,
                                         BoundKind.RemoveHandlerStatement
                                        needDiagnostics = False
 
                                    Case BoundKind.Block
                                        Dim innerBlock = DirectCast(stmt, BoundBlock)
                                        If innerBlock.Locals.IsEmpty AndAlso innerBlock.Statements.Length = 1 Then
                                            stmt = innerBlock.Statements(0)
                                            GoTo lSelect
                                        End If
                                End Select
                            End If
 
                            If needDiagnostics Then
                                GenerateDiagnostic(ERRID.ERR_StatementLambdaInExpressionTree, node)
                            End If
                    End Select
                End If
            End If
 
            Dim save_containingSymbol = Me._containingSymbol
            Me._containingSymbol = node.LambdaSymbol
            Me.Visit(node.Body)
            Me._containingSymbol = save_containingSymbol
            Return Nothing
        End Function
 
        Public Overrides Function VisitCall(node As BoundCall) As BoundNode
            Dim method As MethodSymbol = node.Method
            If Not method.IsShared Then
                Me.Visit(node.ReceiverOpt)
            End If
 
            If IsInExpressionLambda And method.ReturnsByRef Then
                GenerateDiagnostic(ERRID.ERR_RefReturningCallInExpressionTree, node)
            End If
 
            Me.VisitList(node.Arguments)
            Return Nothing
        End Function
 
        Public Overrides Function VisitPropertyAccess(node As BoundPropertyAccess) As BoundNode
            Dim [property] As PropertySymbol = node.PropertySymbol
            If Not [property].IsShared Then
                Me.Visit(node.ReceiverOpt)
            End If
 
            CheckRefReturningPropertyAccess(node)
 
            Me.VisitList(node.Arguments)
            Return Nothing
        End Function
 
        Private Sub CheckRefReturningPropertyAccess(node As BoundPropertyAccess)
            If IsInExpressionLambda AndAlso node.PropertySymbol.ReturnsByRef Then
                GenerateDiagnostic(ERRID.ERR_RefReturningCallInExpressionTree, node)
            End If
        End Sub
 
        Public Overrides Function VisitEventAccess(node As BoundEventAccess) As BoundNode
            Dim [event] As EventSymbol = node.EventSymbol
            If Not [event].IsShared Then
                Me.Visit(node.ReceiverOpt)
            End If
            Return Nothing
        End Function
 
        Private Sub VisitLambdaConversion(operand As BoundExpression, relaxationLambda As BoundLambda)
            Debug.Assert(operand IsNot Nothing AndAlso
                         (operand.Kind = BoundKind.Lambda OrElse operand.Kind = BoundKind.QueryLambda))
 
            If operand.Kind = BoundKind.Lambda AndAlso Not CheckLambdaForByRefParameters(DirectCast(operand, BoundLambda)) AndAlso relaxationLambda IsNot Nothing Then
                CheckLambdaForByRefParameters(relaxationLambda)
            End If
 
            Me.Visit(operand)
        End Sub
 
        Private Function CheckLambdaForByRefParameters(lambda As BoundLambda) As Boolean
            Debug.Assert(Me.IsInExpressionLambda)
            Debug.Assert(lambda IsNot Nothing)
 
            Dim hasByRefParameters As Boolean = False
            For Each p In lambda.LambdaSymbol.Parameters
                If p.IsByRef Then
                    GenerateDiagnostic(ERRID.ERR_ByRefParamInExpressionTree, lambda)
                    Return True
                End If
            Next
 
            Return False
        End Function
 
        Public Overrides Function VisitConversion(node As BoundConversion) As BoundNode
            Dim savedInExpressionLambda As Boolean = Me._inExpressionLambda
            If (node.ConversionKind And ConversionKind.ConvertedToExpressionTree) <> 0 Then
                Me._inExpressionLambda = True
            End If
 
            If Me.IsInExpressionLambda AndAlso (node.ConversionKind And ConversionKind.Lambda) <> 0 Then
                VisitLambdaConversion(node.Operand, DirectCast(node.ExtendedInfoOpt, BoundRelaxationLambda)?.Lambda)
            Else
                MyBase.VisitConversion(node)
            End If
 
            Me._inExpressionLambda = savedInExpressionLambda
            Return Nothing
        End Function
 
        Public Overrides Function VisitTryCast(node As BoundTryCast) As BoundNode
            Dim savedInExpressionLambda As Boolean = Me._inExpressionLambda
            If (node.ConversionKind And ConversionKind.ConvertedToExpressionTree) <> 0 Then
                Me._inExpressionLambda = True
            End If
 
            If Me.IsInExpressionLambda AndAlso (node.ConversionKind And ConversionKind.Lambda) <> 0 Then
                VisitLambdaConversion(node.Operand, node.RelaxationLambdaOpt)
            Else
                MyBase.VisitTryCast(node)
            End If
 
            Me._inExpressionLambda = savedInExpressionLambda
            Return Nothing
        End Function
 
        Public Overrides Function VisitDirectCast(node As BoundDirectCast) As BoundNode
            Dim savedInExpressionLambda As Boolean = Me._inExpressionLambda
            If (node.ConversionKind And ConversionKind.ConvertedToExpressionTree) <> 0 Then
                Me._inExpressionLambda = True
            End If
 
            If Me.IsInExpressionLambda AndAlso (node.ConversionKind And ConversionKind.Lambda) <> 0 Then
                VisitLambdaConversion(node.Operand, node.RelaxationLambdaOpt)
            Else
                MyBase.VisitDirectCast(node)
            End If
 
            Me._inExpressionLambda = savedInExpressionLambda
            Return Nothing
        End Function
 
        Public Overrides Function VisitLateInvocation(node As BoundLateInvocation) As BoundNode
            If Not Me.IsInExpressionLambda Then
                Return MyBase.VisitLateInvocation(node)
            End If
 
            GenerateDiagnostic(ERRID.ERR_ExprTreeNoLateBind, node)
 
            If node.Member.Kind <> BoundKind.LateMemberAccess Then
                Me.Visit(node.Member)
            End If
            Me.VisitList(node.ArgumentsOpt)
            Return Nothing
        End Function
 
        Public Overrides Function VisitLateMemberAccess(node As BoundLateMemberAccess) As BoundNode
            If Me.IsInExpressionLambda Then
                GenerateDiagnostic(ERRID.ERR_ExprTreeNoLateBind, node)
            End If
 
            Return MyBase.VisitLateMemberAccess(node)
        End Function
 
        Public Overrides Function VisitConditionalAccess(node As BoundConditionalAccess) As BoundNode
            If Me.IsInExpressionLambda Then
                GenerateDiagnostic(ERRID.ERR_NullPropagatingOpInExpressionTree, node)
            End If
 
            Return MyBase.VisitConditionalAccess(node)
        End Function
 
        Private Sub GenerateExpressionTreeNotSupportedDiagnostic(node As BoundNode)
            GenerateDiagnostic(ERRID.ERR_ExpressionTreeNotSupported, node)
        End Sub
 
        Private Sub GenerateDiagnostic(code As ERRID, node As BoundNode)
            Me._diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(code), node.Syntax.GetLocation()))
        End Sub
 
        Public Overrides Function VisitInterpolatedStringExpression(node As BoundInterpolatedStringExpression) As BoundNode
            Visit(node.ConstructionOpt)
            Return MyBase.VisitInterpolatedStringExpression(node)
        End Function
    End Class
 
End Namespace