Skip to content

Commit

Permalink
Expose if a Binary/Unary operator was 'Lifted' at the IExpression lev…
Browse files Browse the repository at this point in the history
…el. (#14779)

* Expose if a Binary/Unary operator was 'Lifted' at the IExpression level.

* Update public API.

* Share computed value.

* Remove unnecessary extension.

* Update test code.

* Remove extension method.

* Fixup tests.

* Fixup tests.

* Fixup tests.

* Fix instances of Invalid operations with nullable

* Compound assignments also need to state if they're lifted.

* Move tests.

* Move tests.

* Move tests.

* Simplify comment.

* Case things consistently.

* Use F instead of Foo.  Because Foo is bad.
  • Loading branch information
CyrusNajmabadi authored Aug 1, 2017
1 parent fdb83fa commit e63e0d4
Show file tree
Hide file tree
Showing 20 changed files with 1,037 additions and 140 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ private ICompoundAssignmentExpression CreateBoundCompoundAssignmentOperatorOpera
SyntaxNode syntax = boundCompoundAssignmentOperator.Syntax;
ITypeSymbol type = boundCompoundAssignmentOperator.Type;
Optional<object> constantValue = ConvertToOptional(boundCompoundAssignmentOperator.ConstantValue);
return new LazyCompoundAssignmentExpression(binaryOperationKind, target, value, usesOperatorMethod, operatorMethod, syntax, type, constantValue);
return new LazyCompoundAssignmentExpression(binaryOperationKind, boundCompoundAssignmentOperator.Type.IsNullableType(), target, value, usesOperatorMethod, operatorMethod, syntax, type, constantValue);
}

private IIncrementExpression CreateBoundIncrementOperatorOperation(BoundIncrementOperator boundIncrementOperator)
Expand Down Expand Up @@ -783,7 +783,8 @@ private IUnaryOperatorExpression CreateBoundUnaryOperatorOperation(BoundUnaryOpe
SyntaxNode syntax = boundUnaryOperator.Syntax;
ITypeSymbol type = boundUnaryOperator.Type;
Optional<object> constantValue = ConvertToOptional(boundUnaryOperator.ConstantValue);
return new LazyUnaryOperatorExpression(unaryOperationKind, operand, usesOperatorMethod, operatorMethod, syntax, type, constantValue);
bool isLifted = boundUnaryOperator.OperatorKind.IsLifted();
return new LazyUnaryOperatorExpression(unaryOperationKind, operand, usesOperatorMethod, operatorMethod, syntax, type, constantValue, isLifted);
}

private IBinaryOperatorExpression CreateBoundBinaryOperatorOperation(BoundBinaryOperator boundBinaryOperator)
Expand All @@ -796,7 +797,8 @@ private IBinaryOperatorExpression CreateBoundBinaryOperatorOperation(BoundBinary
SyntaxNode syntax = boundBinaryOperator.Syntax;
ITypeSymbol type = boundBinaryOperator.Type;
Optional<object> constantValue = ConvertToOptional(boundBinaryOperator.ConstantValue);
return new LazyBinaryOperatorExpression(binaryOperationKind, leftOperand, rightOperand, usesOperatorMethod, operatorMethod, syntax, type, constantValue);
bool isLifted = boundBinaryOperator.OperatorKind.IsLifted();
return new LazyBinaryOperatorExpression(binaryOperationKind, leftOperand, rightOperand, usesOperatorMethod, operatorMethod, syntax, type, constantValue, isLifted);
}

private IConditionalChoiceExpression CreateBoundConditionalOperatorOperation(BoundConditionalOperator boundConditionalOperator)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public partial class IOperationTests : SemanticModelTestBase
{
[Fact]
public void VerifyLiftedBinaryOperators1()
{
var source = @"
class C
{
void F(int? x, int? y)
{
var z = /*<bind>*/x + y/*</bind>*/;
}
}";

string expectedOperationTree =
@"IBinaryOperatorExpression (BinaryOperationKind.IntegerAdd-IsLifted) (OperationKind.BinaryOperatorExpression, Type: System.Int32?) (Syntax: 'x + y')
Left: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32?) (Syntax: 'x')
Right: IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32?) (Syntax: 'y')";

VerifyOperationTreeForTest<BinaryExpressionSyntax>(source, expectedOperationTree);
}

[Fact]
public void VerifyNonLiftedBinaryOperators1()
{
var source = @"
class C
{
void F(int x, int y)
{
var z = /*<bind>*/x + y/*</bind>*/;
}
}";

string expectedOperationTree =
@"IBinaryOperatorExpression (BinaryOperationKind.IntegerAdd) (OperationKind.BinaryOperatorExpression, Type: System.Int32) (Syntax: 'x + y')
Left: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x')
Right: IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y')";

VerifyOperationTreeForTest<BinaryExpressionSyntax>(source, expectedOperationTree);
}

