diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs index 250c2619e4..62d758931c 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs @@ -16,11 +16,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver [Test] public void Multiplication() { - AssertType(typeof(int), resolver.ResolveBinaryOperator( - BinaryOperatorType.Multiply, MakeResult(typeof(int)), MakeResult(typeof(int)))); + TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Multiply, MakeResult(typeof(int)), + Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(int)); - AssertType(typeof(float), resolver.ResolveBinaryOperator( - BinaryOperatorType.Multiply, MakeResult(typeof(int)), MakeConstant(0.0f))); + 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))); @@ -28,8 +28,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver 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?)))); + 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)))); @@ -38,18 +38,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver [Test] public void Addition() { - AssertType(typeof(int?), resolver.ResolveBinaryOperator( - BinaryOperatorType.Add, MakeResult(typeof(short)), MakeResult(typeof(byte?)))); + 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(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"))); @@ -59,38 +53,76 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver 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)))); + TestOperator(MakeResult(typeof(int?)), BinaryOperatorType.Add, MakeResult(typeof(uint?)), + Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(long?)); - AssertType(typeof(Action), resolver.ResolveBinaryOperator( - BinaryOperatorType.Add, MakeResult(typeof(Action)), MakeResult(typeof(Action)))); + TestOperator(MakeResult(typeof(ushort?)), BinaryOperatorType.Add, MakeResult(typeof(ushort?)), + Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(int?)); - Assert.IsTrue(resolver.ResolveBinaryOperator( - BinaryOperatorType.Add, MakeResult(typeof(Action)), MakeResult(typeof(Action))).IsError); + TestOperator(MakeConstant(1), BinaryOperatorType.Add, MakeConstant(null), + Conversion.ImplicitNullableConversion, Conversion.NullLiteralConversion, typeof(int?)); + } + + [Test] + public void StringPlusNull() + { + ResolveResult left = MakeResult(typeof(string)); + var rr = (BinaryOperatorResolveResult)resolver.ResolveBinaryOperator( + BinaryOperatorType.Add, left, MakeConstant(null)); + AssertType(typeof(string), rr); + Assert.AreSame(left, rr.Left); + Assert.AreEqual("System.String", rr.Right.Type.FullName); + Assert.IsTrue(rr.Right.IsCompileTimeConstant); + Assert.IsNull(rr.Right.ConstantValue); + } + + [Test] + public void DelegateAddition() + { + TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Add, MakeResult(typeof(Action)), + Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(Action)); - AssertType(typeof(StringComparison?), resolver.ResolveBinaryOperator( - BinaryOperatorType.Add, MakeResult(typeof(StringComparison?)), MakeResult(typeof(int)))); + TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Add, MakeResult(typeof(Action)), + Conversion.ImplicitReferenceConversion, Conversion.IdentityConversion, typeof(Action)); - AssertType(typeof(StringComparison?), resolver.ResolveBinaryOperator( - BinaryOperatorType.Add, MakeResult(typeof(int?)), MakeResult(typeof(StringComparison)))); + TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Add, MakeResult(typeof(Action)), + Conversion.IdentityConversion, Conversion.ImplicitReferenceConversion, typeof(Action)); - AssertType(typeof(long?), resolver.ResolveBinaryOperator( - BinaryOperatorType.Add, MakeResult(typeof(int?)), MakeResult(typeof(uint?)))); + 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))); - AssertType(typeof(int?), resolver.ResolveBinaryOperator( - BinaryOperatorType.Add, MakeResult(typeof(ushort?)), MakeResult(typeof(ushort?)))); + AssertConstant(StringComparison.OrdinalIgnoreCase, resolver.ResolveBinaryOperator( + BinaryOperatorType.Add, MakeConstant((short)3), MakeConstant(StringComparison.InvariantCulture))); - Assert.IsTrue(resolver.ResolveBinaryOperator( - BinaryOperatorType.Add, MakeConstant(null), MakeConstant(null)).IsError); + TestOperator(MakeResult(typeof(StringComparison?)), BinaryOperatorType.Add, MakeResult(typeof(int)), + Conversion.IdentityConversion, Conversion.ImplicitNullableConversion, typeof(StringComparison?)); - AssertType(typeof(int?), resolver.ResolveBinaryOperator( - BinaryOperatorType.Add, MakeConstant(1), MakeConstant(null))); + TestOperator(MakeResult(typeof(StringComparison?)), BinaryOperatorType.Add, MakeResult(typeof(int?)), + Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(StringComparison?)); - AssertType(typeof(int*), resolver.ResolveBinaryOperator( - BinaryOperatorType.Add, MakeResult(typeof(int*)), MakeConstant(1))); + TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Add, MakeResult(typeof(StringComparison?)), + Conversion.ImplicitNullableConversion, Conversion.IdentityConversion, typeof(StringComparison?)); - AssertType(typeof(byte*), resolver.ResolveBinaryOperator( - BinaryOperatorType.Add, MakeResult(typeof(long)), MakeResult(typeof(byte*)))); + 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] @@ -109,44 +141,70 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver [Test] public void Subtraction() { - AssertType(typeof(int?), resolver.ResolveBinaryOperator( - BinaryOperatorType.Subtract, MakeResult(typeof(short)), MakeResult(typeof(byte?)))); + 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, MakeConstant("Te"), MakeConstant("xt")).IsError); + 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)); - AssertType(typeof(Action), resolver.ResolveBinaryOperator( - BinaryOperatorType.Subtract, MakeResult(typeof(Action)), MakeResult(typeof(Action)))); + TestOperator(MakeResult(typeof(Action)), BinaryOperatorType.Subtract, MakeResult(typeof(Action)), + Conversion.ImplicitReferenceConversion, Conversion.IdentityConversion, typeof(Action)); - AssertType(typeof(Action), resolver.ResolveBinaryOperator( - BinaryOperatorType.Subtract, MakeResult(typeof(Action)), MakeResult(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*)); - AssertType(typeof(StringComparison?), resolver.ResolveBinaryOperator( - BinaryOperatorType.Subtract, MakeResult(typeof(StringComparison?)), MakeResult(typeof(int)))); - - AssertType(typeof(int?), resolver.ResolveBinaryOperator( - BinaryOperatorType.Subtract, MakeResult(typeof(StringComparison?)), MakeResult(typeof(StringComparison)))); + TestOperator(MakeResult(typeof(byte*)), BinaryOperatorType.Subtract, MakeResult(typeof(uint)), + Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(byte*)); - Assert.IsTrue(resolver.ResolveBinaryOperator( - BinaryOperatorType.Subtract, MakeResult(typeof(int?)), MakeResult(typeof(StringComparison))).IsError); + TestOperator(MakeResult(typeof(byte*)), BinaryOperatorType.Subtract, MakeResult(typeof(short)), + Conversion.IdentityConversion, Conversion.ImplicitNumericConversion, typeof(byte*)); - AssertType(typeof(byte*), resolver.ResolveBinaryOperator( - BinaryOperatorType.Subtract, MakeResult(typeof(byte*)), MakeResult(typeof(uint)))); + TestOperator(MakeResult(typeof(byte*)), BinaryOperatorType.Subtract, MakeResult(typeof(byte*)), + Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(long)); - AssertType(typeof(long), resolver.ResolveBinaryOperator( - BinaryOperatorType.Subtract, MakeResult(typeof(byte*)), MakeResult(typeof(byte*)))); + AssertError(typeof(long), resolver.ResolveBinaryOperator(BinaryOperatorType.Subtract, MakeResult(typeof(byte*)), MakeResult(typeof(int*)))); } [Test] @@ -158,18 +216,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver AssertConstant(ulong.MaxValue >> 2, resolver.ResolveBinaryOperator( BinaryOperatorType.ShiftRight, MakeConstant(ulong.MaxValue), MakeConstant(2))); - AssertType(typeof(int?), resolver.ResolveBinaryOperator( - BinaryOperatorType.ShiftLeft, MakeResult(typeof(ushort?)), MakeConstant(1))); + TestOperator(MakeResult(typeof(ushort?)), BinaryOperatorType.ShiftLeft, MakeConstant(1), + Conversion.ImplicitNullableConversion, Conversion.ImplicitNullableConversion, typeof(int?)); - AssertType(typeof(int?), resolver.ResolveBinaryOperator( - BinaryOperatorType.ShiftLeft, MakeConstant(null), MakeConstant(1))); + TestOperator(MakeConstant(null), BinaryOperatorType.ShiftLeft, MakeConstant(1), + Conversion.NullLiteralConversion, Conversion.ImplicitNullableConversion, typeof(int?)); - AssertType(typeof(int?), resolver.ResolveBinaryOperator( - BinaryOperatorType.ShiftLeft, MakeConstant(null), MakeConstant(null))); + 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 Equality() + public void ConstantEquality() { AssertConstant(true, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(3), MakeConstant(3))); @@ -203,9 +264,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver AssertConstant(false, resolver.ResolveBinaryOperator( BinaryOperatorType.Equality, MakeConstant(null), MakeConstant('a'))); - - AssertType(typeof(bool), resolver.ResolveBinaryOperator( - BinaryOperatorType.Equality, MakeResult(typeof(int*)), MakeResult(typeof(uint*)))); + } + + [Test] + public void Equality() + { + TestOperator(MakeResult(typeof(int*)), BinaryOperatorType.Equality, MakeResult(typeof(uint*)), + Conversion.ImplicitPointerConversion, Conversion.ImplicitPointerConversion, typeof(bool)); } [Test] diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/CastTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/CastTests.cs index a961a9b8c3..5309f310dd 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/CastTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/CastTests.cs @@ -2,6 +2,7 @@ // 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 @@ -12,13 +13,34 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver [TestFixture] public class CastTests : ResolverTestBase { + void TestCast(Type targetType, ResolveResult input, Conversion expectedConversion) + { + IType type = targetType.ToTypeReference().Resolve(context); + ResolveResult rr = resolver.ResolveCast(type, input); + AssertType(targetType, rr); + Assert.AreEqual(typeof(ConversionResolveResult), rr.GetType()); + var crr = (ConversionResolveResult)rr; + Assert.AreEqual(expectedConversion, crr.Conversion, "ConversionResolveResult.Conversion"); + Assert.AreSame(input, crr.Input, "ConversionResolveResult.Input"); + } + [Test] public void SimpleCast() { - AssertType(typeof(int), resolver.ResolveCast(ResolveType(typeof(int)), MakeResult(typeof(float)))); - AssertType(typeof(string), resolver.ResolveCast(ResolveType(typeof(string)), MakeResult(typeof(object)))); - AssertType(typeof(byte), resolver.ResolveCast(ResolveType(typeof(byte)), MakeResult(typeof(dynamic)))); - AssertType(typeof(dynamic), resolver.ResolveCast(ResolveType(typeof(dynamic)), MakeResult(typeof(double)))); + TestCast(typeof(int), MakeResult(typeof(float)), Conversion.ExplicitNumericConversion); + TestCast(typeof(string), MakeResult(typeof(object)), Conversion.ExplicitReferenceConversion); + TestCast(typeof(byte), MakeResult(typeof(dynamic)), Conversion.ExplicitDynamicConversion); + TestCast(typeof(dynamic), MakeResult(typeof(double)), Conversion.BoxingConversion); + } + + [Test] + public void NullableCasts() + { + TestCast(typeof(int), MakeResult(typeof(int?)), Conversion.ExplicitNullableConversion); + TestCast(typeof(int?), MakeResult(typeof(int)), Conversion.ImplicitNullableConversion); + + TestCast(typeof(int?), MakeResult(typeof(long?)), Conversion.ExplicitNullableConversion); + TestCast(typeof(long?), MakeResult(typeof(int?)), Conversion.ImplicitNullableConversion); } [Test] diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs index a43fabd3aa..6a11e746e6 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs @@ -28,6 +28,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return conversions.ImplicitConversion(from2, to2); } + Conversion ExplicitConversion(Type from, Type to) + { + IType from2 = from.ToTypeReference().Resolve(ctx); + IType to2 = to.ToTypeReference().Resolve(ctx); + return conversions.ExplicitConversion(from2, to2); + } + [Test] public void IdentityConversions() { @@ -196,12 +203,22 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } [Test] - public void PointerConversion() + public void ImplicitPointerConversion() { Assert.AreEqual(C.ImplicitPointerConversion, ImplicitConversion(typeof(Null), typeof(int*))); Assert.AreEqual(C.ImplicitPointerConversion, ImplicitConversion(typeof(int*), typeof(void*))); } + [Test] + public void ExplicitPointerConversion() + { + Assert.AreEqual(C.ExplicitPointerConversion, ExplicitConversion(typeof(int*), typeof(short))); + Assert.AreEqual(C.ExplicitPointerConversion, ExplicitConversion(typeof(short), typeof(void*))); + + Assert.AreEqual(C.ExplicitPointerConversion, ExplicitConversion(typeof(void*), typeof(int*))); + Assert.AreEqual(C.ExplicitPointerConversion, ExplicitConversion(typeof(long*), typeof(byte*))); + } + [Test] public void NoConversionFromPointerTypeToObject() { @@ -217,8 +234,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver DefaultTypeParameter tm = new DefaultTypeParameter(EntityType.Method, 0, "TM"); Assert.AreEqual(C.None, conversions.ImplicitConversion(SharedTypes.Null, t)); - Assert.AreEqual(C.ImplicitTypeParameterConversion, conversions.ImplicitConversion(t, KnownTypeReference.Object.Resolve(ctx))); - Assert.AreEqual(C.ImplicitTypeParameterConversion, conversions.ImplicitConversion(t, SharedTypes.Dynamic)); + Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, KnownTypeReference.Object.Resolve(ctx))); + Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, SharedTypes.Dynamic)); Assert.AreEqual(C.None, conversions.ImplicitConversion(t, ctx.GetTypeDefinition(typeof(ValueType)))); Assert.AreEqual(C.IdentityConversion, conversions.ImplicitConversion(t, t)); @@ -281,14 +298,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver t.Constraints.Add(ctx.GetTypeDefinition(typeof(IList))); Assert.AreEqual(C.None, conversions.ImplicitConversion(SharedTypes.Null, t)); - Assert.AreEqual(C.ImplicitTypeParameterConversion, + Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, KnownTypeReference.Object.Resolve(ctx))); - Assert.AreEqual(C.ImplicitTypeParameterConversion, + Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, SharedTypes.Dynamic)); Assert.AreEqual(C.None, conversions.ImplicitConversion(t, ctx.GetTypeDefinition(typeof(ValueType)))); - Assert.AreEqual(C.ImplicitTypeParameterConversion, + Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, ctx.GetTypeDefinition(typeof(IList)))); - Assert.AreEqual(C.ImplicitTypeParameterConversion, + Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, ctx.GetTypeDefinition(typeof(IEnumerable)))); } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs index 931cdad98d..968daa35bc 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs @@ -121,6 +121,38 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Assert.AreEqual(expectedType.ToTypeReference().Resolve(context), rr.Type); } + protected void TestOperator(UnaryOperatorType op, ResolveResult input, + Conversion expectedConversion, Type expectedResultType) + { + var rr = resolver.ResolveUnaryOperator(op, input); + AssertType(expectedResultType, rr); + Assert.AreEqual(typeof(UnaryOperatorResolveResult), rr.GetType()); + var uorr = (UnaryOperatorResolveResult)rr; + AssertConversion(uorr.Input, input, expectedConversion, "Conversion"); + } + + protected void TestOperator(ResolveResult lhs, BinaryOperatorType op, ResolveResult rhs, + Conversion expectedLeftConversion, Conversion expectedRightConversion, Type expectedResultType) + { + var rr = resolver.ResolveBinaryOperator(op, lhs, rhs); + AssertType(expectedResultType, rr); + Assert.AreEqual(typeof(BinaryOperatorResolveResult), rr.GetType()); + var borr = (BinaryOperatorResolveResult)rr; + AssertConversion(borr.Left, lhs, expectedLeftConversion, "Left conversion"); + AssertConversion(borr.Right, rhs, expectedRightConversion, "Right conversion"); + } + + protected void AssertConversion(ResolveResult conversionResult, ResolveResult expectedRR, Conversion expectedConversion, string text) + { + if (expectedConversion == Conversion.IdentityConversion) { + Assert.AreSame(expectedRR, conversionResult, "Expected no " + text); + } else { + ConversionResolveResult crr = (ConversionResolveResult)conversionResult; + Assert.AreEqual(expectedConversion, crr.Conversion, text); + Assert.AreSame(expectedRR, crr.Input, "Input of " + text); + } + } + IEnumerable FindDollarSigns(string code) { int line = 1; diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/UnaryOperatorTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/UnaryOperatorTests.cs index 7793efc31c..de5d11c43d 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/UnaryOperatorTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/UnaryOperatorTests.cs @@ -16,33 +16,59 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver [Test] public void TestAddressOf() { - AssertType(typeof(int*), resolver.ResolveUnaryOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(int)))); - AssertType(typeof(byte**), resolver.ResolveUnaryOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(byte*)))); - AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(dynamic)))); + TestOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(int)), + Conversion.IdentityConversion, typeof(int*)); + + TestOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(byte*)), + Conversion.IdentityConversion, typeof(byte**)); + + TestOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(dynamic)), + Conversion.IdentityConversion, typeof(dynamic)); } [Test] public void TestDereference() { - AssertType(typeof(int), resolver.ResolveUnaryOperator(UnaryOperatorType.Dereference, MakeResult(typeof(int*)))); - AssertType(typeof(long*), resolver.ResolveUnaryOperator(UnaryOperatorType.Dereference, MakeResult(typeof(long**)))); + TestOperator(UnaryOperatorType.Dereference, MakeResult(typeof(int*)), + Conversion.IdentityConversion, typeof(int)); + + TestOperator(UnaryOperatorType.Dereference, MakeResult(typeof(long**)), + Conversion.IdentityConversion, typeof(long*)); + Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Dereference, MakeResult(typeof(int))).IsError); - AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.Dereference, MakeResult(typeof(dynamic)))); + + TestOperator(UnaryOperatorType.Dereference, MakeResult(typeof(dynamic)), + Conversion.IdentityConversion, typeof(dynamic)); } [Test] public void TestIncrementDecrement() { - AssertType(typeof(byte), resolver.ResolveUnaryOperator(UnaryOperatorType.Increment, MakeResult(typeof(byte)))); - AssertType(typeof(ulong), resolver.ResolveUnaryOperator(UnaryOperatorType.Decrement, MakeResult(typeof(ulong)))); - AssertType(typeof(short?), resolver.ResolveUnaryOperator(UnaryOperatorType.PostDecrement, MakeResult(typeof(short?)))); - AssertType(typeof(TypeCode), resolver.ResolveUnaryOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(TypeCode)))); - AssertType(typeof(TypeCode?), resolver.ResolveUnaryOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(TypeCode?)))); - AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(dynamic)))); + TestOperator(UnaryOperatorType.Increment, MakeResult(typeof(byte)), + Conversion.IdentityConversion, typeof(byte)); + + TestOperator(UnaryOperatorType.Decrement, MakeResult(typeof(ulong)), + Conversion.IdentityConversion, typeof(ulong)); + + TestOperator(UnaryOperatorType.PostDecrement, MakeResult(typeof(short?)), + Conversion.IdentityConversion, typeof(short?)); + + TestOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(TypeCode)), + Conversion.IdentityConversion, typeof(TypeCode)); + + TestOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(TypeCode?)), + Conversion.IdentityConversion, typeof(TypeCode?)); + + TestOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(dynamic)), + Conversion.IdentityConversion, typeof(dynamic)); + AssertError(typeof(object), resolver.ResolveUnaryOperator(UnaryOperatorType.Increment, MakeResult(typeof(object)))); - AssertType(typeof(int*), resolver.ResolveUnaryOperator(UnaryOperatorType.Increment, MakeResult(typeof(int*)))); - AssertType(typeof(uint*), resolver.ResolveUnaryOperator(UnaryOperatorType.PostDecrement, MakeResult(typeof(uint*)))); + TestOperator(UnaryOperatorType.Increment, MakeResult(typeof(int*)), + Conversion.IdentityConversion, typeof(int*)); + + TestOperator(UnaryOperatorType.PostDecrement, MakeResult(typeof(uint*)), + Conversion.IdentityConversion, typeof(uint*)); } [Test] @@ -58,8 +84,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver AssertConstant(1L, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((long)1))); AssertConstant((ulong)1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((ulong)1))); - AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeResult(typeof(dynamic)))); - AssertType(typeof(int?), resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeResult(typeof(ushort?)))); + TestOperator(UnaryOperatorType.Plus, MakeResult(typeof(dynamic)), + Conversion.IdentityConversion, typeof(dynamic)); + + TestOperator(UnaryOperatorType.Plus, MakeResult(typeof(ushort?)), + Conversion.ImplicitNullableConversion, typeof(int?)); } [Test] @@ -73,8 +102,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver AssertConstant(1m, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(-1m))); AssertConstant(-65, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant('A'))); - AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeResult(typeof(dynamic)))); - AssertType(typeof(long?), resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeResult(typeof(uint?)))); + TestOperator(UnaryOperatorType.Minus, MakeResult(typeof(dynamic)), + Conversion.IdentityConversion, typeof(dynamic)); + + TestOperator(UnaryOperatorType.Minus, MakeResult(typeof(uint?)), + Conversion.ImplicitNullableConversion, typeof(long?)); } [Test] @@ -104,9 +136,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver AssertConstant(~(ulong)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((ulong)1))); Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant(1.0)).IsError); - AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeResult(typeof(dynamic)))); - AssertType(typeof(uint), resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeResult(typeof(uint)))); - AssertType(typeof(int?), resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeResult(typeof(ushort?)))); + TestOperator(UnaryOperatorType.BitNot, MakeResult(typeof(dynamic)), + Conversion.IdentityConversion, typeof(dynamic)); + + TestOperator(UnaryOperatorType.BitNot, MakeResult(typeof(uint)), + Conversion.IdentityConversion, typeof(uint)); + + TestOperator(UnaryOperatorType.BitNot, MakeResult(typeof(sbyte)), + Conversion.ImplicitNumericConversion, typeof(int)); + + TestOperator(UnaryOperatorType.BitNot, MakeResult(typeof(ushort?)), + Conversion.ImplicitNullableConversion, typeof(int?)); } [Test] @@ -114,9 +154,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { AssertConstant(true, resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeConstant(false))); AssertConstant(false, resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeConstant(true))); - AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeResult(typeof(dynamic)))); - AssertType(typeof(bool), resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeResult(typeof(bool)))); - AssertType(typeof(bool?), resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeResult(typeof(bool?)))); + + TestOperator(UnaryOperatorType.Not, MakeResult(typeof(dynamic)), + Conversion.IdentityConversion, typeof(dynamic)); + + TestOperator(UnaryOperatorType.Not, MakeResult(typeof(bool)), + Conversion.IdentityConversion, typeof(bool)); + + TestOperator(UnaryOperatorType.Not, MakeResult(typeof(bool?)), + Conversion.IdentityConversion, typeof(bool?)); } [Test] diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ByReferenceResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ByReferenceResolveResult.cs index 5d67cb2b8b..d533f4e5b8 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/ByReferenceResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ByReferenceResolveResult.cs @@ -17,6 +17,8 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; +using System.Linq; using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -29,6 +31,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public bool IsOut { get; private set; } public bool IsRef { get { return !IsOut;} } + public readonly ResolveResult ElementResult; + + public ByReferenceResolveResult(ResolveResult elementResult, bool isOut) + : this(elementResult.Type, isOut) + { + this.ElementResult = elementResult; + } + public ByReferenceResolveResult(IType elementType, bool isOut) : base(new ByReferenceType(elementType)) { @@ -39,6 +49,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver get { return ((ByReferenceType)this.Type).ElementType; } } + public override IEnumerable GetChildResults() + { + if (ElementResult != null) + return new[] { ElementResult }; + else + return Enumerable.Empty(); + } + public override string ToString() { return string.Format("[{0} {1} {2}]", GetType().Name, IsOut ? "out" : "ref", ElementType); diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs index 6fedab33db..afbfbfa687 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs @@ -38,6 +38,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver static readonly ResolveResult NullResult = new ResolveResult(SharedTypes.Null); readonly ITypeResolveContext context; + readonly Conversions conversions; internal readonly CancellationToken cancellationToken; #region Constructor @@ -51,6 +52,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver throw new ArgumentNullException("context"); this.context = context; this.cancellationToken = cancellationToken; + this.conversions = new Conversions(context); } #endregion @@ -450,7 +452,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver cancellationToken.ThrowIfCancellationRequested(); if (SharedTypes.Dynamic.Equals(expression.Type)) - return DynamicResult; + return new UnaryOperatorResolveResult(SharedTypes.Dynamic, op, expression); // C# 4.0 spec: §7.3.3 Unary operator overload resolution string overloadableOperatorName = GetOverloadableOperatorName(op); @@ -459,11 +461,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver case UnaryOperatorType.Dereference: PointerType p = expression.Type as PointerType; if (p != null) - return new ResolveResult(p.ElementType); + return new UnaryOperatorResolveResult(p.ElementType, op, expression); else return ErrorResult; case UnaryOperatorType.AddressOf: - return new ResolveResult(new PointerType(expression.Type)); + return new UnaryOperatorResolveResult(new PointerType(expression.Type), op, expression); default: throw new ArgumentException("Invalid value for UnaryOperatorType", "op"); } @@ -486,8 +488,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver // C# 4.0 spec: §7.6.9 Postfix increment and decrement operators // C# 4.0 spec: §7.7.5 Prefix increment and decrement operators TypeCode code = ReflectionHelper.GetTypeCode(type); - if ((code >= TypeCode.SByte && code <= TypeCode.Decimal) || type.IsEnum() || type is PointerType) - return new ResolveResult(expression.Type); + if ((code >= TypeCode.SByte && code <= TypeCode.Decimal) || type.Kind == TypeKind.Enum || type.Kind == TypeKind.Pointer) + return new UnaryOperatorResolveResult(expression.Type, op, expression); else return new ErrorResolveResult(expression.Type); case UnaryOperatorType.Plus: @@ -500,14 +502,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver methodGroup = logicalNegationOperator; break; case UnaryOperatorType.BitNot: - if (type.IsEnum()) { + if (type.Kind == TypeKind.Enum) { if (expression.IsCompileTimeConstant && !isNullable) { // evaluate as (E)(~(U)x); var U = expression.ConstantValue.GetType().ToTypeReference().Resolve(context); var unpackedEnum = new ConstantResolveResult(U, expression.ConstantValue); return CheckErrorAndResolveCast(expression.Type, ResolveUnaryOperator(op, unpackedEnum)); } else { - return new ResolveResult(expression.Type); + return new UnaryOperatorResolveResult(expression.Type, op, expression); } } else { methodGroup = bitwiseComplementOperators; @@ -533,7 +535,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } return new ConstantResolveResult(resultType, val); } else { - return new ResolveResult(resultType); + expression = Convert(expression, m.Parameters[0].Type, r.ArgumentConversions[0]); + return new UnaryOperatorResolveResult(resultType, op, expression); } } #endregion @@ -548,19 +551,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver switch (op) { case UnaryOperatorType.Minus: if (code == TypeCode.UInt32) { - IType targetType = KnownTypeReference.Int64.Resolve(context); - type = targetType; - if (isNullable) targetType = NullableType.Create(targetType, context); - return ResolveCast(targetType, expression); + type = KnownTypeReference.Int64.Resolve(context); + return Convert(expression, MakeNullable(type, isNullable), + isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion); } goto case UnaryOperatorType.Plus; case UnaryOperatorType.Plus: case UnaryOperatorType.BitNot: if (code >= TypeCode.Char && code <= TypeCode.UInt16) { - IType targetType = KnownTypeReference.Int32.Resolve(context); - type = targetType; - if (isNullable) targetType = NullableType.Create(targetType, context); - return ResolveCast(targetType, expression); + type = KnownTypeReference.Int32.Resolve(context); + return Convert(expression, MakeNullable(type, isNullable), + isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion); } break; } @@ -700,8 +701,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { cancellationToken.ThrowIfCancellationRequested(); - if (SharedTypes.Dynamic.Equals(lhs.Type) || SharedTypes.Dynamic.Equals(rhs.Type)) - return DynamicResult; + if (SharedTypes.Dynamic.Equals(lhs.Type) || SharedTypes.Dynamic.Equals(rhs.Type)) { + lhs = Convert(lhs, SharedTypes.Dynamic, conversions.ImplicitConversion(lhs, SharedTypes.Dynamic)); + rhs = Convert(rhs, SharedTypes.Dynamic, conversions.ImplicitConversion(rhs, SharedTypes.Dynamic)); + return new BinaryOperatorResolveResult(SharedTypes.Dynamic, lhs, op, rhs); + } // C# 4.0 spec: §7.3.4 Binary operator overload resolution string overloadableOperatorName = GetOverloadableOperatorName(op); @@ -765,23 +769,41 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver case BinaryOperatorType.Add: methodGroup = CheckForOverflow ? checkedAdditionOperators : uncheckedAdditionOperators; { - Conversions conversions = new Conversions(context); - if (lhsType.IsEnum() && conversions.ImplicitConversion(rhsType, lhsType.GetEnumUnderlyingType(context))) { + if (lhsType.Kind == TypeKind.Enum) { // E operator +(E x, U y); - return HandleEnumAdditionOrSubtraction(isNullable, lhsType, op, lhs, rhs); - } else if (rhsType.IsEnum() && conversions.ImplicitConversion(lhsType, rhsType.GetEnumUnderlyingType(context))) { + IType underlyingType = MakeNullable(lhsType.GetEnumUnderlyingType(context), isNullable); + if (TryConvert(ref rhs, underlyingType)) { + return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs); + } + } + if (rhsType.Kind == TypeKind.Enum) { // E operator +(U x, E y); - return ResolveBinaryOperator(op, rhs, lhs); // swap arguments + IType underlyingType = MakeNullable(rhsType.GetEnumUnderlyingType(context), isNullable); + if (TryConvert(ref lhs, underlyingType)) { + return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs); + } } - 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.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) { + return new BinaryOperatorResolveResult(lhsType, lhs, op, rhs); + } else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) { + return new BinaryOperatorResolveResult(rhsType, lhs, op, rhs); } - if (lhsType is PointerType && IsInteger(ReflectionHelper.GetTypeCode(rhsType))) { - return new ResolveResult(lhsType); - } else if (rhsType is PointerType && IsInteger(ReflectionHelper.GetTypeCode(lhsType))) { - return new ResolveResult(rhsType); + + if (lhsType is PointerType) { + methodGroup = new [] { + new PointerArithmeticOperator(lhsType, lhsType, KnownTypeReference.Int32), + new PointerArithmeticOperator(lhsType, lhsType, KnownTypeReference.UInt32), + new PointerArithmeticOperator(lhsType, lhsType, KnownTypeReference.Int64), + new PointerArithmeticOperator(lhsType, lhsType, KnownTypeReference.UInt64) + }; + } else if (rhsType is PointerType) { + methodGroup = new [] { + new PointerArithmeticOperator(rhsType, KnownTypeReference.Int32, rhsType), + new PointerArithmeticOperator(rhsType, KnownTypeReference.UInt32, rhsType), + new PointerArithmeticOperator(rhsType, KnownTypeReference.Int64, rhsType), + new PointerArithmeticOperator(rhsType, KnownTypeReference.UInt64, rhsType) + }; } if (SharedTypes.Null.Equals(lhsType) && SharedTypes.Null.Equals(rhsType)) return new ErrorResolveResult(SharedTypes.Null); @@ -790,27 +812,47 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver case BinaryOperatorType.Subtract: methodGroup = CheckForOverflow ? checkedSubtractionOperators : uncheckedSubtractionOperators; { - Conversions conversions = new Conversions(context); - if (lhsType.IsEnum() && conversions.ImplicitConversion(rhsType, lhsType.GetEnumUnderlyingType(context))) { + if (lhsType.Kind == TypeKind.Enum) { // E operator –(E x, U y); - return HandleEnumAdditionOrSubtraction(isNullable, lhsType, op, lhs, rhs); - } else if (lhsType.IsEnum() && conversions.ImplicitConversion(rhs, lhs.Type)) { + IType underlyingType = MakeNullable(lhsType.GetEnumUnderlyingType(context), isNullable); + if (TryConvert(ref rhs, underlyingType)) { + return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs); + } // U operator –(E x, E y); - return HandleEnumSubtraction(isNullable, lhsType, lhs, rhs); - } else if (rhsType.IsEnum() && conversions.ImplicitConversion(lhs, rhs.Type)) { + if (TryConvert(ref rhs, lhs.Type)) { + return HandleEnumSubtraction(isNullable, lhsType, lhs, rhs); + } + } + if (rhsType.Kind == TypeKind.Enum) { // U operator –(E x, E y); - return HandleEnumSubtraction(isNullable, lhsType, lhs, rhs); + if (TryConvert(ref lhs, rhs.Type)) { + return HandleEnumSubtraction(isNullable, rhsType, lhs, rhs); + } } - 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.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) { + return new BinaryOperatorResolveResult(lhsType, lhs, op, rhs); + } else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) { + return new BinaryOperatorResolveResult(rhsType, lhs, op, rhs); } - if (lhsType is PointerType && IsInteger(ReflectionHelper.GetTypeCode(rhsType))) { - return new ResolveResult(lhsType); - } else if (lhsType is PointerType && lhsType.Equals(rhsType)) { - return new ResolveResult(KnownTypeReference.Int64.Resolve(context)); + + if (lhsType is PointerType) { + if (rhsType is PointerType) { + IType int64 = KnownTypeReference.Int64.Resolve(context); + if (lhsType.Equals(rhsType)) { + return new BinaryOperatorResolveResult(int64, lhs, op, rhs); + } else { + return new ErrorResolveResult(int64); + } + } + methodGroup = new [] { + new PointerArithmeticOperator(lhsType, lhsType, KnownTypeReference.Int32), + new PointerArithmeticOperator(lhsType, lhsType, KnownTypeReference.UInt32), + new PointerArithmeticOperator(lhsType, lhsType, KnownTypeReference.Int64), + new PointerArithmeticOperator(lhsType, lhsType, KnownTypeReference.UInt64) + }; } + if (SharedTypes.Null.Equals(lhsType) && SharedTypes.Null.Equals(rhsType)) return new ErrorResolveResult(SharedTypes.Null); } @@ -828,11 +870,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver case BinaryOperatorType.LessThanOrEqual: case BinaryOperatorType.GreaterThanOrEqual: { - Conversions conversions = new Conversions(context); - if (lhsType.IsEnum() && conversions.ImplicitConversion(rhs, lhs.Type)) { + if (lhsType.Kind == TypeKind.Enum && TryConvert(ref rhs, lhs.Type)) { // bool operator op(E x, E y); return HandleEnumComparison(op, lhsType, isNullable, lhs, rhs); - } else if (rhsType.IsEnum() && conversions.ImplicitConversion(lhs, rhs.Type)) { + } else if (rhsType.Kind == TypeKind.Enum && TryConvert(ref lhs, rhs.Type)) { // bool operator op(E x, E y); return HandleEnumComparison(op, rhsType, isNullable, lhs, rhs); } else if (lhsType is PointerType && rhsType is PointerType) { @@ -866,14 +907,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver case BinaryOperatorType.BitwiseOr: case BinaryOperatorType.ExclusiveOr: { - Conversions conversions = new Conversions(context); - if (lhsType.IsEnum() && conversions.ImplicitConversion(rhs, lhs.Type)) { - // E operator op(E x, E y); - return HandleEnumAdditionOrSubtraction(isNullable, lhsType, op, lhs, rhs); - } else if (rhsType.IsEnum() && conversions.ImplicitConversion(lhs, rhs.Type)) { - // E operator op(E x, E y); - return HandleEnumAdditionOrSubtraction(isNullable, rhsType, op, lhs, rhs); + Conversion c; + if (lhsType.Kind == TypeKind.Enum && TryConvert(ref rhs, lhs.Type)) { + // bool operator op(E x, E y); + return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs); + } else if (rhsType.Kind == TypeKind.Enum && TryConvert(ref lhs, rhs.Type)) { + // bool operator op(E x, E y); + return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs); } + switch (op) { case BinaryOperatorType.BitwiseAnd: methodGroup = bitwiseAndOperators; @@ -915,7 +957,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } return new ConstantResolveResult(resultType, val); } else { - return new ResolveResult(resultType); + lhs = Convert(lhs, m.Parameters[0].Type, r.ArgumentConversions[0]); + rhs = Convert(rhs, m.Parameters[1].Type, r.ArgumentConversions[1]); + return new BinaryOperatorResolveResult(resultType, lhs, op, rhs); } } #endregion @@ -938,7 +982,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return rhs; return ResolveBinaryOperator(op, lhs, rhs); } - return new ResolveResult(KnownTypeReference.Boolean.Resolve(context)); + IType resultType = KnownTypeReference.Boolean.Resolve(context); + return new BinaryOperatorResolveResult(resultType, lhs, op, rhs); } /// @@ -958,19 +1003,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return rhs; return CheckErrorAndResolveCast(elementType, ResolveBinaryOperator(BinaryOperatorType.Subtract, lhs, rhs)); } - return new ResolveResult(isNullable ? NullableType.Create(elementType, context) : elementType); + IType resultType = MakeNullable(elementType, isNullable); + return new BinaryOperatorResolveResult(resultType, lhs, BinaryOperatorType.Subtract, rhs); } /// - /// Handle the case where an integral value is added to or subtracted from an enum value, - /// or when two enum values of the same type are combined using a bitwise operator. + /// Handle the following enum operators: /// E operator +(E x, U y); + /// E operator +(U x, E y); /// E operator –(E x, U y); /// E operator &(E x, E y); /// E operator |(E x, E y); /// E operator ^(E x, E y); /// - ResolveResult HandleEnumAdditionOrSubtraction(bool isNullable, IType enumType, BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs) + ResolveResult HandleEnumOperator(bool isNullable, IType enumType, BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs) { // evaluate as (E)((U)x op (U)y) if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable) { @@ -983,7 +1029,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return rhs; return CheckErrorAndResolveCast(enumType, ResolveBinaryOperator(op, lhs, rhs)); } - return new ResolveResult(isNullable ? NullableType.Create(enumType, context) : enumType); + IType resultType = MakeNullable(enumType, isNullable); + return new BinaryOperatorResolveResult(resultType, lhs, op, rhs); + } + + IType MakeNullable(IType type, bool isNullable) + { + if (isNullable) + return NullableType.Create(type, context); + else + return type; } #endregion @@ -1055,15 +1110,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - static bool IsInteger(TypeCode code) - { - return code >= TypeCode.SByte && code <= TypeCode.UInt64; - } - ResolveResult CastTo(TypeCode targetType, bool isNullable, ResolveResult expression, bool allowNullableConstants) { IType elementType = targetType.ToTypeReference().Resolve(context); - IType nullableType = isNullable ? NullableType.Create(elementType, context) : elementType; + IType nullableType = MakeNullable(elementType, isNullable); + if (nullableType.Equals(expression.Type)) + return expression; if (allowNullableConstants && expression.IsCompileTimeConstant) { if (expression.ConstantValue == null) return new ConstantResolveResult(nullableType, null); @@ -1073,7 +1125,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Debug.Assert(rr.IsCompileTimeConstant); return new ConstantResolveResult(nullableType, rr.ConstantValue); } else { - return ResolveCast(nullableType, expression); + return Convert(expression, nullableType, + isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion); } } #endregion @@ -1127,6 +1180,25 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public abstract object Invoke(CSharpResolver resolver, object lhs, object rhs); } + sealed class PointerArithmeticOperator : BinaryOperatorMethod + { + public PointerArithmeticOperator(ITypeReference returnType, ITypeReference parameter1, ITypeReference parameter2) + { + this.ReturnType = returnType; + this.Parameters.Add(new DefaultParameter(parameter1, "x")); + this.Parameters.Add(new DefaultParameter(parameter2, "y")); + } + + public override bool CanEvaluateAtCompileTime { + get { return false; } + } + + public override object Invoke(CSharpResolver resolver, object lhs, object rhs) + { + throw new NotSupportedException(); + } + } + sealed class LambdaBinaryOperatorMethod : BinaryOperatorMethod { readonly Func func; @@ -1517,18 +1589,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #region Null coalescing operator ResolveResult ResolveNullCoalescingOperator(ResolveResult lhs, ResolveResult rhs) { - Conversions conversions = new Conversions(context); if (NullableType.IsNullable(lhs.Type)) { IType a0 = NullableType.GetUnderlyingType(lhs.Type); - if (conversions.ImplicitConversion(rhs, a0)) - return new ResolveResult(a0); + if (TryConvert(ref rhs, a0)) { + return new BinaryOperatorResolveResult(a0, lhs, BinaryOperatorType.NullCoalescing, rhs); + } } - if (conversions.ImplicitConversion(rhs, lhs.Type)) - return new ResolveResult(lhs.Type); - if (conversions.ImplicitConversion(lhs, rhs.Type)) - return new ResolveResult(rhs.Type); - else + if (TryConvert(ref rhs, lhs.Type)) { + return new BinaryOperatorResolveResult(lhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs); + } + if (TryConvert(ref lhs, rhs.Type)) { + return new BinaryOperatorResolveResult(rhs.Type, lhs, BinaryOperatorType.NullCoalescing, rhs); + } else { return new ErrorResolveResult(lhs.Type); + } } #endregion @@ -1541,6 +1615,27 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #endregion #region ResolveCast + bool TryConvert(ref ResolveResult rr, IType targetType) + { + Conversion c = conversions.ImplicitConversion(rr, targetType); + if (c) { + rr = Convert(rr, targetType, c); + return true; + } else { + return false; + } + } + + ResolveResult Convert(ResolveResult rr, ITypeReference targetType, Conversion c) + { + if (c == Conversion.IdentityConversion) + return rr; + else if (rr.IsCompileTimeConstant && c != Conversion.None) + return ResolveCast(targetType.Resolve(context), rr); + else + return new ConversionResolveResult(targetType.Resolve(context), rr, c); + } + public ResolveResult ResolveCast(IType targetType, ResolveResult expression) { cancellationToken.ThrowIfCancellationRequested(); @@ -1570,8 +1665,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } } - // TODO: return implicit/explicit conversion being applied - return new ResolveResult(targetType); + Conversion c = conversions.ExplicitConversion(expression, targetType); + if (c) + return new ConversionResolveResult(targetType, expression, c); + else + return new ErrorResolveResult(targetType); } object CSharpPrimitiveCast(TypeCode targetType, object input) @@ -1860,7 +1958,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// /// Gets all extension methods available in the current using scope. - /// This list includes unaccessible + /// This list includes unaccessible /// List> GetAllExtensionMethods() { @@ -2024,9 +2122,31 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { cancellationToken.ThrowIfCancellationRequested(); - if (SharedTypes.Dynamic.Equals(target.Type)) - return DynamicResult; + switch (target.Type.Kind) { + case TypeKind.Dynamic: + for (int i = 0; i < arguments.Length; i++) { + arguments[i] = Convert(arguments[i], SharedTypes.Dynamic, + conversions.ImplicitConversion(arguments[i], SharedTypes.Dynamic)); + } + return new ArrayAccessResolveResult(SharedTypes.Dynamic, target, arguments); + + case TypeKind.Array: + case TypeKind.Pointer: + // §7.6.6.1 Array access / §18.5.3 Pointer element access + for (int i = 0; i < arguments.Length; i++) { + if (!(TryConvert(ref arguments[i], KnownTypeReference.Int32.Resolve(context)) || + TryConvert(ref arguments[i], KnownTypeReference.UInt32.Resolve(context)) || + TryConvert(ref arguments[i], KnownTypeReference.Int64.Resolve(context)) || + TryConvert(ref arguments[i], KnownTypeReference.UInt64.Resolve(context)))) + { + // conversion failed + arguments[i] = Convert(arguments[i], KnownTypeReference.Int32, Conversion.None); + } + } + return new ArrayAccessResolveResult(((TypeWithElementType)target.Type).ElementType, target, arguments); + } + // §7.6.6.2 Array access OverloadResolution or = new OverloadResolution(context, arguments, argumentNames, new IType[0]); MemberLookup lookup = CreateMemberLookup(); bool allowProtectedAccess = lookup.IsProtectedAccessAllowed(target.Type); @@ -2137,15 +2257,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver cancellationToken.ThrowIfCancellationRequested(); - Conversions c = new Conversions(context); bool isValid; IType resultType; if (SharedTypes.Dynamic.Equals(trueExpression.Type) || SharedTypes.Dynamic.Equals(falseExpression.Type)) { resultType = SharedTypes.Dynamic; isValid = true; } else if (HasType(trueExpression) && HasType(falseExpression)) { - bool t2f = c.ImplicitConversion(trueExpression.Type, falseExpression.Type); - bool f2t = c.ImplicitConversion(falseExpression.Type, trueExpression.Type); + bool t2f = conversions.ImplicitConversion(trueExpression.Type, falseExpression.Type); + bool f2t = conversions.ImplicitConversion(falseExpression.Type, trueExpression.Type); resultType = (f2t && !t2f) ? trueExpression.Type : falseExpression.Type; // The operator is valid: // a) if there's a conversion in one direction but not the other @@ -2153,10 +2272,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver isValid = (t2f != f2t) || (t2f && f2t && trueExpression.Type.Equals(falseExpression.Type)); } else if (HasType(trueExpression)) { resultType = trueExpression.Type; - isValid = c.ImplicitConversion(falseExpression, resultType); + isValid = conversions.ImplicitConversion(falseExpression, resultType); } else if (HasType(falseExpression)) { resultType = falseExpression.Type; - isValid = c.ImplicitConversion(trueExpression, resultType); + isValid = conversions.ImplicitConversion(trueExpression, resultType); } else { return ErrorResult; } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ConversionResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ConversionResolveResult.cs new file mode 100644 index 0000000000..1dd937c3e0 --- /dev/null +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ConversionResolveResult.cs @@ -0,0 +1,40 @@ +// 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.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + public class ConversionResolveResult : ResolveResult + { + public readonly ResolveResult Input; + public readonly Conversion Conversion; + + public ConversionResolveResult(IType targetType, ResolveResult input, Conversion conversion) + : base(targetType) + { + this.Input = input; + this.Conversion = conversion; + } + + public override bool IsError { + get { return !Conversion.IsValid; } + } + } +} diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs b/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs index 6d6a189b04..11be4fdcaa 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.Utils; @@ -39,11 +40,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public static readonly Conversion BoxingConversion = new Conversion(7); public static readonly Conversion ImplicitDynamicConversion = new Conversion(8); public static readonly Conversion ImplicitConstantExpressionConversion = new Conversion(9); - public static readonly Conversion ImplicitTypeParameterConversion = new Conversion(10); - const int userDefinedImplicitConversionKind = 11; - public static readonly Conversion ImplicitPointerConversion = new Conversion(12); - const int anonymousFunctionConversionKind = 13; - const int methodGroupConversionKind = 14; + const int userDefinedImplicitConversionKind = 10; + public static readonly Conversion ImplicitPointerConversion = new Conversion(11); + const int anonymousFunctionConversionKind = 12; + const int methodGroupConversionKind = 13; + public static readonly Conversion ExplicitNumericConversion = new Conversion(14); + public static readonly Conversion ExplicitEnumerationConversion = new Conversion(15); + public static readonly Conversion ExplicitNullableConversion = new Conversion(16); + public static readonly Conversion ExplicitReferenceConversion = new Conversion(17); + public static readonly Conversion UnboxingConversion = new Conversion(18); + public static readonly Conversion ExplicitDynamicConversion = new Conversion(19); + public static readonly Conversion ExplicitPointerConversion = new Conversion(20); + const int userDefinedExplicitConversionKind = 21; static readonly string[] conversionNames = { "None", @@ -56,11 +64,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver "Boxing conversion", "Implicit dynamic conversion", "Implicit constant expression conversion", - "Implicit conversion involving type parameter", "User-defined implicit conversion", "Implicit pointer conversion", "Anonymous function conversion", "Method group conversion", + "Explicit numeric conversion", + "Explicit enumeration conversion", + "Explicit nullable conversion", + "Explicit reference conversion", + "Unboxing conversion", + "Explicit dynamic conversion", + "Explicit pointer conversion", + "User-defined explicit conversion" }; public static Conversion UserDefinedImplicitConversion(IMethod operatorMethod) @@ -68,6 +83,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return new Conversion(userDefinedImplicitConversionKind, operatorMethod); } + public static Conversion UserDefinedExplicitConversion(IMethod operatorMethod) + { + return new Conversion(userDefinedExplicitConversionKind, operatorMethod); + } + public static Conversion MethodGroupConversion(IMethod chosenMethod) { return new Conversion(methodGroupConversionKind, chosenMethod); @@ -95,16 +115,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// Gets whether this conversion is an implicit conversion. /// public bool IsImplicitConversion { - get { - return kind >= IdentityConversion.kind && kind <= methodGroupConversionKind; - } + get { return kind >= IdentityConversion.kind && kind <= methodGroupConversionKind; } + } + + /// + /// Gets whether this conversion is an explicit conversion. + /// + public bool IsExplicitConversion { + get { return kind > methodGroupConversionKind; } } /// /// Gets whether this conversion is user-defined. /// public bool IsUserDefined { - get { return kind == userDefinedImplicitConversionKind; } + get { return kind == userDefinedImplicitConversionKind || kind == userDefinedExplicitConversionKind; } } /// @@ -139,9 +164,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return name; } + public bool IsValid { + get { return kind > 0; } + } + public static implicit operator bool(Conversion conversion) { - return conversion.kind != 0; + return conversion.kind > 0; } #region Equals and GetHashCode implementation @@ -219,6 +248,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (toType == null) throw new ArgumentNullException("toType"); // C# 4.0 spec: §6.1 + Conversion c = StandardImplicitConversion(fromType, toType); + if (c) return c; + + return UserDefinedImplicitConversion(fromType, toType); + } + + public Conversion StandardImplicitConversion(IType fromType, IType toType) + { + if (fromType == null) + throw new ArgumentNullException("fromType"); + if (toType == null) + throw new ArgumentNullException("toType"); + // C# 4.0 spec: §6.3.1 if (IdentityConversion(fromType, toType)) return Conversion.IdentityConversion; if (ImplicitNumericConversion(fromType, toType)) @@ -231,39 +273,78 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return Conversion.ImplicitReferenceConversion; if (BoxingConversion(fromType, toType)) return Conversion.BoxingConversion; - if (ImplicitDynamicConversion(fromType, toType)) + if (fromType.Kind == TypeKind.Dynamic) return Conversion.ImplicitDynamicConversion; - if (ImplicitTypeParameterConversion(fromType, toType)) - return Conversion.ImplicitTypeParameterConversion; + if (ImplicitTypeParameterConversion(fromType, toType)) { + // Implicit type parameter conversions that aren't also + // reference conversions are considered to be boxing conversions + return Conversion.BoxingConversion; + } if (ImplicitPointerConversion(fromType, toType)) return Conversion.ImplicitPointerConversion; - return UserDefinedImplicitConversion(fromType, toType); + return Conversion.None; } + #endregion - public Conversion StandardImplicitConversion(IType fromType, IType toType) + #region ExplicitConversion + public Conversion ExplicitConversion(ResolveResult resolveResult, IType toType) + { + if (resolveResult == null) + throw new ArgumentNullException("resolveResult"); + if (toType == null) + throw new ArgumentNullException("toType"); + + if (resolveResult.Type.Kind == TypeKind.Dynamic) + return Conversion.ExplicitDynamicConversion; + Conversion c = ImplicitConversion(resolveResult, toType); + if (c) + return c; + else + return ExplicitConversionImpl(resolveResult.Type, toType); + } + + public Conversion ExplicitConversion(IType fromType, IType toType) { if (fromType == null) throw new ArgumentNullException("fromType"); if (toType == null) throw new ArgumentNullException("toType"); - // C# 4.0 spec: §6.3.1 - if (IdentityConversion(fromType, toType)) - return Conversion.IdentityConversion; - if (ImplicitNumericConversion(fromType, toType)) - return Conversion.ImplicitNumericConversion; - if (ImplicitNullableConversion(fromType, toType)) - return Conversion.ImplicitNullableConversion; - if (ImplicitReferenceConversion(fromType, toType)) - return Conversion.ImplicitReferenceConversion; - if (ImplicitTypeParameterConversion(fromType, toType)) - return Conversion.ImplicitTypeParameterConversion; - if (BoxingConversion(fromType, toType)) - return Conversion.BoxingConversion; - return Conversion.None; + + if (fromType.Kind == TypeKind.Dynamic) + return Conversion.ExplicitDynamicConversion; + Conversion c = ImplicitConversion(fromType, toType); + if (c) + return c; + else + return ExplicitConversionImpl(fromType, toType); + } + + Conversion ExplicitConversionImpl(IType fromType, IType toType) + { + // This method is called after we already checked for implicit conversions, + // so any remaining conversions must be explicit. + if (AnyNumericConversion(fromType, toType)) + return Conversion.ExplicitNumericConversion; + if (ExplicitEnumerationConversion(fromType, toType)) + return Conversion.ExplicitEnumerationConversion; + if (ExplicitNullableConversion(fromType, toType)) + return Conversion.ExplicitNullableConversion; + if (ExplicitReferenceConversion(fromType, toType)) + return Conversion.ExplicitReferenceConversion; + if (UnboxingConversion(fromType, toType)) + return Conversion.UnboxingConversion; + if (ExplicitTypeParameterConversion(fromType, toType)) { + // Explicit type parameter conversions that aren't also + // reference conversions are considered to be unboxing conversions + return Conversion.UnboxingConversion; + } + if (ExplicitPointerConversion(fromType, toType)) + return Conversion.ExplicitPointerConversion; + return UserDefinedExplicitConversion(fromType, toType); } #endregion - #region IdentityConversion + #region Identity Conversion /// /// Gets whether there is an identity conversion from to /// @@ -294,7 +375,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } #endregion - #region ImplicitNumericConversion + #region Numeric Conversions static readonly bool[,] implicitNumericConversionLookup = { // to: short ushort int uint long ulong // from: @@ -325,21 +406,45 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver && implicitNumericConversionLookup[from - TypeCode.Char, to - TypeCode.Int16]; } } + + bool IsNumericType(IType type) + { + TypeCode c = ReflectionHelper.GetTypeCode(type); + return c >= TypeCode.Char && c <= TypeCode.Decimal; + } + + bool AnyNumericConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.1.2 + §6.2.1 + return IsNumericType(fromType) && IsNumericType(toType); + } #endregion - #region ImplicitEnumerationConversion + #region Enumeration Conversions bool ImplicitEnumerationConversion(ResolveResult rr, IType toType) { // C# 4.0 spec: §6.1.3 + Debug.Assert(rr.IsCompileTimeConstant); TypeCode constantType = ReflectionHelper.GetTypeCode(rr.Type); if (constantType >= TypeCode.SByte && constantType <= TypeCode.Decimal && Convert.ToDouble(rr.ConstantValue) == 0) { return NullableType.GetUnderlyingType(toType).IsEnum(); } return false; } + + bool ExplicitEnumerationConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.2.2 + if (fromType.Kind == TypeKind.Enum) { + return toType.Kind == TypeKind.Enum || IsNumericType(toType); + } else if (IsNumericType(fromType)) { + return toType.Kind == TypeKind.Enum; + } + return false; + } #endregion - #region ImplicitNullableConversion + #region Nullable Conversions bool ImplicitNullableConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.1.4 @@ -351,9 +456,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return false; } } + + bool ExplicitNullableConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.1.4 + if (NullableType.IsNullable(toType) || NullableType.IsNullable(fromType)) { + IType t = NullableType.GetUnderlyingType(toType); + IType s = NullableType.GetUnderlyingType(fromType); + return IdentityConversion(s, t) || AnyNumericConversion(s, t) || ExplicitEnumerationConversion(s, t); + } else { + return false; + } + } #endregion - #region NullLiteralConversion + #region Null Literal Conversion bool NullLiteralConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.1.5 @@ -365,7 +482,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } #endregion - #region ImplicitReferenceConversion + #region Implicit Reference Conversion bool ImplicitReferenceConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.1.6 @@ -456,27 +573,48 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } #endregion - #region BoxingConversion + #region Explicit Reference Conversion + bool ExplicitReferenceConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §6.2.4 + + // reference conversions are possible only if both types are known to be reference types + if (!(fromType.IsReferenceType(context) == true && toType.IsReferenceType(context) == true)) + return false; + + // There's lots of additional rules, but they're not really relevant, + // as they are only used to identify invalid casts, and we don't care about reporting those. + return true; + } + #endregion + + #region Boxing Conversions bool BoxingConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.1.7 fromType = NullableType.GetUnderlyingType(fromType); - return fromType.IsReferenceType(context) == false && toType.IsReferenceType(context) == true && IsSubtypeOf(fromType, toType); + if (fromType.IsReferenceType(context) == false && toType.IsReferenceType(context) == true) + return IsSubtypeOf(fromType, toType); + else + return false; } - #endregion - #region ImplicitDynamicConversion - bool ImplicitDynamicConversion(IType fromType, IType toType) + bool UnboxingConversion(IType fromType, IType toType) { - // C# 4.0 spec: §6.1.8 - return SharedTypes.Dynamic.Equals(fromType); + // C# 4.0 spec: §6.2.5 + toType = NullableType.GetUnderlyingType(toType); + if (fromType.IsReferenceType(context) == true && toType.IsReferenceType(context) == false) + return IsSubtypeOf(toType, fromType); + else + return false; } #endregion - #region ImplicitConstantExpressionConversion + #region Implicit Constant-Expression Conversion bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType) { // C# 4.0 spec: §6.1.9 + Debug.Assert(rr.IsCompileTimeConstant); TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type); TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType)); if (fromTypeCode == TypeCode.Int64) { @@ -503,22 +641,30 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } #endregion - #region ImplicitTypeParameterConversion + #region Conversions involving type parameters /// /// Implicit conversions involving type parameters. /// bool ImplicitTypeParameterConversion(IType fromType, IType toType) { - ITypeParameter t = fromType as ITypeParameter; - if (t == null) + if (fromType.Kind != TypeKind.TypeParameter) return false; // not a type parameter - if (t.IsReferenceType(context) == true) + if (fromType.IsReferenceType(context) == true) return false; // already handled by ImplicitReferenceConversion - return IsSubtypeOf(t, toType); + return IsSubtypeOf(fromType, toType); + } + + bool ExplicitTypeParameterConversion(IType fromType, IType toType) + { + if (toType.Kind == TypeKind.TypeParameter) { + return fromType.Kind == TypeKind.TypeParameter || fromType.IsReferenceType(context) == true; + } else { + return fromType.Kind == TypeKind.TypeParameter && toType.Kind == TypeKind.Interface; + } } #endregion - #region ImplicitPointerConversion + #region Pointer Conversions bool ImplicitPointerConversion(IType fromType, IType toType) { // C# 4.0 spec: §18.4 Pointer conversions @@ -528,37 +674,120 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return true; return false; } + + bool ExplicitPointerConversion(IType fromType, IType toType) + { + // C# 4.0 spec: §18.4 Pointer conversions + if (fromType.Kind == TypeKind.Pointer) { + return toType.Kind == TypeKind.Pointer || IsIntegerType(toType); + } else { + return toType.Kind == TypeKind.Pointer && IsIntegerType(fromType); + } + } + + bool IsIntegerType(IType type) + { + TypeCode c = ReflectionHelper.GetTypeCode(type); + return c >= TypeCode.SByte && c <= TypeCode.UInt64; + } #endregion - #region UserDefinedImplicitConversion + #region User-Defined Conversions + /// + /// Gets whether type A is encompassed by type B. + /// + bool IsEncompassedBy(IType a, IType b) + { + return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface && StandardImplicitConversion(a, b); + } + + bool IsEncompassingOrEncompassedBy(IType a, IType b) + { + return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface + && (StandardImplicitConversion(a, b) || StandardImplicitConversion(b, a)); + } + Conversion UserDefinedImplicitConversion(IType fromType, IType toType) { // C# 4.0 spec §6.4.4 User-defined implicit conversions - // Currently we only test whether an applicable implicit conversion exists, - // we do not resolve which conversion is the most specific and gets used. + var operators = GetApplicableConversionOperators(fromType, toType, false); + // TODO: Find most specific conversion + if (operators.Count > 0) + return Conversion.UserDefinedImplicitConversion(operators[0].Method); + else + return Conversion.None; + } + + Conversion UserDefinedExplicitConversion(IType fromType, IType toType) + { + // C# 4.0 spec §6.4.5 User-defined implicit conversions + var operators = GetApplicableConversionOperators(fromType, toType, true); + // TODO: Find most specific conversion + if (operators.Count > 0) + return Conversion.UserDefinedExplicitConversion(operators[0].Method); + else + return Conversion.None; + } + + class OperatorInfo + { + public readonly IMethod Method; + public readonly IType SourceType; + public readonly IType TargetType; + public readonly bool IsLifted; + public OperatorInfo(IMethod method, IType sourceType, IType targetType, bool isLifted) + { + this.Method = method; + this.SourceType = sourceType; + this.TargetType = targetType; + this.IsLifted = isLifted; + } + } + + List GetApplicableConversionOperators(IType fromType, IType toType, bool isExplicit) + { // Find the candidate operators: - Predicate opImplicitFilter = m => m.IsStatic && m.IsOperator && m.Name == "op_Implicit" && m.Parameters.Count == 1; - var operators = NullableType.GetUnderlyingType(fromType).GetMethods(context, opImplicitFilter) - .Concat(NullableType.GetUnderlyingType(toType).GetMethods(context, opImplicitFilter)); + Predicate opFilter; + if (isExplicit) + opFilter = m => m.IsStatic && m.IsOperator && m.Name == "op_Explicit" && m.Parameters.Count == 1; + else + opFilter = m => m.IsStatic && m.IsOperator && m.Name == "op_Implicit" && m.Parameters.Count == 1; + + var operators = NullableType.GetUnderlyingType(fromType).GetMethods(context, opFilter) + .Concat(NullableType.GetUnderlyingType(toType).GetMethods(context, opFilter)).Distinct(); // Determine whether one of them is applicable: + List result = new List(); foreach (IMethod op in operators) { IType sourceType = op.Parameters[0].Type.Resolve(context); IType targetType = op.ReturnType.Resolve(context); // Try if the operator is applicable: - if (StandardImplicitConversion(fromType, sourceType) && StandardImplicitConversion(targetType, toType)) { - return Conversion.UserDefinedImplicitConversion(op); + bool isApplicable; + if (isExplicit) { + isApplicable = IsEncompassingOrEncompassedBy(fromType, sourceType) + && IsEncompassingOrEncompassedBy(targetType, toType); + } else { + isApplicable = IsEncompassedBy(fromType, sourceType) && IsEncompassedBy(targetType, toType); + } + if (isApplicable) { + result.Add(new OperatorInfo(op, sourceType, targetType, false)); } // Try if the operator is applicable in lifted form: if (sourceType.IsReferenceType(context) == false && targetType.IsReferenceType(context) == false) { IType liftedSourceType = NullableType.Create(sourceType, context); IType liftedTargetType = NullableType.Create(targetType, context); - if (StandardImplicitConversion(fromType, liftedSourceType) && StandardImplicitConversion(liftedTargetType, toType)) { - return Conversion.UserDefinedImplicitConversion(op); + if (isExplicit) { + isApplicable = IsEncompassingOrEncompassedBy(fromType, liftedSourceType) + && IsEncompassingOrEncompassedBy(liftedTargetType, toType); + } else { + isApplicable = IsEncompassedBy(fromType, liftedSourceType) && IsEncompassedBy(liftedTargetType, toType); + } + if (isApplicable) { + result.Add(new OperatorInfo(op, liftedSourceType, liftedTargetType, true)); } } } - return Conversion.None; + return result; } #endregion diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs index dfc152d701..dc8a19c309 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs @@ -18,6 +18,8 @@ using System; using System.Collections.Generic; +using System.Linq; + using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; @@ -97,5 +99,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public new IParameterizedMember Member { get { return (IParameterizedMember)base.Member; } } + + public override IEnumerable GetChildResults() + { + return base.GetChildResults().Concat(this.Arguments); + } } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs index daf8831121..1a1cbbff80 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs @@ -17,6 +17,8 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; +using System.Linq; using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -61,6 +63,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } + public ResolveResult TargetResult { + get { return targetResult; } + } + public IMember Member { get { return member; } } @@ -73,6 +79,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver get { return constantValue; } } + public override IEnumerable GetChildResults() + { + if (targetResult != null) + return new[] { targetResult }; + else + return Enumerable.Empty(); + } + public override string ToString() { return string.Format("[{0} {1}]", GetType().Name, member); diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs index fddda32d9e..85692eb9c8 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs @@ -163,5 +163,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } return or; } + + public override IEnumerable GetChildResults() + { + if (targetResult != null) + return new[] { targetResult }; + else + return Enumerable.Empty(); + } } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/OperatorResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/OperatorResolveResult.cs new file mode 100644 index 0000000000..3328fca06c --- /dev/null +++ b/ICSharpCode.NRefactory/CSharp/Resolver/OperatorResolveResult.cs @@ -0,0 +1,130 @@ +// 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 System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Resolve result representing a built-in unary operator. + /// (user-defined operators use InvocationResolveResult) + /// + public class UnaryOperatorResolveResult : ResolveResult + { + public readonly UnaryOperatorType Operator; + public readonly ResolveResult Input; + + public UnaryOperatorResolveResult(IType resultType, UnaryOperatorType op, ResolveResult input) + : base(resultType) + { + if (input == null) + throw new ArgumentNullException("input"); + this.Operator = op; + this.Input = input; + } + + public override IEnumerable GetChildResults() + { + return new [] { Input }; + } + } + + /// + /// Resolve result representing a built-in binary operator. + /// (user-defined operators use InvocationResolveResult) + /// + public class BinaryOperatorResolveResult : ResolveResult + { + public readonly BinaryOperatorType Operator; + public readonly ResolveResult Left; + public readonly ResolveResult Right; + + public BinaryOperatorResolveResult(IType resultType, ResolveResult lhs, BinaryOperatorType op, ResolveResult rhs) + : base(resultType) + { + if (lhs == null) + throw new ArgumentNullException("lhs"); + if (rhs == null) + throw new ArgumentNullException("rhs"); + this.Left = lhs; + this.Operator = op; + this.Right = rhs; + } + + public override IEnumerable GetChildResults() + { + return new [] { Left, Right }; + } + } + + /// + /// Resolve result representing the conditional operator. + /// + public class ConditionalOperatorResolveResult : ResolveResult + { + public readonly ResolveResult Condition; + public readonly ResolveResult True; + public readonly ResolveResult False; + + public ConditionalOperatorResolveResult(IType targetType, ResolveResult condition, ResolveResult @true, ResolveResult @false) + : base(targetType) + { + if (condition == null) + throw new ArgumentNullException("condition"); + if (@true == null) + throw new ArgumentNullException("true"); + if (@false == null) + throw new ArgumentNullException("false"); + this.Condition = condition; + this.True = @true; + this.False = @false; + } + + public override IEnumerable GetChildResults() + { + return new [] { Condition, True, False }; + } + } + + /// + /// Resolve result representing an array access. + /// + public class ArrayAccessResolveResult : ResolveResult + { + public readonly ResolveResult Array; + public readonly ResolveResult[] Indices; + + public ArrayAccessResolveResult(IType elementType, ResolveResult array, ResolveResult[] indices) : base(elementType) + { + if (array == null) + throw new ArgumentNullException("array"); + if (indices == null) + throw new ArgumentNullException("indices"); + this.Array = array; + this.Indices = indices; + } + + public override IEnumerable GetChildResults() + { + return new [] { Array }.Concat(Indices); + } + } +} diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveResult.cs index 97ba872759..9f31e05720 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveResult.cs @@ -17,6 +17,8 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; +using System.Linq; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; @@ -56,5 +58,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { return "[" + GetType().Name + " " + type + "]"; } + + public virtual IEnumerable GetChildResults() + { + return Enumerable.Empty(); + } } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs index b9d0dbf49e..432fec5bcf 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs @@ -765,7 +765,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { if (resolverEnabled) { ResolveResult rr = Resolve(directionExpression.Expression); - return new ByReferenceResolveResult(rr.Type, directionExpression.FieldDirection == FieldDirection.Out); + return new ByReferenceResolveResult(rr, directionExpression.FieldDirection == FieldDirection.Out); } else { ScanChildren(directionExpression); return null; diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 786db88365..17fb06ff5f 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -102,6 +102,8 @@ + + diff --git a/ICSharpCode.NRefactory/TypeSystem/PointerType.cs b/ICSharpCode.NRefactory/TypeSystem/PointerType.cs index 2811cc2252..ba70e36a8c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/PointerType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/PointerType.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.TypeSystem