From 7da143edf24e9b1b9ae531258cc854a48236b0a8 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 6 Nov 2017 16:50:27 -0800 Subject: [PATCH] Enable compound expressions with conversions in them. --- .../Operations/VisualBasicOperationFactory.vb | 15 +- .../VisualBasicOperationFactory_Methods.vb | 125 ++++++- ...perationTests_IBinaryOperatorExpression.vb | 96 +----- ...ationTests_ICompoundAssignmentOperation.vb | 324 ++++++++++++++++++ 4 files changed, 460 insertions(+), 100 deletions(-) create mode 100644 src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ICompoundAssignmentOperation.vb diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb index 1d4977c86ef..1b1c8ec33f2 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb @@ -272,26 +272,23 @@ Namespace Microsoft.CodeAnalysis.Operations End Function Private Function CreateBoundAssignmentOperatorOperation(boundAssignmentOperator As BoundAssignmentOperator) As IOperation - Dim kind = GetAssignmentKind(boundAssignmentOperator) + Dim assignmentInfo = GetAssignmentInfo(boundAssignmentOperator) Dim isImplicit As Boolean = boundAssignmentOperator.WasCompilerGenerated - If kind = OperationKind.CompoundAssignment Then - ' convert Right to IOperation temporarily. we do this to get right operand, operator method and etc - Dim temporaryRight = DirectCast(Create(boundAssignmentOperator.Right), IBinaryOperation) + If assignmentInfo.OperationKind = OperationKind.CompoundAssignment Then - Dim operatorKind As BinaryOperatorKind = temporaryRight.OperatorKind + Dim operatorKind As BinaryOperatorKind = assignmentInfo.OperatorKind Dim target As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() Create(boundAssignmentOperator.Left)) ' right now, parent of right operand is set to the temporary IOperation, reset the parent ' we basically need to do this since we skip BoundAssignmentOperator.Right from IOperation tree - Dim rightOperand = Operation.ResetParentOperation(temporaryRight.RightOperand) - Dim value As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() rightOperand) + Dim value As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() Create(assignmentInfo.RightNode)) - Dim operatorMethod As IMethodSymbol = temporaryRight.OperatorMethod + Dim operatorMethod As IMethodSymbol = assignmentInfo.OperatorMethod Dim syntax As SyntaxNode = boundAssignmentOperator.Syntax Dim type As ITypeSymbol = boundAssignmentOperator.Type Dim constantValue As [Optional](Of Object) = ConvertToOptional(boundAssignmentOperator.ConstantValueOpt) Dim isLifted As Boolean = boundAssignmentOperator.Type.IsNullableType() - Dim isChecked As Boolean = temporaryRight.IsChecked + Dim isChecked As Boolean = assignmentInfo.IsChecked Return New LazyCompoundAssignmentExpression(operatorKind, isLifted, isChecked, target, value, operatorMethod, _semanticModel, syntax, type, constantValue, isImplicit) Else Dim target As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() Create(boundAssignmentOperator.Left)) diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb index dbbfd41b1f7..ab63d8c56bd 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb @@ -12,28 +12,127 @@ Namespace Microsoft.CodeAnalysis.Operations Return If(value Is Nothing OrElse value.IsBad, New [Optional](Of Object)(), New [Optional](Of Object)(value.Value)) End Function - Private Shared Function GetAssignmentKind(value As BoundAssignmentOperator) As OperationKind + Private Shared Function GetAssignmentInfo(value As BoundAssignmentOperator) As (OperationKind As OperationKind, OperatorKind As BinaryOperatorKind, IsChecked As Boolean, InConversion As Conversion, OutConversion As Conversion, RightNode As BoundExpression, OperatorMethod As IMethodSymbol) + Dim identityConversion = New Conversion(Conversions.Identity) If value.LeftOnTheRightOpt IsNot Nothing Then Select Case value.Right.Kind Case BoundKind.BinaryOperator Dim rightBinary As BoundBinaryOperator = DirectCast(value.Right, BoundBinaryOperator) If rightBinary.Left Is value.LeftOnTheRightOpt Then - Return OperationKind.CompoundAssignment + Dim operatorKind = Helper.DeriveBinaryOperatorKind(rightBinary.OperatorKind, rightBinary.Left) + Return (OperationKind.CompoundAssignment, + operatorKind, + IsChecked:=rightBinary.Checked, + InConversion:=identityConversion, + OutConversion:=identityConversion, + RightNode:=rightBinary.Right, + OperatorMethod:=Nothing) End If Case BoundKind.UserDefinedBinaryOperator Dim rightOperatorBinary As BoundUserDefinedBinaryOperator = DirectCast(value.Right, BoundUserDefinedBinaryOperator) - - ' It is not permissible to access the Left property of a BoundUserDefinedBinaryOperator unconditionally, - ' because that property can throw an exception if the operator expression is semantically invalid. - ' get it through helper method - Dim leftOperand = GetUserDefinedBinaryOperatorChildBoundNode(rightOperatorBinary, 0) - If leftOperand Is value.LeftOnTheRightOpt Then - Return OperationKind.CompoundAssignment + Dim inConversion As Conversion = identityConversion + Dim operatorMethod As IMethodSymbol = Nothing + If IsUserDefinedBinaryOperatorCompound(rightOperatorBinary, value.LeftOnTheRightOpt, inConversion, operatorMethod) Then + Dim operatorKind = Helper.DeriveBinaryOperatorKind(rightOperatorBinary.OperatorKind, leftOpt:=Nothing) + Return (OperationKind.CompoundAssignment, + operatorKind, + IsChecked:=rightOperatorBinary.Checked, + inConversion, + OutConversion:=identityConversion, + RightNode:=GetUserDefinedBinaryOperatorChildBoundNode(rightOperatorBinary, 1), + operatorMethod) + End If + Case BoundKind.Conversion + ' In this case, we could be in a scenario like this: + ' + ' Dim x, y As New Integer + ' x /= y + ' + ' In this case, there will be a BoundConversion on top of the BoundBinaryOperator, and a BoundConversion on + ' top of the left operand to the BoundBinaryOperator. The operand of the conversion on the left and the + ' value of LeftOnTheRightOpt will be the same BoundCompoundAssignmentTargetPlaceholder + Dim rightConversion = DirectCast(value.Right, BoundConversion) + Dim outConversion = CreateConversion(rightConversion) + If rightConversion.Operand.Kind = BoundKind.BinaryOperator Then + Dim binaryOperator = DirectCast(rightConversion.Operand, BoundBinaryOperator) + + ' binaryOperator.Left must be a conversion or the TargetPlaceholder (in error cases). The Operand of the + ' Conversion will be the same BoundCompoundAssignmentTargetPlaceholder as value.LeftOnTheRightOpt + If binaryOperator.Left Is value.LeftOnTheRightOpt OrElse + (binaryOperator.Left.Kind = BoundKind.Conversion AndAlso + DirectCast(binaryOperator.Left, BoundConversion).Operand Is value.LeftOnTheRightOpt) Then + Dim operatorKind = Helper.DeriveBinaryOperatorKind(binaryOperator.OperatorKind, binaryOperator.Left) + Return (OperationKind.CompoundAssignment, + operatorKind, + IsChecked:=binaryOperator.Checked, + InConversion:=identityConversion, + outConversion, + RightNode:=binaryOperator.Right, + OperatorMethod:=Nothing) + End If + ElseIf rightConversion.Operand.Kind = BoundKind.UserDefinedConversion OrElse + rightConversion.Operand.Kind = BoundKind.UserDefinedBinaryOperator Then + + ' In error scenarios, we can have a standard conversion on top of a UserDefinedBinaryOperator + Dim rightOperatorBinary = If(TryCast(rightConversion.Operand, BoundUserDefinedBinaryOperator), + TryCast(DirectCast(rightConversion.Operand, BoundUserDefinedConversion).Operand, BoundUserDefinedBinaryOperator)) + If rightOperatorBinary IsNot Nothing Then + Dim inConversion As Conversion = identityConversion + Dim operatorMethod As IMethodSymbol = Nothing + If IsUserDefinedBinaryOperatorCompound(rightOperatorBinary, value.LeftOnTheRightOpt, inConversion, operatorMethod) Then + Dim operatorKind = Helper.DeriveBinaryOperatorKind(rightOperatorBinary.OperatorKind, leftOpt:=Nothing) + Return (OperationKind.CompoundAssignment, + operatorKind, + IsChecked:=rightOperatorBinary.Checked, + inConversion, + outConversion, + RightNode:=GetUserDefinedBinaryOperatorChildBoundNode(rightOperatorBinary, 1), + operatorMethod) + End If + End If End If End Select End If - Return OperationKind.SimpleAssignment + Return (OperationKind.SimpleAssignment, OperatorKind:=Nothing, IsChecked:=False, InConversion:=identityConversion, OutConversion:=identityConversion, RightNode:=Nothing, OperatorMethod:=Nothing) + End Function + + Private Shared Function IsUserDefinedBinaryOperatorCompound([operator] As BoundUserDefinedBinaryOperator, + leftOnTheRightOpt As BoundCompoundAssignmentTargetPlaceholder, + ByRef inConversion As Conversion, + ByRef operatorMethod As IMethodSymbol) As Boolean + + ' It is not permissible to access the Left property of a BoundUserDefinedBinaryOperator unconditionally, + ' because that property can throw an exception if the operator expression is semantically invalid. + ' get it through helper method + Dim leftOperand = GetUserDefinedBinaryOperatorChildBoundNode([operator], 0) + operatorMethod = If([operator].UnderlyingExpression.Kind = BoundKind.Call, [operator].Call.Method, Nothing) + If leftOperand Is leftOnTheRightOpt Then + Return True + ElseIf leftOperand.Kind = BoundKind.Conversion Then + ' In this case, we might have a user-defined operator with an in-conversion on the left operand + ' This can happen in the case when the user-defined operator returns the target type, but the left + ' parameter does not accept the target type and it must be converted before use. + Dim leftConversion = DirectCast(leftOperand, BoundConversion) + If leftConversion.Operand.Kind = BoundKind.UserDefinedConversion Then + Dim userDefinedConversion = DirectCast(leftConversion.Operand, BoundUserDefinedConversion) + If userDefinedConversion.Operand Is leftOnTheRightOpt Then + inConversion = CreateConversion(leftConversion) + Return True + End If + End If + End If + + Return False + End Function + + Private Shared Function CreateConversion(boundConversion As BoundConversion) As Conversion + If boundConversion.Operand.Kind = BoundKind.UserDefinedConversion Then + Dim userDefinedConversion = DirectCast(boundConversion.Operand, BoundUserDefinedConversion) + Return New Conversion(New KeyValuePair(Of ConversionKind, MethodSymbol)(boundConversion.ConversionKind, userDefinedConversion.Call.Method)) + Else + Return New Conversion(New KeyValuePair(Of ConversionKind, MethodSymbol)(boundConversion.ConversionKind, Nothing)) + End If End Function Private Function GetUserDefinedBinaryOperatorChild([operator] As BoundUserDefinedBinaryOperator, index As Integer) As IOperation @@ -45,7 +144,7 @@ Namespace Microsoft.CodeAnalysis.Operations Return OperationFactory.CreateInvalidExpression(_semanticModel, [operator].UnderlyingExpression.Syntax, ImmutableArray(Of IOperation).Empty, isImplicit) End Function - Private Shared Function GetUserDefinedBinaryOperatorChildBoundNode([operator] As BoundUserDefinedBinaryOperator, index As Integer) As BoundNode + 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 @@ -176,10 +275,10 @@ Namespace Microsoft.CodeAnalysis.Operations Return OperationFactory.CreateInvalidExpression(_semanticModel, parent.Syntax, ImmutableArray(Of IOperation).Empty, isImplicit) End Function - Private Shared Function GetChildOfBadExpressionBoundNode(parent As BoundNode, index As Integer) As BoundNode + 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 - Dim child As BoundNode = badParent.ChildBoundNodes(index) + Dim child As BoundExpression = badParent.ChildBoundNodes(index) If child IsNot Nothing Then Return child End If diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IBinaryOperatorExpression.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IBinaryOperatorExpression.vb index e91d582a99d..ab916a96fe0 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IBinaryOperatorExpression.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IBinaryOperatorExpression.vb @@ -1220,24 +1220,14 @@ IBlockOperation (12 statements, 2 locals) (OperationKind.Block, Type: null) (Syn ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x /= y') Expression: - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x /= y') + ICompoundAssignmentOperation (BinaryOperatorKind.Divide, Checked) (OperationKind.CompoundAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x /= y') Left: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, IsImplicit) (Syntax: 'x /= y') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: - IBinaryOperation (BinaryOperatorKind.Divide, Checked) (OperationKind.BinaryOperator, Type: System.Double, IsImplicit) (Syntax: 'x /= y') - Left: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: 'x') - Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'y') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x \= y') Expression: ICompoundAssignmentOperation (BinaryOperatorKind.IntegerDivide, Checked) (OperationKind.CompoundAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x \= y') @@ -1247,44 +1237,24 @@ IBlockOperation (12 statements, 2 locals) (OperationKind.Block, Type: null) (Syn ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x ^= y') Expression: - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x ^= y') + ICompoundAssignmentOperation (BinaryOperatorKind.Power, Checked) (OperationKind.CompoundAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x ^= y') Left: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, IsImplicit) (Syntax: 'x ^= y') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: - IBinaryOperation (BinaryOperatorKind.Power, Checked) (OperationKind.BinaryOperator, Type: System.Double, IsImplicit) (Syntax: 'x ^= y') - Left: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: 'x') - Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'y') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x &= y') Expression: - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x &= y') + ICompoundAssignmentOperation (BinaryOperatorKind.Concatenate, Checked) (OperationKind.CompoundAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x &= y') Left: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, IsImplicit) (Syntax: 'x &= y') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: - IBinaryOperation (BinaryOperatorKind.Concatenate, Checked) (OperationKind.BinaryOperator, Type: System.String, IsImplicit) (Syntax: 'x &= y') - Left: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: 'x') - Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, IsImplicit) (Syntax: 'y') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x <<= 2') Expression: ICompoundAssignmentOperation (BinaryOperatorKind.LeftShift, Checked) (OperationKind.CompoundAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x <<= 2') @@ -1374,24 +1344,14 @@ IBlockOperation (12 statements, 2 locals) (OperationKind.Block, Type: null) (Syn ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x /= y') Expression: - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x /= y') + ICompoundAssignmentOperation (BinaryOperatorKind.Divide) (OperationKind.CompoundAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x /= y') Left: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, IsImplicit) (Syntax: 'x /= y') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: - IBinaryOperation (BinaryOperatorKind.Divide) (OperationKind.BinaryOperator, Type: System.Double, IsImplicit) (Syntax: 'x /= y') - Left: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: 'x') - Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'y') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x \= y') Expression: ICompoundAssignmentOperation (BinaryOperatorKind.IntegerDivide) (OperationKind.CompoundAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x \= y') @@ -1401,44 +1361,24 @@ IBlockOperation (12 statements, 2 locals) (OperationKind.Block, Type: null) (Syn ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x ^= y') Expression: - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x ^= y') + ICompoundAssignmentOperation (BinaryOperatorKind.Power) (OperationKind.CompoundAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x ^= y') Left: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, IsImplicit) (Syntax: 'x ^= y') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: - IBinaryOperation (BinaryOperatorKind.Power) (OperationKind.BinaryOperator, Type: System.Double, IsImplicit) (Syntax: 'x ^= y') - Left: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: 'x') - Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, IsImplicit) (Syntax: 'y') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x &= y') Expression: - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x &= y') + ICompoundAssignmentOperation (BinaryOperatorKind.Concatenate) (OperationKind.CompoundAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x &= y') Left: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, IsImplicit) (Syntax: 'x &= y') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: - IBinaryOperation (BinaryOperatorKind.Concatenate) (OperationKind.BinaryOperator, Type: System.String, IsImplicit) (Syntax: 'x &= y') - Left: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: 'x') - Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, IsImplicit) (Syntax: 'y') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'y') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'x <<= 2') Expression: ICompoundAssignmentOperation (BinaryOperatorKind.LeftShift) (OperationKind.CompoundAssignment, Type: System.Int32, IsImplicit) (Syntax: 'x <<= 2') diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ICompoundAssignmentOperation.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ICompoundAssignmentOperation.vb new file mode 100644 index 00000000000..0167797c75a --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_ICompoundAssignmentOperation.vb @@ -0,0 +1,324 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.CodeAnalysis.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics + + Partial Public Class IOperationTests + Inherits SemanticModelTestBase + + + + Public Sub CompoundAssignmentOperation_UserDefinedOperatorInConversion() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AssignmentStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub CompoundAssignmentOperation_UserDefinedOperatorOutConversion() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AssignmentStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub CompoundAssignmentOperation_UserDefinedOperatorInOutConversion() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AssignmentStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub CompoundAssignmentExpression_BuiltInOperatorInOutConversion() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AssignmentStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub CompoundAssignmentExpression_InvalidNoOperator() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AssignmentStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub CompoundAssignmentExpression_InvalidNoOutConversion() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AssignmentStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub CompoundAssignmentExpression_InvalidNoInConversion() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AssignmentStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub CompoundAssignmentExpression_InvalidNoOperatorOrConversions() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of AssignmentStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + End Class +End Namespace -- GitLab