[Fact]
public void VerifyLiftedUserDefinedBinaryOperators1()
{
var source = @"
struct C
{
public static C operator +(C c1, C c2) { }
void F(C? x, C? y)
{
var z = /*<bind>*/x + y/*</bind>*/;
}
}";

string expectedOperationTree =
@"IBinaryOperatorExpression (BinaryOperationKind.OperatorMethodAdd-IsLifted) (OperatorMethod: C C.op_Addition(C c1, C c2)) (OperationKind.BinaryOperatorExpression, Type: C?) (Syntax: 'x + y')
Left: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: C?) (Syntax: 'x')
Right: IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: C?) (Syntax: 'y')";

VerifyOperationTreeForTest<BinaryExpressionSyntax>(source, expectedOperationTree);
}

[Fact]
public void VerifyNonLiftedUserDefinedBinaryOperators1()
{
var source = @"
struct C
{
public static C operator +(C c1, C c2) { }
void F(C x, C y)
{
var z = /*<bind>*/x + y/*</bind>*/;
}
}";

string expectedOperationTree =
@"IBinaryOperatorExpression (BinaryOperationKind.OperatorMethodAdd) (OperatorMethod: C C.op_Addition(C c1, C c2)) (OperationKind.BinaryOperatorExpression, Type: C) (Syntax: 'x + y')
Left: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: C) (Syntax: 'x')
Right: IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: C) (Syntax: 'y')";

