// Copyright (c) AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; using NUnit.Framework; namespace ICSharpCode.NRefactory.CSharp.Resolver { // assign short name to the fake reflection type using dynamic = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.Dynamic; [TestFixture] public unsafe class BinaryOperatorTests : ResolverTestBase { CSharpResolver resolver; public override void SetUp() { base.SetUp(); resolver = new CSharpResolver(compilation); } [Test] public void Multiplication() { TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Multiply, MakeResult(typeof(int)), Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(int)); TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Multiply, MakeConstant(0.0f), Conversion.ImplicitNumericConversion, Conversion.IdentityConversion, typeof(float)); AssertConstant(3.0f, resolver.ResolveBinaryOperator( BinaryOperatorType.Multiply, MakeConstant(1.5f), MakeConstant(2))); AssertConstant(6, resolver.ResolveBinaryOperator( BinaryOperatorType.Multiply, MakeConstant((byte)2), MakeConstant((byte)3))); TestOperator(MakeResult(typeof(uint?)), BinaryOperatorType.Multiply, MakeResult(typeof(int?)), Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(long?)); AssertError(typeof(decimal), resolver.ResolveBinaryOperator( BinaryOperatorType.Multiply, MakeResult(typeof(float)), MakeResult(typeof(decimal)))); } [Test] public void Addition() { TestOperator(MakeResult(typeof(short)), BinaryOperatorType.Add, MakeResult(typeof(byte?)), Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(int?)); AssertConstant(3.0, resolver.ResolveBinaryOperator( BinaryOperatorType.Add, MakeConstant(1.0f), MakeConstant(2.0))); AssertConstant("Text", resolver.ResolveBinaryOperator( BinaryOperatorType.Add, MakeConstant("Te"), MakeConstant("xt"))); AssertConstant("", resolver.ResolveBinaryOperator( BinaryOperatorType.Add, MakeConstant(null), resolver.ResolveCast(ResolveType(typeof(string)), MakeConstant(null)))); AssertError(typeof(ReflectionHelper.Null), resolver.ResolveBinaryOperator( BinaryOperatorType.Add, MakeConstant(null), MakeConstant(null))); TestOperator(MakeResult(typeof(int?)), BinaryOperatorType.Add, MakeResult(typeof(uint?)), Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(long?)); TestOperator(MakeResult(typeof(ushort?)), BinaryOperatorType.Add, MakeResult(typeof(ushort?)), Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(int?)); TestOperator(MakeConstant(1), BinaryOperatorType.Add, MakeConstant(null), Conversion.ImplicitNullableConversion, Conversion.NullLiteralConversion, typeof(int?)); } [Test] public void StringPlusNull() { ResolveResult left = MakeResult(typeof(string)); var rr = (OperatorResolveResult)resolver.ResolveBinaryOperator( BinaryOperatorType.Add, left, MakeConstant(null)); AssertType(typeof(string), rr); Assert.AreSame(left, rr.Operands[0]); Assert.AreEqual("System.String", rr.Operands[1].Type.FullName); Assert.IsTrue(rr.Operands[1].IsCompileTimeConstant); Assert.IsNull(rr.Operands[1].ConstantValue); } [Test] public void DelegateAddition() { TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Add, MakeResult(typeof(Action)), Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(Action)); TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Add, MakeResult(typeof(Action)), Conversion.ImplicitReferenceConversion, Conversion.IdentityConversion, typeof(Action)); TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Add, MakeResult(typeof(Action)), Conversion.IdentityConversion, Conversion.ImplicitReferenceConversion, typeof(Action)); Assert.IsTrue(resolver.ResolveBinaryOperator( BinaryOperatorType.Add, MakeResult(typeof(Action)), MakeResult(typeof(Action))).IsError); } [Test] public void EnumAddition() { AssertConstant(StringComparison.Ordinal, resolver.ResolveBinaryOperator( BinaryOperatorType.Add, MakeConstant(StringComparison.InvariantCulture), MakeConstant(2))); AssertConstant(StringComparison.OrdinalIgnoreCase, resolver.ResolveBinaryOperator( BinaryOperatorType.Add, MakeConstant((short)3), MakeConstant(StringComparison.InvariantCulture))); TestOperator(MakeResult(typeof(StringComparison?)), BinaryOperatorType.Add, MakeResult(typeof(int)), Conversion.IdentityConversion, Conversion.ImplicitNullableConversion, typeof(StringComparison?)); TestOperator(MakeResult(typeof(StringComparison?)), BinaryOperatorType.Add, MakeResult(typeof(int?)), Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(StringComparison?)); TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Add, MakeResult(typeof(StringComparison?)), Conversion.ImplicitNullableConversion, Conversion.IdentityConversion, typeof(StringComparison?)); TestOperator(MakeResult(typeof(int?)), BinaryOperatorType.Add, MakeResult(typeof(StringComparison?)), Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(StringComparison?)); } [Test] public void PointerAddition() { TestOperator(MakeResult(typeof(int*)), BinaryOperatorType.Add, MakeConstant(1), Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(int*)); TestOperator(MakeResult(typeof(long)), BinaryOperatorType.Add, MakeResult(typeof(byte*)), Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(byte*)); } [Test] public void AdditionWithOverflow() { AssertConstant(int.MinValue, resolver.WithCheckForOverflow(false).ResolveBinaryOperator( BinaryOperatorType.Add, MakeConstant(int.MaxValue), MakeConstant(1))); AssertError(typeof(int), resolver.WithCheckForOverflow(true).ResolveBinaryOperator( BinaryOperatorType.Add, MakeConstant(int.MaxValue), MakeConstant(1))); } [Test] public void Subtraction() { TestOperator(MakeResult(typeof(short)), BinaryOperatorType.Subtract, MakeResult(typeof(byte?)), Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(int?)); TestOperator(MakeResult(typeof(float)), BinaryOperatorType.Subtract, MakeResult(typeof(long)), Conversion.IdentityConversion, Conversion.ImplicitNumericConversion, typeof(float)); AssertConstant(-1.0, resolver.ResolveBinaryOperator( BinaryOperatorType.Subtract, MakeConstant(1.0f), MakeConstant(2.0))); Assert.IsTrue(resolver.ResolveBinaryOperator( BinaryOperatorType.Subtract, MakeConstant("Te"), MakeConstant("xt")).IsError); } [Test] public void EnumSubtraction() { AssertConstant(StringComparison.InvariantCulture, resolver.ResolveBinaryOperator( BinaryOperatorType.Subtract, MakeConstant(StringComparison.Ordinal), MakeConstant(2))); AssertConstant(3, resolver.ResolveBinaryOperator( BinaryOperatorType.Subtract, MakeConstant(StringComparison.OrdinalIgnoreCase), MakeConstant(StringComparison.InvariantCulture))); TestOperator(MakeResult(typeof(StringComparison?)), BinaryOperatorType.Subtract, MakeResult(typeof(int)), Conversion.IdentityConversion, Conversion.ImplicitNullableConversion, typeof(StringComparison?)); TestOperator(MakeResult(typeof(StringComparison?)), BinaryOperatorType.Subtract, MakeResult(typeof(StringComparison)), Conversion.IdentityConversion, Conversion.ImplicitNullableConversion, typeof(int?)); Assert.IsTrue(resolver.ResolveBinaryOperator( BinaryOperatorType.Subtract, MakeResult(typeof(int?)), MakeResult(typeof(StringComparison))).IsError); } [Test] public void DelegateSubtraction() { TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Subtract, MakeResult(typeof(Action)), Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(Action)); TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Subtract, MakeResult(typeof(Action)), Conversion.ImplicitReferenceConversion, Conversion.IdentityConversion, typeof(Action)); TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Subtract, MakeResult(typeof(Action)), Conversion.IdentityConversion, Conversion.ImplicitReferenceConversion, typeof(Action)); Assert.IsTrue(resolver.ResolveBinaryOperator( BinaryOperatorType.Subtract, MakeResult(typeof(Action)), MakeResult(typeof(Action))).IsError); } [Test] public void PointerSubtraction() { TestOperator(MakeResult(typeof(int*)), BinaryOperatorType.Subtract, MakeConstant(1), Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(int*)); TestOperator(MakeResult(typeof(byte*)), BinaryOperatorType.Subtract, MakeResult(typeof(uint)), Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(byte*)); TestOperator(MakeResult(typeof(byte*)), BinaryOperatorType.Subtract, MakeResult(typeof(short)), Conversion.IdentityConversion, Conversion.ImplicitNumericConversion, typeof(byte*)); TestOperator(MakeResult(typeof(byte*)), BinaryOperatorType.Subtract, MakeResult(typeof(byte*)), Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(long)); AssertError(typeof(long), resolver.ResolveBinaryOperator(BinaryOperatorType.Subtract, MakeResult(typeof(byte*)), MakeResult(typeof(int*)))); } [Test] public void ShiftTest() { AssertConstant(6, resolver.ResolveBinaryOperator( BinaryOperatorType.ShiftLeft, MakeConstant(3), MakeConstant(1))); AssertConstant(ulong.MaxValue >> 2, resolver.ResolveBinaryOperator( BinaryOperatorType.ShiftRight, MakeConstant(ulong.MaxValue), MakeConstant(2))); TestOperator(MakeResult(typeof(ushort?)), BinaryOperatorType.ShiftLeft, MakeConstant(1), Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(int?)); TestOperator(MakeConstant(null), BinaryOperatorType.ShiftLeft, MakeConstant(1), Conversion.NullLiteralConversion, Conversion.ImplicitNullableConversion, typeof(int?)); TestOperator(MakeResult(typeof(long)), BinaryOperatorType.ShiftLeft, MakeConstant(null), Conversion.ImplicitNullableConversion, Conversion.NullLiteralConversion, typeof(long?)); TestOperator(MakeConstant(null), BinaryOperatorType.ShiftLeft, MakeConstant(null), Conversion.NullLiteralConversion, Conversion.NullLiteralConversion, typeof(int?)); } [Test] public void ConstantEquality() { AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(3), MakeConstant(3))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(3), MakeConstant(3.0))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(2.9), MakeConstant(3))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(double.NaN), MakeConstant(double.NaN))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(float.NaN), MakeConstant(float.NaN))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant("A"), MakeConstant("B"))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant("A"), MakeConstant("A"))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(""), MakeConstant(null))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(null), MakeConstant(null))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(1), MakeConstant(null))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(null), MakeConstant('a'))); } [Test] public void Equality() { TestOperator(MakeResult(typeof(int*)), BinaryOperatorType.Equality, MakeResult(typeof(uint*)), Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(bool)); TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Equality, MakeResult(typeof(int?)), Conversion.ImplicitNullableConversion, Conversion.IdentityConversion, typeof(bool)); TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Equality, MakeResult(typeof(float)), Conversion.ImplicitNumericConversion, Conversion.IdentityConversion, typeof(bool)); AssertType(typeof(bool), resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeResult(typeof(int)), MakeConstant(null))); AssertError(typeof(bool), resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeResult(typeof(int)), MakeResult(typeof(string)))); AssertError(typeof(bool), resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeResult(typeof(int)), MakeResult(typeof(object)))); } [Test] public void Inequality() { AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant(3), MakeConstant(3))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant(3), MakeConstant(3.0))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant(2.9), MakeConstant(3))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant(double.NaN), MakeConstant(double.NaN))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant(float.NaN), MakeConstant(double.NaN))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant(float.NaN), MakeConstant(float.NaN))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant("A"), MakeConstant("B"))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant("A"), MakeConstant("A"))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant(""), MakeConstant(null))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant(null), MakeConstant(null))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant(1), MakeConstant(null))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeConstant(null), MakeConstant('a'))); AssertType(typeof(bool), resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeResult(typeof(int*)), MakeResult(typeof(uint*)))); AssertType(typeof(bool), resolver.ResolveBinaryOperator( BinaryOperatorType.InEquality, MakeResult(typeof(bool?)), MakeConstant(null))); } [Test] public void EqualityEnum() { AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(0), MakeConstant(StringComparison.Ordinal))); AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(0), MakeConstant(StringComparison.CurrentCulture))); Assert.IsFalse(resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(StringComparison.Ordinal), MakeConstant(1)).IsCompileTimeConstant); } [Test] public void RelationalOperators() { AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.LessThan, MakeConstant(0), MakeConstant(0))); AssertType(typeof(bool), resolver.ResolveBinaryOperator( BinaryOperatorType.LessThan, MakeResult(typeof(int*)), MakeResult(typeof(uint*)))); TestOperator(MakeResult(typeof(int?)), BinaryOperatorType.LessThan, MakeResult(typeof(int)), Conversion.IdentityConversion, Conversion.ImplicitNullableConversion, typeof(bool)); } [Test] public void RelationalEnum() { AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.LessThan, MakeConstant(0), MakeConstant(StringComparison.Ordinal))); AssertError(typeof(bool), resolver.ResolveBinaryOperator( BinaryOperatorType.LessThanOrEqual, MakeConstant(1), MakeConstant(StringComparison.Ordinal))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.GreaterThan, MakeConstant(StringComparison.CurrentCultureIgnoreCase), MakeConstant(StringComparison.Ordinal))); AssertType(typeof(bool), resolver.ResolveBinaryOperator( BinaryOperatorType.GreaterThan, MakeResult(typeof(StringComparison?)), MakeResult(typeof(StringComparison?)))); } [Test] public void BitAnd() { AssertConstant(5, resolver.ResolveBinaryOperator( BinaryOperatorType.BitwiseAnd, MakeConstant(7), MakeConstant(13))); AssertType(typeof(int?), resolver.ResolveBinaryOperator( BinaryOperatorType.BitwiseAnd, MakeConstant(null), MakeConstant((short)13))); AssertType(typeof(long?), resolver.ResolveBinaryOperator( BinaryOperatorType.BitwiseAnd, MakeResult(typeof(uint?)), MakeConstant((short)13))); AssertType(typeof(uint?), resolver.ResolveBinaryOperator( BinaryOperatorType.BitwiseAnd, MakeResult(typeof(uint?)), MakeConstant((int)13))); AssertType(typeof(ulong?), resolver.ResolveBinaryOperator( BinaryOperatorType.BitwiseAnd, MakeResult(typeof(ulong?)), MakeConstant((long)13))); Assert.IsTrue(resolver.ResolveBinaryOperator( BinaryOperatorType.BitwiseAnd, MakeResult(typeof(ulong?)), MakeConstant((short)13)).IsError); } [Test] public void BitXor() { AssertConstant(6L ^ 3, resolver.ResolveBinaryOperator( BinaryOperatorType.ExclusiveOr, MakeConstant(6L), MakeConstant(3))); AssertConstant(6UL ^ 3L, resolver.ResolveBinaryOperator( BinaryOperatorType.ExclusiveOr, MakeConstant(6UL), MakeConstant(3L))); AssertError(typeof(ulong), resolver.ResolveBinaryOperator( BinaryOperatorType.ExclusiveOr, MakeConstant(6UL), MakeConstant(-3L))); } [Test] public void BitwiseEnum() { AssertConstant(AttributeTargets.Field | AttributeTargets.Property, resolver.ResolveBinaryOperator( BinaryOperatorType.BitwiseOr, MakeConstant(AttributeTargets.Field), MakeConstant(AttributeTargets.Property))); AssertConstant(AttributeTargets.Field & AttributeTargets.All, resolver.ResolveBinaryOperator( BinaryOperatorType.BitwiseAnd, MakeConstant(AttributeTargets.Field), MakeConstant(AttributeTargets.All))); AssertConstant(AttributeTargets.Field & 0, resolver.ResolveBinaryOperator( BinaryOperatorType.BitwiseAnd, MakeConstant(AttributeTargets.Field), MakeConstant(0))); AssertConstant(0 | AttributeTargets.Field, resolver.ResolveBinaryOperator( BinaryOperatorType.BitwiseOr, MakeConstant(0), MakeConstant(AttributeTargets.Field))); } [Test] public void LogicalAnd() { AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.ConditionalAnd, MakeConstant(true), MakeConstant(true))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.ConditionalAnd, MakeConstant(false), MakeConstant(true))); AssertError(typeof(bool), resolver.ResolveBinaryOperator( BinaryOperatorType.ConditionalAnd, MakeConstant(false), MakeResult(typeof(bool?)))); } [Test] public void LogicalOr() { AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.ConditionalOr, MakeConstant(false), MakeConstant(true))); AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.ConditionalOr, MakeConstant(false), MakeConstant(false))); AssertError(typeof(bool), resolver.ResolveBinaryOperator( BinaryOperatorType.ConditionalOr, MakeConstant(false), MakeResult(typeof(bool?)))); } [Test] public void NullCoalescing() { AssertType(typeof(int), resolver.ResolveBinaryOperator( BinaryOperatorType.NullCoalescing, MakeResult(typeof(int?)), MakeResult(typeof(short)))); AssertType(typeof(int?), resolver.ResolveBinaryOperator( BinaryOperatorType.NullCoalescing, MakeResult(typeof(int?)), MakeResult(typeof(short?)))); AssertType(typeof(object), resolver.ResolveBinaryOperator( BinaryOperatorType.NullCoalescing, MakeResult(typeof(string)), MakeResult(typeof(object)))); AssertError(typeof(string), resolver.ResolveBinaryOperator( BinaryOperatorType.NullCoalescing, MakeResult(typeof(string)), MakeResult(typeof(int)))); AssertType(typeof(dynamic), resolver.ResolveBinaryOperator( BinaryOperatorType.NullCoalescing, MakeResult(typeof(dynamic)), MakeResult(typeof(string)))); AssertType(typeof(dynamic), resolver.ResolveBinaryOperator( BinaryOperatorType.NullCoalescing, MakeResult(typeof(string)), MakeResult(typeof(dynamic)))); } [Test] public void LiftedUserDefined() { AssertType(typeof(TimeSpan), resolver.ResolveBinaryOperator( BinaryOperatorType.Subtract, MakeResult(typeof(DateTime)), MakeResult(typeof(DateTime)))); AssertType(typeof(TimeSpan?), resolver.ResolveBinaryOperator( BinaryOperatorType.Subtract, MakeResult(typeof(DateTime?)), MakeResult(typeof(DateTime)))); AssertType(typeof(TimeSpan?), resolver.ResolveBinaryOperator( BinaryOperatorType.Subtract, MakeResult(typeof(DateTime)), MakeResult(typeof(DateTime?)))); AssertType(typeof(TimeSpan?), resolver.ResolveBinaryOperator( BinaryOperatorType.Subtract, MakeResult(typeof(DateTime?)), MakeResult(typeof(DateTime?)))); } [Test] public void UserDefinedNeedsLiftingDueToImplicitConversion() { string program = @"struct S {} struct A { public static implicit operator S?(A a) { return null; } public static S operator +(A a, S s) { return s; } } class Test { void M(A a) { var s = $a + a$; } } "; var irr = Resolve(program); Assert.IsFalse(irr.IsError); Assert.IsTrue(irr.IsLiftedOperator); Assert.IsTrue(irr.UserDefinedOperatorMethod is OverloadResolution.ILiftedOperator); Assert.AreEqual("A.op_Addition", irr.UserDefinedOperatorMethod.FullName); Assert.AreEqual("System.Nullable`1[[S]]", irr.Type.ReflectionName); Assert.AreEqual("System.Nullable`1[[S]]", irr.UserDefinedOperatorMethod.ReturnType.ReflectionName); Conversion lhsConv = ((ConversionResolveResult)irr.Operands[0]).Conversion; Conversion rhsConv = ((ConversionResolveResult)irr.Operands[1]).Conversion; Assert.AreEqual(Conversion.ImplicitNullableConversion, lhsConv); Assert.IsTrue(rhsConv.IsUserDefined); Assert.AreEqual("A.op_Implicit", rhsConv.Method.FullName); } [Test] public void ThereAreNoLiftedOperatorsForClasses() { string program = @"struct S {} class A { public static implicit operator S?(A a) { return null; } public static S operator +(A a, S s) { return s; } } class Test { void M(A a) { var s = $a + a$; } } "; var irr = Resolve(program); Assert.IsTrue(irr.IsError); // cannot convert from A to S Assert.AreEqual("A.op_Addition", irr.Member.FullName); Assert.AreEqual("S", irr.Type.ReflectionName); } [Test] public void CompoundAssign_String_Char() { string program = @" class Test { string text; void Append(char c) { $text += c$; } }"; var irr = Resolve(program); Assert.IsFalse(irr.IsError); Assert.AreEqual(System.Linq.Expressions.ExpressionType.AddAssign, irr.OperatorType); Assert.IsNull(irr.UserDefinedOperatorMethod); Assert.AreEqual("System.String", irr.Type.ReflectionName); } [Test] public void CompoundAssign_Byte_Literal1() { string program = @" class Test { byte c; void Inc() { $c += 1$; } }"; var irr = Resolve(program); Assert.IsFalse(irr.IsError); Assert.AreEqual(System.Linq.Expressions.ExpressionType.AddAssign, irr.OperatorType); Assert.IsNull(irr.UserDefinedOperatorMethod); Assert.AreEqual("System.Byte", irr.Type.ReflectionName); } [Test] public void CompareDateTimeWithNullLiteral() { string program = @"using System; class Test { static void Inc(DateTime x) { var c = $x == null$; } }"; var irr = Resolve(program); Assert.IsFalse(irr.IsError); Assert.IsTrue(irr.IsLiftedOperator); Assert.IsNotNull(irr.UserDefinedOperatorMethod); Assert.AreEqual(compilation.FindType(KnownTypeCode.Boolean), irr.Type); } [Test] public void CompareStructWithNullLiteral() { string program = @" struct X { } class Test { static void Inc(X x) { var c = $x == null$; } }"; var irr = Resolve(program); Assert.IsTrue(irr.IsError); Assert.AreEqual(compilation.FindType(KnownTypeCode.Boolean), irr.Type); } [Test] public void CompareNullableStructWithNullLiteral() { string program = @" struct X { } class Test { static void Inc(X? x) { var c = $x == null$; } }"; var irr = Resolve(program); Assert.IsFalse(irr.IsError); Assert.AreEqual(compilation.FindType(KnownTypeCode.Boolean), irr.Type); } [Test] public void CompareUnrestrictedTypeParameterWithNullLiteral() { string program = @" class Test { static void Inc(X x) { var c = $x == null$; } }"; var irr = Resolve(program); Assert.IsFalse(irr.IsError); Assert.AreEqual(compilation.FindType(KnownTypeCode.Boolean), irr.Type); } [Test] public void LiftedEqualityOperator() { string program = @" struct X { public static bool operator ==(X a, X b) {} } class Test { static void Inc(X? x) { var c = $x == x$; } }"; var irr = Resolve(program); Assert.IsFalse(irr.IsError); Assert.AreEqual(compilation.FindType(KnownTypeCode.Boolean), irr.Type); } [Test] public void IsLiftedProperty() { string program = @" class Test { static void Inc() { int? a = 0, b = 0; int? c = $a + b$; } }"; var irr = Resolve(program); Assert.IsFalse(irr.IsError); Assert.IsTrue(irr.IsLiftedOperator); } [Test] public void IsLiftedProperty2() { string program = @" class Test { static void Inc() { int? a = 0, b = 0; $b += a$; } }"; var irr = Resolve(program); Assert.IsFalse(irr.IsError); Assert.IsTrue(irr.IsLiftedOperator); } } }