diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs new file mode 100644 index 0000000000..490f1863a6 --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using ICSharpCode.NRefactory.TypeSystem; +using NUnit.Framework; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + [TestFixture] + public class BinaryOperatorTests : ResolverTestBase + { + [Test] + public void Multiplication() + { + AssertType(typeof(int), resolver.ResolveBinaryOperator( + BinaryOperatorType.Multiply, MakeResult(typeof(int)), MakeResult(typeof(int)))); + + AssertType(typeof(float), resolver.ResolveBinaryOperator( + BinaryOperatorType.Multiply, MakeResult(typeof(int)), MakeConstant(0.0f))); + + AssertConstant(3.0f, resolver.ResolveBinaryOperator( + BinaryOperatorType.Multiply, MakeConstant(1.5f), MakeConstant(2))); + + AssertConstant(6, resolver.ResolveBinaryOperator( + BinaryOperatorType.Multiply, MakeConstant((byte)2), MakeConstant((byte)3))); + + AssertType(typeof(long?), resolver.ResolveBinaryOperator( + BinaryOperatorType.Multiply, MakeResult(typeof(uint?)), MakeResult(typeof(int?)))); + + AssertError(typeof(decimal), resolver.ResolveBinaryOperator( + BinaryOperatorType.Multiply, MakeResult(typeof(float)), MakeResult(typeof(decimal)))); + } + + [Test] + public void Addition() + { + AssertType(typeof(int?), resolver.ResolveBinaryOperator( + BinaryOperatorType.Add, MakeResult(typeof(short)), MakeResult(typeof(byte?)))); + + AssertConstant(3.0, resolver.ResolveBinaryOperator( + BinaryOperatorType.Add, MakeConstant(1.0f), MakeConstant(2.0))); + + 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))); + + 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))); + + AssertType(typeof(Action), resolver.ResolveBinaryOperator( + BinaryOperatorType.Add, MakeResult(typeof(Action)), MakeResult(typeof(Action)))); + + AssertType(typeof(Action), resolver.ResolveBinaryOperator( + BinaryOperatorType.Add, MakeResult(typeof(Action)), MakeResult(typeof(Action)))); + + Assert.IsTrue(resolver.ResolveBinaryOperator( + BinaryOperatorType.Add, MakeResult(typeof(Action)), MakeResult(typeof(Action))).IsError); + + AssertType(typeof(StringComparison?), resolver.ResolveBinaryOperator( + BinaryOperatorType.Add, MakeResult(typeof(StringComparison?)), MakeResult(typeof(int)))); + + AssertType(typeof(StringComparison?), resolver.ResolveBinaryOperator( + BinaryOperatorType.Add, MakeResult(typeof(int?)), MakeResult(typeof(StringComparison)))); + } + + [Test] + public void AdditionWithOverflow() + { + resolver.IsCheckedContext = false; + AssertConstant(int.MinValue, resolver.ResolveBinaryOperator( + BinaryOperatorType.Add, MakeConstant(int.MaxValue), MakeConstant(1))); + + resolver.IsCheckedContext = true; + AssertError(typeof(int), resolver.ResolveBinaryOperator( + BinaryOperatorType.Add, MakeConstant(int.MaxValue), MakeConstant(1))); + } + } +} diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs index f01b62ad9e..582a7976f0 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs @@ -301,6 +301,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Assert.AreEqual(0, BetterConversion(typeof(byte), typeof(ushort?), typeof(int))); Assert.AreEqual(2, BetterConversion(typeof(byte), typeof(ulong?), typeof(uint))); Assert.AreEqual(0, BetterConversion(typeof(byte), typeof(ulong?), typeof(int))); + Assert.AreEqual(2, BetterConversion(typeof(ushort?), typeof(long?), typeof(int?))); } + + void Bla(X x) { + var y = x + x; + } + class X { public static implicit operator string(X x) { return null ; } } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs index 3a47ab3828..d71e9454d7 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs @@ -31,6 +31,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver protected ConstantResolveResult MakeConstant(object value) { + if (value == null) + return new ConstantResolveResult(SharedTypes.Null, null); IType type = ResolveType(value.GetType()); if (type.IsEnum()) value = Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType())); diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj index db5d369576..2b6aee8c44 100644 --- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj +++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj @@ -43,6 +43,7 @@ + diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs index 132dd3ab08..2edeed9c0a 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs @@ -34,6 +34,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } #region class OperatorMethod + static OperatorMethod[] Lift(params OperatorMethod[] methods) + { + List result = new List(methods); + foreach (OperatorMethod method in methods) { + OperatorMethod lifted = method.Lift(); + if (lifted != null) + result.Add(lifted); + } + return result.ToArray(); + } + class OperatorMethod : Immutable, IParameterizedMember { IList parameters = new List(); @@ -46,6 +57,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver get; set; } + public virtual OperatorMethod Lift() + { + return null; + } + ITypeDefinition IEntity.DeclaringTypeDefinition { get { return null; } } @@ -187,7 +203,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver // TODO: implicit support for user operators //var candidateSet = GetUnaryOperatorCandidates(); - UnaryOperatorMethod[] methodGroup; + expression = UnaryNumericPromotion(op, ref type, isNullable, expression); + OperatorMethod[] methodGroup; switch (op) { case UnaryOperatorType.Increment: case UnaryOperatorType.Decrement: @@ -238,7 +255,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver object val; try { val = m.Invoke(this, expression.ConstantValue); - } catch (OverflowException) { + } catch (ArithmeticException) { return new ErrorResolveResult(resultType); } return new ConstantResolveResult(resultType, val); @@ -247,6 +264,32 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } + ResolveResult UnaryNumericPromotion(UnaryOperatorType op, ref IType type, bool isNullable, ResolveResult expression) + { + // C# 4.0 spec: §7.3.6.1 + TypeCode code = ReflectionHelper.GetTypeCode(type); + switch (op) { + case UnaryOperatorType.Minus: + if (code == TypeCode.UInt32) { + IType targetType = context.GetClass(typeof(long)) ?? SharedTypes.UnknownType; + type = targetType; + if (isNullable) targetType = NullableType.Create(targetType, context); + return ResolveCast(targetType, expression); + } + goto case UnaryOperatorType.Plus; + case UnaryOperatorType.Plus: + case UnaryOperatorType.BitNot: + if (code >= TypeCode.Char && code <= TypeCode.UInt16) { + IType targetType = context.GetClass(typeof(int)) ?? SharedTypes.UnknownType; + type = targetType; + if (isNullable) targetType = NullableType.Create(targetType, context); + return ResolveCast(targetType, expression); + } + break; + } + return expression; + } + static string GetOverloadableOperatorName(UnaryOperatorType op) { switch (op) { @@ -289,6 +332,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { return func((T)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T)), input)); } + + public override OperatorMethod Lift() + { + return new LiftedUnaryOperatorMethod(this); + } } sealed class LiftedUnaryOperatorMethod : UnaryOperatorMethod, OverloadResolution.ILiftedOperator @@ -315,18 +363,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - static UnaryOperatorMethod[] Lift(params UnaryOperatorMethod[] methods) - { - UnaryOperatorMethod[] lifted = new UnaryOperatorMethod[methods.Length * 2]; - methods.CopyTo(lifted, 0); - for (int i = 0; i < methods.Length; i++) { - lifted[methods.Length + i] = new LiftedUnaryOperatorMethod(methods[i]); - } - return lifted; - } - // C# 4.0 spec: §7.7.1 Unary plus operator - static readonly UnaryOperatorMethod[] unaryPlusOperators = Lift( + static readonly OperatorMethod[] unaryPlusOperators = Lift( new LambdaUnaryOperatorMethod(i => +i), new LambdaUnaryOperatorMethod(i => +i), new LambdaUnaryOperatorMethod(i => +i), @@ -337,14 +375,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ); // C# 4.0 spec: §7.7.2 Unary minus operator - static readonly UnaryOperatorMethod[] uncheckedUnaryMinusOperators = Lift( + static readonly OperatorMethod[] uncheckedUnaryMinusOperators = Lift( new LambdaUnaryOperatorMethod(i => unchecked(-i)), new LambdaUnaryOperatorMethod(i => unchecked(-i)), new LambdaUnaryOperatorMethod(i => -i), new LambdaUnaryOperatorMethod(i => -i), new LambdaUnaryOperatorMethod(i => -i) ); - static readonly UnaryOperatorMethod[] checkedUnaryMinusOperators = Lift( + static readonly OperatorMethod[] checkedUnaryMinusOperators = Lift( new LambdaUnaryOperatorMethod(i => checked(-i)), new LambdaUnaryOperatorMethod(i => checked(-i)), new LambdaUnaryOperatorMethod(i => -i), @@ -353,10 +391,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ); // C# 4.0 spec: §7.7.3 Logical negation operator - static readonly UnaryOperatorMethod[] logicalNegationOperator = Lift(new LambdaUnaryOperatorMethod(b => !b)); + static readonly OperatorMethod[] logicalNegationOperator = Lift(new LambdaUnaryOperatorMethod(b => !b)); // C# 4.0 spec: §7.7.4 Bitwise complement operator - static readonly UnaryOperatorMethod[] bitwiseComplementOperators = Lift( + static readonly OperatorMethod[] bitwiseComplementOperators = Lift( new LambdaUnaryOperatorMethod(i => ~i), new LambdaUnaryOperatorMethod(i => ~i), new LambdaUnaryOperatorMethod(i => ~i), @@ -397,7 +435,118 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver IType lhsType = NullableType.GetUnderlyingType(lhs.Type); IType rhsType = NullableType.GetUnderlyingType(rhs.Type); - throw new NotImplementedException(); + // TODO: find user-defined operators + if (!BinaryNumericPromotion(isNullable, ref lhs, ref rhs)) + return new ErrorResolveResult(lhs.Type); + // re-read underlying types after numeric promotion + lhsType = NullableType.GetUnderlyingType(lhs.Type); + rhsType = NullableType.GetUnderlyingType(rhs.Type); + + IEnumerable methodGroup; + switch (op) { + case BinaryOperatorType.Multiply: + methodGroup = IsCheckedContext ? checkedMultiplicationOperators : uncheckedMultiplicationOperators; + break; + case BinaryOperatorType.Divide: + methodGroup = IsCheckedContext ? checkedDivisionOperators : uncheckedDivisionOperators; + break; + case BinaryOperatorType.Modulus: + methodGroup = IsCheckedContext ? checkedRemainderOperators : uncheckedRemainderOperators; + break; + case BinaryOperatorType.Add: + methodGroup = IsCheckedContext ? checkedAdditionOperators : uncheckedAdditionOperators; + Conversions conversions = new Conversions(context); + if (lhsType.IsEnum() && conversions.ImplicitConversion(rhsType, lhsType.GetEnumUnderlyingType(context))) { + // E operator +(E x, U y); + if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable) { + // evaluate as (E)((U)x + (U)y) + lhs = ResolveCast(lhsType.GetEnumUnderlyingType(context), lhs); + if (lhs.IsError) return lhs; + rhs = ResolveCast(lhsType.GetEnumUnderlyingType(context), rhs); + if (rhs.IsError) return rhs; + return CheckErrorAndResolveCast(lhsType, ResolveBinaryOperator(op, lhs, rhs)); + } + return new ResolveResult(isNullable ? NullableType.Create(lhsType, context) : lhsType); + } else if (rhsType.IsEnum() && conversions.ImplicitConversion(lhsType, rhsType.GetEnumUnderlyingType(context))) { + // E operator +(U x, E y); + return ResolveBinaryOperator(op, rhs, lhs); // swap arguments + } + if (lhsType.IsDelegate() && conversions.ImplicitConversion(rhsType, lhsType)) { + return new ResolveResult(lhsType); + } else if (rhsType.IsDelegate() && conversions.ImplicitConversion(lhsType, rhsType)) { + return new ResolveResult(rhsType); + } + if (lhsType == SharedTypes.Null && rhsType == SharedTypes.Null) + return new ErrorResolveResult(SharedTypes.Null); + break; + default: + throw new InvalidOperationException(); + } + OverloadResolution r = new OverloadResolution(context, new[] { lhs, rhs }); + foreach (var candidate in methodGroup) { + r.AddCandidate(candidate); + } + BinaryOperatorMethod m = (BinaryOperatorMethod)r.BestCandidate; + IType resultType = m.ReturnType.Resolve(context); + if (r.BestCandidateErrors != OverloadResolutionErrors.None) { + return new ErrorResolveResult(resultType); + } else if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable && m.CanEvaluateAtCompileTime) { + object val; + try { + val = m.Invoke(this, lhs.ConstantValue, rhs.ConstantValue); + } catch (ArithmeticException) { + return new ErrorResolveResult(resultType); + } + return new ConstantResolveResult(resultType, val); + } else { + return new ResolveResult(resultType); + } + } + + bool BinaryNumericPromotion(bool isNullable, ref ResolveResult lhs, ref ResolveResult rhs) + { + // C# 4.0 spec: §7.3.6.2 + TypeCode lhsCode = ReflectionHelper.GetTypeCode(lhs.Type); + TypeCode rhsCode = ReflectionHelper.GetTypeCode(rhs.Type); + bool bindingError = false; + if (lhsCode >= TypeCode.Char && lhsCode <= TypeCode.Decimal + && rhsCode >= TypeCode.Char && rhsCode <= TypeCode.Decimal) + { + Type targetType; + if (lhsCode == TypeCode.Decimal || rhsCode == TypeCode.Decimal) { + targetType = typeof(decimal); + bindingError = (lhsCode == TypeCode.Single || lhsCode == TypeCode.Double + || rhsCode == TypeCode.Single || rhsCode == TypeCode.Double); + } else if (lhsCode == TypeCode.Double || rhsCode == TypeCode.Double) { + targetType = typeof(double); + } else if (lhsCode == TypeCode.Single || rhsCode == TypeCode.Single) { + targetType = typeof(float); + } else if (lhsCode == TypeCode.UInt64 || rhsCode == TypeCode.UInt64) { + targetType = typeof(ulong); + bindingError = IsSigned(lhsCode) || IsSigned(rhsCode); + } else if (lhsCode == TypeCode.Int64 || rhsCode == TypeCode.Int64) { + targetType = typeof(long); + } else if (lhsCode == TypeCode.UInt32 || rhsCode == TypeCode.UInt32) { + targetType = (IsSigned(lhsCode) || IsSigned(rhsCode)) ? typeof(long) : typeof(uint); + } else { + targetType = typeof(int); + } + lhs = CastTo(targetType, isNullable, lhs); + rhs = CastTo(targetType, isNullable, rhs); + } + return !bindingError; + } + + bool IsSigned(TypeCode code) + { + return code == TypeCode.SByte || code == TypeCode.Int16 || code == TypeCode.Int32 || code == TypeCode.Int64; + } + + ResolveResult CastTo(Type targetType, bool isNullable, ResolveResult expression) + { + IType t = context.GetClass(targetType) ?? SharedTypes.UnknownType; + if (isNullable) t = NullableType.Create(t, context); + return ResolveCast(t, expression); } static string GetOverloadableOperatorName(BinaryOperatorType op) @@ -442,6 +591,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver abstract class BinaryOperatorMethod : OperatorMethod { + public virtual bool CanEvaluateAtCompileTime { get { return true; } } public abstract object Invoke(CSharpResolver resolver, object lhs, object rhs); } @@ -463,6 +613,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return func((T)resolver.CSharpPrimitiveCast(typeCode, lhs), (T)resolver.CSharpPrimitiveCast(typeCode, rhs)); } + + public override OperatorMethod Lift() + { + return new LiftedBinaryOperatorMethod(this); + } } sealed class LiftedBinaryOperatorMethod : BinaryOperatorMethod, OverloadResolution.ILiftedOperator @@ -490,14 +645,112 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - static BinaryOperatorMethod[] Lift(params BinaryOperatorMethod[] methods) + // C# 4.0 spec: §7.8.1 Multiplication operator + static readonly OperatorMethod[] checkedMultiplicationOperators = Lift( + new LambdaBinaryOperatorMethod ((a, b) => checked(a * b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a * b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a * b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a * b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a * b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a * b)), + new LambdaBinaryOperatorMethod((a, b) => checked(a * b)) + ); + static readonly OperatorMethod[] uncheckedMultiplicationOperators = Lift( + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a * b)), + new LambdaBinaryOperatorMethod((a, b) => unchecked(a * b)) + ); + + // C# 4.0 spec: §7.8.2 Division operator + static readonly OperatorMethod[] checkedDivisionOperators = Lift( + new LambdaBinaryOperatorMethod ((a, b) => checked(a / b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a / b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a / b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a / b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a / b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a / b)), + new LambdaBinaryOperatorMethod((a, b) => checked(a / b)) + ); + static readonly OperatorMethod[] uncheckedDivisionOperators = Lift( + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a / b)), + new LambdaBinaryOperatorMethod((a, b) => unchecked(a / b)) + ); + + // C# 4.0 spec: §7.8.3 Remainder operator + static readonly OperatorMethod[] checkedRemainderOperators = Lift( + new LambdaBinaryOperatorMethod ((a, b) => checked(a % b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a % b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a % b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a % b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a % b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a % b)), + new LambdaBinaryOperatorMethod((a, b) => checked(a % b)) + ); + static readonly OperatorMethod[] uncheckedRemainderOperators = Lift( + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a % b)), + new LambdaBinaryOperatorMethod((a, b) => unchecked(a % b)) + ); + + // C# 4.0 spec: §7.8.3 Addition operator + static readonly OperatorMethod[] checkedAdditionOperators = Lift( + new LambdaBinaryOperatorMethod ((a, b) => checked(a + b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a + b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a + b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a + b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a + b)), + new LambdaBinaryOperatorMethod ((a, b) => checked(a + b)), + new LambdaBinaryOperatorMethod((a, b) => checked(a + b)), + new StringConcatenation(typeof(string), typeof(string)), + new StringConcatenation(typeof(string), typeof(object)), + new StringConcatenation(typeof(object), typeof(string)) + ); + static readonly OperatorMethod[] uncheckedAdditionOperators = Lift( + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod ((a, b) => unchecked(a + b)), + new LambdaBinaryOperatorMethod((a, b) => unchecked(a + b)), + new StringConcatenation(typeof(string), typeof(string)), + new StringConcatenation(typeof(string), typeof(object)), + new StringConcatenation(typeof(object), typeof(string)) + ); + // not in this list, but handled manually: enum addition, delegate combination + sealed class StringConcatenation : BinaryOperatorMethod { - BinaryOperatorMethod[] lifted = new BinaryOperatorMethod[methods.Length * 2]; - methods.CopyTo(lifted, 0); - for (int i = 0; i < methods.Length; i++) { - lifted[methods.Length + i] = new LiftedBinaryOperatorMethod(methods[i]); + bool canEvaluateAtCompileTime; + + public StringConcatenation(Type p1, Type p2) + { + this.canEvaluateAtCompileTime = p1 == typeof(string) && p2 == typeof(string); + this.ReturnType = typeof(string).ToTypeReference(); + this.Parameters.Add(new DefaultParameter(p1.ToTypeReference(), string.Empty)); + this.Parameters.Add(new DefaultParameter(p2.ToTypeReference(), string.Empty)); + } + + public override bool CanEvaluateAtCompileTime { + get { return canEvaluateAtCompileTime; } + } + + public override object Invoke(CSharpResolver resolver, object lhs, object rhs) + { + return string.Concat(lhs, rhs); } - return lifted; } object GetUserBinaryOperatorCandidates() diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs index fe1df7393c..6cd993a83c 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs @@ -299,30 +299,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return 1; if (lift1 != null && lift2 == null) return 2; - if (lift1 != null && lift2 != null - && lift1.NonLiftedParameters.Count == arguments.Length - && lift2.NonLiftedParameters.Count == arguments.Length) - { - // for two lifted operators, compare which of the non-lifted versions is better - for (int i = 0; i < arguments.Length; i++) { - switch (conversions.BetterConversion( - arguments[i], - lift1.NonLiftedParameters[i].Type.Resolve(context), - lift2.NonLiftedParameters[i].Type.Resolve(context))) - { - case 1: - c1IsBetter = true; - break; - case 2: - c2IsBetter = true; - break; - } - } - if (c1IsBetter && !c2IsBetter) - return 1; - if (!c1IsBetter && c2IsBetter) - return 2; - } } return 0; } @@ -399,7 +375,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } else { switch (BetterFunctionMember(candidate, bestCandidate)) { case 0: - bestCandidateAmbiguousWith = candidate; + if (bestCandidateAmbiguousWith == null) + bestCandidateAmbiguousWith = candidate; break; case 1: bestCandidate = candidate; @@ -412,11 +389,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #endregion public IParameterizedMember BestCandidate { - get { return bestCandidate.Member; } + get { return bestCandidate != null ? bestCandidate.Member : null; } } public OverloadResolutionErrors BestCandidateErrors { get { + if (bestCandidate == null) + return OverloadResolutionErrors.None; OverloadResolutionErrors err = bestCandidate.Errors; if (bestCandidateAmbiguousWith != null) err |= OverloadResolutionErrors.AmbiguousMatch; @@ -425,7 +404,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } public IParameterizedMember BestCandidateAmbiguousWith { - get { return bestCandidateAmbiguousWith.Member; } + get { return bestCandidateAmbiguousWith != null ? bestCandidateAmbiguousWith.Member : null; } } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs index a55a760f3e..e8bb5c49bc 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs @@ -121,6 +121,17 @@ namespace ICSharpCode.NRefactory.TypeSystem throw new ArgumentException("enumType must be an enum"); } } + + /// + /// Gets whether the type is an delegate type. + /// + public static bool IsDelegate(this IType type) + { + if (type == null) + throw new ArgumentNullException("type"); + ITypeDefinition def = type.GetDefinition(); + return def != null && def.ClassType == ClassType.Delegate; + } #endregion } }