VerifyOperationTreeForTest<BinaryExpressionSyntax>(source, expectedOperationTree);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ public void IForEachLoopStatement_ModifyIterationVariable()
string source = @"
class C
{
void Foo(int[] a)
void F(int[] a)
{
/*<bind>*/foreach (int x in a) { x++; }/*</bind>*/
}
Expand All @@ -538,7 +538,7 @@ public void IForEachLoopStatement_Pattern()
string source = @"
class C
{
void Foo(Enumerable e)
void F(Enumerable e)
{
/*<bind>*/foreach (long x in e) { }/*</bind>*/
}
Expand Down Expand Up @@ -571,7 +571,7 @@ public void IForEachLoopStatement_ImplicitlyTypedString()
string source = @"
class C
{
void Foo(string s)
void F(string s)
{
/*<bind>*/foreach (var x in s) { }/*</bind>*/
}
Expand All @@ -592,7 +592,7 @@ public void IForEachLoopStatement_ExplicitlyTypedVar()
string source = @"
class C
{
void Foo(var[] a)
void F(var[] a)
{
/*<bind>*/foreach (var x in a) { }/*</bind>*/
}
Expand All @@ -615,7 +615,7 @@ public void IForEachLoopStatement_DynamicEnumerable()
string source = @"
class C
{
void Foo(dynamic d)
void F(dynamic d)
{
/*<bind>*/foreach (int x in d) { }/*</bind>*/
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1155,52 +1155,51 @@ class C
{
static void Main(string[] args)
{
/*<bind>*/for (Foo f = new Foo { i = 0, s = ""abc"" }; f.i < 5; f.i = f.i + 1)
/*<bind>*/for (F f = new F { i = 0, s = ""abc"" }; f.i < 5; f.i = f.i + 1)
{
}/*</bind>*/
}
}
public class Foo
public class F
{
public int i;
public string s;
}
";
string expectedOperationTree = @"
IForLoopStatement (LoopKind.For) (OperationKind.LoopStatement) (Syntax: 'for (Foo f ... }')
IForLoopStatement (LoopKind.For) (OperationKind.LoopStatement) (Syntax: 'for (F f = ... }')
Condition: IBinaryOperatorExpression (BinaryOperationKind.IntegerLessThan) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 'f.i < 5')
Left: IFieldReferenceExpression: System.Int32 Foo.i (OperationKind.FieldReferenceExpression, Type: System.Int32) (Syntax: 'f.i')
Instance Receiver: ILocalReferenceExpression: f (OperationKind.LocalReferenceExpression, Type: Foo) (Syntax: 'f')
Left: IFieldReferenceExpression: System.Int32 F.i (OperationKind.FieldReferenceExpression, Type: System.Int32) (Syntax: 'f.i')
Instance Receiver: ILocalReferenceExpression: f (OperationKind.LocalReferenceExpression, Type: F) (Syntax: 'f')
Right: ILiteralExpression (Text: 5) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 5) (Syntax: '5')
Locals: Local_1: Foo f
Locals: Local_1: F f
Before:
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'f = new Foo ... s = ""abc"" }')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'f = new Foo ... s = ""abc"" }')
Variables: Local_1: Foo f
Initializer: IObjectCreationExpression (Constructor: Foo..ctor()) (OperationKind.ObjectCreationExpression, Type: Foo) (Syntax: 'new Foo { i ... s = ""abc"" }')
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'f = new F { ... s = ""abc"" }')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'f = new F { ... s = ""abc"" }')
Variables: Local_1: F f
Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F { i = ... s = ""abc"" }')
Arguments(0)
Initializer: IObjectOrCollectionInitializerExpression (OperationKind.ObjectOrCollectionInitializerExpression, Type: Foo) (Syntax: '{ i = 0, s = ""abc"" }')
Initializer: IObjectOrCollectionInitializerExpression (OperationKind.ObjectOrCollectionInitializerExpression, Type: F) (Syntax: '{ i = 0, s = ""abc"" }')
Initializers(2):
ISimpleAssignmentExpression (OperationKind.SimpleAssignmentExpression, Type: System.Int32) (Syntax: 'i = 0')
Left: IFieldReferenceExpression: System.Int32 Foo.i (OperationKind.FieldReferenceExpression, Type: System.Int32) (Syntax: 'i')
Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: Foo) (Syntax: 'i')
Left: IFieldReferenceExpression: System.Int32 F.i (OperationKind.FieldReferenceExpression, Type: System.Int32) (Syntax: 'i')
Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: F) (Syntax: 'i')
Right: ILiteralExpression (Text: 0) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 0) (Syntax: '0')
ISimpleAssignmentExpression (OperationKind.SimpleAssignmentExpression, Type: System.String) (Syntax: 's = ""abc""')
Left: IFieldReferenceExpression: System.String Foo.s (OperationKind.FieldReferenceExpression, Type: System.String) (Syntax: 's')
Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: Foo) (Syntax: 's')
Left: IFieldReferenceExpression: System.String F.s (OperationKind.FieldReferenceExpression, Type: System.String) (Syntax: 's')
Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: F) (Syntax: 's')
Right: ILiteralExpression (OperationKind.LiteralExpression, Type: System.String, Constant: ""abc"") (Syntax: '""abc""')
AtLoopBottom:
IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'f.i = f.i + 1')
Expression: ISimpleAssignmentExpression (OperationKind.SimpleAssignmentExpression, Type: System.Int32) (Syntax: 'f.i = f.i + 1')
Left: IFieldReferenceExpression: System.Int32 Foo.i (OperationKind.FieldReferenceExpression, Type: System.Int32) (Syntax: 'f.i')
Instance Receiver: ILocalReferenceExpression: f (OperationKind.LocalReferenceExpression, Type: Foo) (Syntax: 'f')
Left: IFieldReferenceExpression: System.Int32 F.i (OperationKind.FieldReferenceExpression, Type: System.Int32) (Syntax: 'f.i')
Instance Receiver: ILocalReferenceExpression: f (OperationKind.LocalReferenceExpression, Type: F) (Syntax: 'f')
Right: IBinaryOperatorExpression (BinaryOperationKind.IntegerAdd) (OperationKind.BinaryOperatorExpression, Type: System.Int32) (Syntax: 'f.i + 1')
Left: IFieldReferenceExpression: System.Int32 Foo.i (OperationKind.FieldReferenceExpression, Type: System.Int32) (Syntax: 'f.i')
Instance Receiver: ILocalReferenceExpression: f (OperationKind.LocalReferenceExpression, Type: Foo) (Syntax: 'f')
Left: IFieldReferenceExpression: System.Int32 F.i (OperationKind.FieldReferenceExpression, Type: System.Int32) (Syntax: 'f.i')
Instance Receiver: ILocalReferenceExpression: f (OperationKind.LocalReferenceExpression, Type: F) (Syntax: 'f')
Right: ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1')
Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }')
";
Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }')";
VerifyOperationTreeForTest<ForStatementSyntax>(source, expectedOperationTree);
}

Expand Down Expand Up @@ -1265,8 +1264,8 @@ Type Arguments(0)
IDynamicMemberReferenceExpression (Member Name: ""Next"", Containing Type: null) (OperationKind.DynamicMemberReferenceExpression, Type: dynamic) (Syntax: 'd.Next')
Type Arguments(0)
Instance Receiver: ILocalReferenceExpression: d (OperationKind.LocalReferenceExpression, Type: dynamic) (Syntax: 'd')
Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }')
";
Body: IBlockStatement (0 statements) (OperationKind.BlockStatement) (Syntax: '{ ... }')";

VerifyOperationTreeForTest<ForStatementSyntax>(source, expectedOperationTree);
}

Expand Down
Loading

0 comments on commit e63e0d4

Please sign in to comment.