Browse Source

Add support for multiplication, division, modulus and addition operators.

newNRvisualizers
Daniel Grunwald 16 years ago
parent
commit
ca28cd6478
  1. 87
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs
  2. 6
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs
  3. 2
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs
  4. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  5. 301
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  6. 33
      ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs
  7. 11
      ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs

87
ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs

@ -0,0 +1,87 @@ @@ -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<string>), resolver.ResolveBinaryOperator(
BinaryOperatorType.Add, MakeResult(typeof(Action<object>)), MakeResult(typeof(Action<string>))));
Assert.IsTrue(resolver.ResolveBinaryOperator(
BinaryOperatorType.Add, MakeResult(typeof(Action<int>)), MakeResult(typeof(Action<long>))).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)));
}
}
}

6
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs

@ -301,6 +301,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -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 ; } }
}
}

2
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs

@ -31,6 +31,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -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()));

1
ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -43,6 +43,7 @@ @@ -43,6 +43,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CSharp\Resolver\BinaryOperatorTests.cs" />
<Compile Include="CSharp\Resolver\CastTests.cs" />
<Compile Include="CSharp\Resolver\ConversionsTest.cs" />
<Compile Include="CSharp\Resolver\ResolverTestBase.cs" />

301
ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs

@ -34,6 +34,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -34,6 +34,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
#region class OperatorMethod
static OperatorMethod[] Lift(params OperatorMethod[] methods)
{
List<OperatorMethod> result = new List<OperatorMethod>(methods);
foreach (OperatorMethod method in methods) {
OperatorMethod lifted = method.Lift();
if (lifted != null)
result.Add(lifted);
}
return result.ToArray();
}
class OperatorMethod : Immutable, IParameterizedMember
{
IList<IParameter> parameters = new List<IParameter>();
@ -46,6 +57,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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<int>(i => +i),
new LambdaUnaryOperatorMethod<uint>(i => +i),
new LambdaUnaryOperatorMethod<long>(i => +i),
@ -337,14 +375,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -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<int>(i => unchecked(-i)),
new LambdaUnaryOperatorMethod<long>(i => unchecked(-i)),
new LambdaUnaryOperatorMethod<float>(i => -i),
new LambdaUnaryOperatorMethod<double>(i => -i),
new LambdaUnaryOperatorMethod<decimal>(i => -i)
);
static readonly UnaryOperatorMethod[] checkedUnaryMinusOperators = Lift(
static readonly OperatorMethod[] checkedUnaryMinusOperators = Lift(
new LambdaUnaryOperatorMethod<int>(i => checked(-i)),
new LambdaUnaryOperatorMethod<long>(i => checked(-i)),
new LambdaUnaryOperatorMethod<float>(i => -i),
@ -353,10 +391,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -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<bool>(b => !b));
static readonly OperatorMethod[] logicalNegationOperator = Lift(new LambdaUnaryOperatorMethod<bool>(b => !b));
// C# 4.0 spec: §7.7.4 Bitwise complement operator
static readonly UnaryOperatorMethod[] bitwiseComplementOperators = Lift(
static readonly OperatorMethod[] bitwiseComplementOperators = Lift(
new LambdaUnaryOperatorMethod<int>(i => ~i),
new LambdaUnaryOperatorMethod<uint>(i => ~i),
new LambdaUnaryOperatorMethod<long>(i => ~i),
@ -397,7 +435,118 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -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<OperatorMethod> 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 @@ -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 @@ -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 @@ -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<int> ((a, b) => checked(a * b)),
new LambdaBinaryOperatorMethod<uint> ((a, b) => checked(a * b)),
new LambdaBinaryOperatorMethod<long> ((a, b) => checked(a * b)),
new LambdaBinaryOperatorMethod<ulong> ((a, b) => checked(a * b)),
new LambdaBinaryOperatorMethod<float> ((a, b) => checked(a * b)),
new LambdaBinaryOperatorMethod<double> ((a, b) => checked(a * b)),
new LambdaBinaryOperatorMethod<decimal>((a, b) => checked(a * b))
);
static readonly OperatorMethod[] uncheckedMultiplicationOperators = Lift(
new LambdaBinaryOperatorMethod<int> ((a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<uint> ((a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<long> ((a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<ulong> ((a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<float> ((a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<double> ((a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<decimal>((a, b) => unchecked(a * b))
);
// C# 4.0 spec: §7.8.2 Division operator
static readonly OperatorMethod[] checkedDivisionOperators = Lift(
new LambdaBinaryOperatorMethod<int> ((a, b) => checked(a / b)),
new LambdaBinaryOperatorMethod<uint> ((a, b) => checked(a / b)),
new LambdaBinaryOperatorMethod<long> ((a, b) => checked(a / b)),
new LambdaBinaryOperatorMethod<ulong> ((a, b) => checked(a / b)),
new LambdaBinaryOperatorMethod<float> ((a, b) => checked(a / b)),
new LambdaBinaryOperatorMethod<double> ((a, b) => checked(a / b)),
new LambdaBinaryOperatorMethod<decimal>((a, b) => checked(a / b))
);
static readonly OperatorMethod[] uncheckedDivisionOperators = Lift(
new LambdaBinaryOperatorMethod<int> ((a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<uint> ((a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<long> ((a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<ulong> ((a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<float> ((a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<double> ((a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<decimal>((a, b) => unchecked(a / b))
);
// C# 4.0 spec: §7.8.3 Remainder operator
static readonly OperatorMethod[] checkedRemainderOperators = Lift(
new LambdaBinaryOperatorMethod<int> ((a, b) => checked(a % b)),
new LambdaBinaryOperatorMethod<uint> ((a, b) => checked(a % b)),
new LambdaBinaryOperatorMethod<long> ((a, b) => checked(a % b)),
new LambdaBinaryOperatorMethod<ulong> ((a, b) => checked(a % b)),
new LambdaBinaryOperatorMethod<float> ((a, b) => checked(a % b)),
new LambdaBinaryOperatorMethod<double> ((a, b) => checked(a % b)),
new LambdaBinaryOperatorMethod<decimal>((a, b) => checked(a % b))
);
static readonly OperatorMethod[] uncheckedRemainderOperators = Lift(
new LambdaBinaryOperatorMethod<int> ((a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<uint> ((a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<long> ((a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<ulong> ((a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<float> ((a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<double> ((a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<decimal>((a, b) => unchecked(a % b))
);
// C# 4.0 spec: §7.8.3 Addition operator
static readonly OperatorMethod[] checkedAdditionOperators = Lift(
new LambdaBinaryOperatorMethod<int> ((a, b) => checked(a + b)),
new LambdaBinaryOperatorMethod<uint> ((a, b) => checked(a + b)),
new LambdaBinaryOperatorMethod<long> ((a, b) => checked(a + b)),
new LambdaBinaryOperatorMethod<ulong> ((a, b) => checked(a + b)),
new LambdaBinaryOperatorMethod<float> ((a, b) => checked(a + b)),
new LambdaBinaryOperatorMethod<double> ((a, b) => checked(a + b)),
new LambdaBinaryOperatorMethod<decimal>((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<int> ((a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<uint> ((a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<long> ((a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<ulong> ((a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<float> ((a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<double> ((a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<decimal>((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()

33
ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs

@ -299,30 +299,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -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 @@ -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 @@ -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 @@ -425,7 +404,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
public IParameterizedMember BestCandidateAmbiguousWith {
get { return bestCandidateAmbiguousWith.Member; }
get { return bestCandidateAmbiguousWith != null ? bestCandidateAmbiguousWith.Member : null; }
}
}
}

11
ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs

@ -121,6 +121,17 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -121,6 +121,17 @@ namespace ICSharpCode.NRefactory.TypeSystem
throw new ArgumentException("enumType must be an enum");
}
}
/// <summary>
/// Gets whether the type is an delegate type.
/// </summary>
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
}
}

Loading…
Cancel
Save