diff --git a/ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs b/ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs
index 4f9edfc30..614893487 100644
--- a/ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs
+++ b/ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs
@@ -1297,25 +1297,28 @@ namespace ICSharpCode.Decompiler.Tests.Semantics
Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("i"));
}
+ ///
+ /// Creates a fake method named M with a single parameter of the given type.
+ ///
+ IMethod MakeUnaryMethod(Type parameterType)
+ {
+ var m = new FakeMethod(compilation, SymbolKind.Method);
+ m.Name = "M";
+ m.Parameters = new[] { new DefaultParameter(compilation.FindType(parameterType), "x", owner: m) };
+ return m;
+ }
+
[Test]
public void PreferUserDefinedConversionOverReferenceConversion()
{
// M(new AA()) with overloads M(object) and M(string), where AA has an implicit
// conversion to string, picks M(string) -- not because user-defined conversions
// are better, but because string is a better conversion target.
- IMethod MakeM(Type parameterType)
- {
- var m = new FakeMethod(compilation, SymbolKind.Method);
- m.Name = "M";
- m.Parameters = new[] { new DefaultParameter(compilation.FindType(parameterType), "x", owner: m) };
- return m;
- }
-
var or = new OverloadResolution(compilation, new[] {
new ResolveResult(compilation.FindType(typeof(UserDefinedConversionTestCases.ConvertibleToString)))
});
- IMethod mObject = MakeM(typeof(object));
- IMethod mString = MakeM(typeof(string));
+ IMethod mObject = MakeUnaryMethod(typeof(object));
+ IMethod mString = MakeUnaryMethod(typeof(string));
Assert.That(or.AddCandidate(mObject), Is.EqualTo(OverloadResolutionErrors.None));
Assert.That(or.AddCandidate(mString), Is.EqualTo(OverloadResolutionErrors.None));
Assert.That(!or.IsAmbiguous);
@@ -1328,13 +1331,22 @@ namespace ICSharpCode.Decompiler.Tests.Semantics
// Ambiguous conversions are a compiler error; but they are not
// preventing the overload from being chosen.
- // M(new AmbiguousA()) with overloads M(AmbiguousB) and M(object) picks
- // M(AmbiguousB) because AmbiguousB is a better conversion target than object,
- // even though the user-defined conversion itself is ambiguous (declared both
- // in AmbiguousA and AmbiguousB) and therefore invalid.
+ // The user-defined conversion AmbiguousA -> AmbiguousB is ambiguous (declared
+ // in both classes) and therefore invalid...
var c = ImplicitConversion(typeof(UserDefinedConversionTestCases.AmbiguousA), typeof(UserDefinedConversionTestCases.AmbiguousB));
Assert.That(c.IsUserDefined);
Assert.That(!c.IsValid);
+
+ // ...but M(new AmbiguousA()) with overloads M(AmbiguousB) and M(object) still
+ // picks M(AmbiguousB), because AmbiguousB is a better conversion target than object.
+ var or = new OverloadResolution(compilation, new[] {
+ new ResolveResult(compilation.FindType(typeof(UserDefinedConversionTestCases.AmbiguousA)))
+ });
+ IMethod mAmbiguousB = MakeUnaryMethod(typeof(UserDefinedConversionTestCases.AmbiguousB));
+ IMethod mObject = MakeUnaryMethod(typeof(object));
+ or.AddCandidate(mAmbiguousB);
+ or.AddCandidate(mObject);
+ Assert.That(or.BestCandidate, Is.SameAs(mAmbiguousB));
}
[Test]
diff --git a/ICSharpCode.Decompiler.Tests/Semantics/ExplicitConversionTest.cs b/ICSharpCode.Decompiler.Tests/Semantics/ExplicitConversionTest.cs
index 15266b783..85b735fe5 100644
--- a/ICSharpCode.Decompiler.Tests/Semantics/ExplicitConversionTest.cs
+++ b/ICSharpCode.Decompiler.Tests/Semantics/ExplicitConversionTest.cs
@@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.Semantics;
@@ -286,656 +287,416 @@ namespace ICSharpCode.Decompiler.Tests.Semantics
Assert.That(ExplicitConversion(typeof(Enum), typeof(int?)), Is.EqualTo(C.None));
}
- /* TODO: we should probably revive these tests somehow
- Conversion ResolveCast(string program)
+ ///
+ /// Converts a constant expression (e.g. an integer literal) to the target type.
+ ///
+ Conversion ExplicitConstantConversion(object value, Type to)
{
- return Resolve(program).Conversion;
+ IType fromType = compilation.FindType(value.GetType());
+ IType to2 = compilation.FindType(to).AcceptVisitor(new ConversionTest.ReplaceSpecialTypesVisitor());
+ return conversions.ExplicitConversion(new ConstantResolveResult(fromType, value), to2);
}
[Test]
public void ObjectToTypeParameter()
{
- string program = @"using System;
-class Test {
- public void M(object o) {
- T t = $(T)o$;
- }
-}";
- Assert.AreEqual(C.UnboxingConversion, ResolveCast(program));
+ // void M(object o) { T t = (T)o; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T");
+ Assert.That(conversions.ExplicitConversion(compilation.FindType(KnownTypeCode.Object), t), Is.EqualTo(C.UnboxingConversion));
}
[Test]
public void UnrelatedClassToTypeParameter()
{
- string program = @"using System;
-class Test {
- public void M(string o) {
- T t = $(T)o$;
- }
-}";
- Assert.AreEqual(C.None, ResolveCast(program));
+ // void M(string o) { T t = (T)o; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T");
+ Assert.That(conversions.ExplicitConversion(compilation.FindType(KnownTypeCode.String), t), Is.EqualTo(C.None));
}
[Test]
public void IntefaceToTypeParameter()
{
- string program = @"using System;
-class Test {
- public void M(IDisposable o) {
- T t = $(T)o$;
- }
-}";
- Assert.AreEqual(C.UnboxingConversion, ResolveCast(program));
+ // void M(IDisposable o) { T t = (T)o; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T");
+ Assert.That(conversions.ExplicitConversion(compilation.FindType(typeof(IDisposable)), t), Is.EqualTo(C.UnboxingConversion));
}
[Test]
public void TypeParameterToInterface()
{
- string program = @"using System;
-class Test {
- public void M(T t) {
- IDisposable d = $(IDisposable)t$;
- }
-}";
- Assert.AreEqual(C.BoxingConversion, ResolveCast(program));
+ // void M(T t) { IDisposable d = (IDisposable)t; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T");
+ Assert.That(conversions.ExplicitConversion(t, compilation.FindType(typeof(IDisposable))), Is.EqualTo(C.BoxingConversion));
}
[Test]
public void ValueTypeToTypeParameter()
{
- string program = @"using System;
-class Test {
- public void M(ValueType o) where T : struct {
- T t = $(T)o$;
- }
-}";
- Assert.AreEqual(C.UnboxingConversion, ResolveCast(program));
+ // void M(ValueType o) where T : struct { T t = (T)o; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T", hasValueTypeConstraint: true);
+ Assert.That(conversions.ExplicitConversion(compilation.FindType(typeof(ValueType)), t), Is.EqualTo(C.UnboxingConversion));
}
[Test]
public void InvalidTypeParameterConversion()
{
- string program = @"using System;
-class Test {
- public void M(T t) {
- U u = $(U)t$;
- }
-}";
- Assert.AreEqual(C.None, ResolveCast(program));
+ // void M(T t) { U u = (U)t; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T");
+ ITypeParameter u = new DefaultTypeParameter(compilation, SymbolKind.Method, 1, "U");
+ Assert.That(conversions.ExplicitConversion(t, u), Is.EqualTo(C.None));
}
[Test]
public void TypeParameterConversion1()
{
- string program = @"using System;
-class Test {
- public void M(T t) where T : U {
- U u = $(U)t$;
- }
-}";
- Assert.AreEqual(C.BoxingConversion, ResolveCast(program));
+ // void M(T t) where T : U { U u = (U)t; }
+ ITypeParameter u = new DefaultTypeParameter(compilation, SymbolKind.Method, 1, "U");
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T", constraints: new[] { u });
+ Assert.That(conversions.ExplicitConversion(t, u), Is.EqualTo(C.BoxingConversion));
}
[Test]
public void TypeParameterConversion1Array()
{
- string program = @"using System;
-class Test {
- public void M(T[] t) where T : U {
- U[] u = $(U[])t$;
- }
-}";
- Assert.AreEqual(C.None, ResolveCast(program));
+ // void M(T[] t) where T : U { U[] u = (U[])t; }
+ ITypeParameter u = new DefaultTypeParameter(compilation, SymbolKind.Method, 1, "U");
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T", constraints: new[] { u });
+ Assert.That(conversions.ExplicitConversion(new ArrayType(compilation, t), new ArrayType(compilation, u)), Is.EqualTo(C.None));
}
[Test]
public void TypeParameterConversion2()
{
- string program = @"using System;
-class Test {
- public void M(T t) where U : T {
- U u = $(U)t$;
- }
-}";
- Assert.AreEqual(C.UnboxingConversion, ResolveCast(program));
+ // void M(T t) where U : T { U u = (U)t; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T");
+ ITypeParameter u = new DefaultTypeParameter(compilation, SymbolKind.Method, 1, "U", constraints: new[] { t });
+ Assert.That(conversions.ExplicitConversion(t, u), Is.EqualTo(C.UnboxingConversion));
}
[Test]
public void TypeParameterConversion2Array()
{
- string program = @"using System;
-class Test {
- public void M(T[] t) where U : T {
- U[] u = $(U[])t$;
- }
-}";
- Assert.AreEqual(C.None, ResolveCast(program));
+ // void M(T[] t) where U : T { U[] u = (U[])t; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T");
+ ITypeParameter u = new DefaultTypeParameter(compilation, SymbolKind.Method, 1, "U", constraints: new[] { t });
+ Assert.That(conversions.ExplicitConversion(new ArrayType(compilation, t), new ArrayType(compilation, u)), Is.EqualTo(C.None));
}
[Test]
public void ImplicitTypeParameterConversionWithClassConstraint()
{
- string program = @"using System;
-class Test {
- public void M(T t) where T : class where U : class, T {
- U u = $(U)t$;
- }
-}";
- Assert.AreEqual(C.ExplicitReferenceConversion, ResolveCast(program));
+ // void M(T t) where T : class where U : class, T { U u = (U)t; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T", hasReferenceTypeConstraint: true);
+ ITypeParameter u = new DefaultTypeParameter(compilation, SymbolKind.Method, 1, "U", hasReferenceTypeConstraint: true, constraints: new[] { t });
+ Assert.That(conversions.ExplicitConversion(t, u), Is.EqualTo(C.ExplicitReferenceConversion));
}
[Test]
public void ImplicitTypeParameterArrayConversionWithClassConstraint()
{
- string program = @"using System;
-class Test {
- public void M(T[] t) where T : class where U : class, T {
- U[] u = $(U[])t$;
- }
-}";
- Assert.AreEqual(C.ExplicitReferenceConversion, ResolveCast(program));
+ // void M(T[] t) where T : class where U : class, T { U[] u = (U[])t; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T", hasReferenceTypeConstraint: true);
+ ITypeParameter u = new DefaultTypeParameter(compilation, SymbolKind.Method, 1, "U", hasReferenceTypeConstraint: true, constraints: new[] { t });
+ Assert.That(conversions.ExplicitConversion(new ArrayType(compilation, t), new ArrayType(compilation, u)), Is.EqualTo(C.ExplicitReferenceConversion));
}
[Test]
public void ImplicitTypeParameterConversionWithClassConstraintOnlyOnT()
{
- string program = @"using System;
-class Test {
- public void M(T t) where U : class, T {
- U u = $(U)t$;
- }
-}";
- Assert.AreEqual(C.ExplicitReferenceConversion, ResolveCast(program));
+ // void M(T t) where U : class, T { U u = (U)t; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T");
+ ITypeParameter u = new DefaultTypeParameter(compilation, SymbolKind.Method, 1, "U", hasReferenceTypeConstraint: true, constraints: new[] { t });
+ Assert.That(conversions.ExplicitConversion(t, u), Is.EqualTo(C.ExplicitReferenceConversion));
}
[Test]
public void ImplicitTypeParameterArrayConversionWithClassConstraintOnlyOnT()
{
- string program = @"using System;
-class Test {
- public void M(T[] t) where U : class, T {
- U[] u = $(U[])t$;
- }
-}";
- Assert.AreEqual(C.ExplicitReferenceConversion, ResolveCast(program));
+ // void M(T[] t) where U : class, T { U[] u = (U[])t; }
+ ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T");
+ ITypeParameter u = new DefaultTypeParameter(compilation, SymbolKind.Method, 1, "U", hasReferenceTypeConstraint: true, constraints: new[] { t });
+ Assert.That(conversions.ExplicitConversion(new ArrayType(compilation, t), new ArrayType(compilation, u)), Is.EqualTo(C.ExplicitReferenceConversion));
}
[Test]
public void SimpleUserDefinedConversion()
{
- var rr = Resolve(@"
-class C1 {}
-class C2 {
- public static explicit operator C1(C2 c2) {
- return null;
- }
-}
-class C {
- public void M() {
- var c2 = new C2();
- C1 c1 = $(C1)c2$;
- }
-}");
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("op_Explicit", rr.Conversion.Method.Name);
+ // C1 c1 = (C1)c2; with explicit operator C1(C2 c2)
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.SimpleSource),
+ typeof(UserDefinedExplicitConversionTestCases.SimpleTarget));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Name, Is.EqualTo("op_Explicit"));
}
[Test]
public void ExplicitReferenceConversionFollowedByUserDefinedConversion()
{
- var rr = Resolve(@"
- class B {}
- class S : B {}
- class T {
- public static explicit operator T(S s) { return null; }
- }
- class Test {
- void Run(B b) {
- T t = $(T)b$;
- }
- }");
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("B", rr.Input.Type.Name);
+ // class S : B, explicit operator T(S s);
+ // T t = (T)b; with b of type B needs the explicit reference conversion B -> S first
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.BaseClass),
+ typeof(UserDefinedExplicitConversionTestCases.TFromDerived));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
}
[Test]
public void ImplicitUserDefinedConversionFollowedByExplicitNumericConversion()
{
- var rr = Resolve(@"
- struct T {
- public static implicit operator float(T t) { return 0; }
- }
- class Test {
- void Run(T t) {
- int x = $(int)t$;
- }
- }");
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
+ // struct T { implicit operator float(T t) }
+ // int x = (int)t;
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.TImplicitToFloat), typeof(int));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
// even though the user-defined conversion is implicit, the combined conversion is explicit
- Assert.That(rr.Conversion.IsExplicit);
+ Assert.That(c.IsExplicit);
}
[Test]
public void BothDirectConversionAndBaseClassConversionAvailable()
{
- var rr = Resolve(@"
- class B {}
- class S : B {}
- class T {
- public static explicit operator T(S s) { return null; }
- public static explicit operator T(B b) { return null; }
- }
- class Test {
- void Run(B b) {
- T t = $(T)b$;
- }
- }");
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("b", rr.Conversion.Method.Parameters.Single().Name);
+ // class S : B, T with explicit operators from S ("s") and from B ("b");
+ // T t = (T)b; picks the operator from B
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.BaseClass),
+ typeof(UserDefinedExplicitConversionTestCases.TFromDerivedOrBase));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Parameters.Single().Name, Is.EqualTo("b"));
}
[Test]
public void UserDefinedExplicitConversion_PicksExactSourceTypeIfPossible()
{
- string program = @"using System;
-class Convertible {
- public static explicit operator Convertible(int i) {return new Convertible(); }
- public static explicit operator Convertible(short s) {return new Convertible(); }
-}
-class Test {
- public void M() {
- var a = $(Convertible)33$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name);
+ // explicit operators from int ("i") and short ("s");
+ // (Convertible)33
+ var c = ExplicitConstantConversion(33, typeof(UserDefinedExplicitConversionTestCases.ExplicitFromIntOrShort));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("i"));
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassedSourceTypeIfPossible()
{
- string program = @"using System;
-class Convertible {
- public static explicit operator Convertible(long l) {return new Convertible(); }
- public static explicit operator Convertible(uint ui) {return new Convertible(); }
-}
-class Test {
- public void M() {
- var a = $(Convertible)(ushort)33$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name);
+ // explicit operators from long ("l") and uint ("ui");
+ // (Convertible)(ushort)33
+ var c = ExplicitConstantConversion((ushort)33, typeof(UserDefinedExplicitConversionTestCases.ExplicitFromLongOrUInt));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("ui"));
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassingSourceType()
{
- string program = @"using System;
-class Convertible {
- public static explicit operator Convertible(int i) {return new Convertible(); }
- public static explicit operator Convertible(ushort us) {return new Convertible(); }
-}
-class Test {
- public void M() {
- var a = $(Convertible)(long)33$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name);
+ // explicit operators from int ("i") and ushort ("us");
+ // (Convertible)(long)33
+ var c = ExplicitConstantConversion((long)33, typeof(UserDefinedExplicitConversionTestCases.ExplicitFromIntOrUShort));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("i"));
}
[Test]
public void UserDefinedExplicitConversion_NoMostEncompassingSourceTypeIsInvalid()
{
- string program = @"using System;
-class Convertible {
- public static explicit operator Convertible(uint i) {return new Convertible(); }
- public static explicit operator Convertible(short us) {return new Convertible(); }
-}
-class Test {
- public void M() {
- var a = $(Convertible)(long)33$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(!rr.Conversion.IsValid);
+ // explicit operators from uint and short; neither source type encompasses the
+ // other, so the conversion from long is ambiguous.
+ var c = ExplicitConstantConversion((long)33, typeof(UserDefinedExplicitConversionTestCases.ExplicitFromUIntOrShort));
+ Assert.That(!c.IsValid);
}
[Test]
public void UserDefinedExplicitConversion_PicksExactTargetTypeIfPossible()
{
- string program = @"using System;
-class Convertible {
- public static explicit operator int(Convertible i) {return 0; }
- public static explicit operator short(Convertible s) {return 0; }
-}
-class Test {
- public void M() {
- var a = $(int)new Convertible()$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name);
+ // explicit operators to int ("i") and short ("s");
+ // (int)new Convertible()
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.ExplicitToIntOrShort), typeof(int));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("i"));
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassingTargetTypeIfPossible()
{
- string program = @"using System;
-class Convertible {
- public static explicit operator int(Convertible i) {return 0; }
- public static explicit operator ushort(Convertible us) {return 0; }
-}
-class Test {
- public void M() {
- var a = $(ulong)new Convertible()$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("us", rr.Conversion.Method.Parameters[0].Name);
+ // explicit operators to int ("i") and ushort ("us");
+ // (ulong)new Convertible()
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.ExplicitToIntOrUShort), typeof(ulong));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("us"));
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassedTargetType()
{
- string program = @"using System;
-class Convertible {
- public static explicit operator long(Convertible l) { return 0; }
- public static explicit operator uint(Convertible ui) { return 0; }
-}
-class Test {
- public void M() {
- var a = $(ushort)new Convertible()$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name);
+ // explicit operators to long ("l") and uint ("ui");
+ // (ushort)new Convertible()
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.ExplicitToLongOrUInt), typeof(ushort));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("ui"));
}
[Test]
public void UserDefinedExplicitConversion_NoMostEncompassedTargetTypeIsInvalid()
{
- string program = @"using System;
-class Convertible {
- public static explicit operator ulong(Convertible l) { return 0; }
- public static explicit operator int(Convertible ui) { return 0; }
-}
-class Test {
- public void M() {
- var a = $(ushort)new Convertible()$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(!rr.Conversion.IsValid);
+ // explicit operators to ulong and int; neither target type encompasses the
+ // other, so the conversion to ushort is ambiguous.
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.ExplicitToULongOrInt), typeof(ushort));
+ Assert.That(!c.IsValid);
}
[Test]
public void UserDefinedExplicitConversion_AmbiguousIsInvalid()
{
- string program = @"using System;
-class Convertible1 {
- public static explicit operator Convertible2(Convertible1 c) {return 0; }
-}
-class Convertible2 {
- public static explicit operator Convertible2(Convertible1 c) {return 0; }
-}
-class Test {
- public void M() {
- var a = $(Convertible2)new Convertible1()$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(!rr.Conversion.IsValid);
+ // Both ExplicitAmbiguousA and ExplicitAmbiguousB declare
+ // explicit operator ExplicitAmbiguousB(ExplicitAmbiguousA).
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.ExplicitAmbiguousA),
+ typeof(UserDefinedExplicitConversionTestCases.ExplicitAmbiguousB));
+ Assert.That(!c.IsValid);
}
[Test]
public void UserDefinedExplicitConversion_Lifted()
{
- string program = @"using System;
-struct Convertible {
- public static explicit operator Convertible(int i) {return new Convertible(); }
-}
-class Test {
- public void M(int? i) {
- a = $(Convertible?)i$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.That(rr.Conversion.IsLifted);
+ // struct Convertible { explicit operator Convertible(int i) }
+ // (Convertible?)i with i of type int?
+ var c = ExplicitConversion(typeof(int?), typeof(UserDefinedExplicitConversionTestCases.ExplicitFromInt?));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.IsLifted);
}
[Test]
public void UserDefinedExplicitConversionFollowedByImplicitNullableConversion()
{
- string program = @"using System;
-struct Convertible {
- public static explicit operator Convertible(int i) {return new Convertible(); }
-}
-class Test {
- public void M(int i) {
- a = $(Convertible?)i$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.That(!rr.Conversion.IsLifted);
+ // struct Convertible { explicit operator Convertible(int i) }
+ // (Convertible?)i with i of type int
+ var c = ExplicitConversion(typeof(int), typeof(UserDefinedExplicitConversionTestCases.ExplicitFromInt?));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(!c.IsLifted);
}
[Test]
public void UserDefinedExplicitConversion_ExplicitNullable_ThenUserDefined()
{
- string program = @"using System;
-struct Convertible {
- public static explicit operator Convertible(int i) {return new Convertible(); }
- public static explicit operator Convertible?(int? ni) {return new Convertible(); }
-}
-class Test {
- public void M(int? i) {
- a = $(Convertible)i$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.That(!rr.Conversion.IsLifted);
- Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name);
+ // struct Convertible with explicit operators from int ("i") and from int? ("ni");
+ // (Convertible)i with i of type int? unwraps the nullable and uses the int operator
+ var c = ExplicitConversion(typeof(int?), typeof(UserDefinedExplicitConversionTestCases.ExplicitNullableConvertible));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(!c.IsLifted);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("i"));
}
[Test]
public void UserDefinedExplicitConversion_DefinedNullableTakesPrecedenceOverLifted()
{
- string program = @"using System;
-struct Convertible {
- public static explicit operator Convertible(int i) {return new Convertible(); }
- public static explicit operator Convertible?(int? ni) {return new Convertible(); }
-}
-class Test {
- public void M() {
- a = $(Convertible?)(int?)33$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.That(!rr.Conversion.IsLifted);
- Assert.AreEqual("ni", rr.Conversion.Method.Parameters[0].Name);
+ // struct Convertible with explicit operators from int ("i") and from int? ("ni");
+ // (Convertible?)(int?)33 -- the user-defined nullable operator wins over the
+ // lifted form of the int operator.
+ var c = ExplicitConversion(typeof(int?), typeof(UserDefinedExplicitConversionTestCases.ExplicitNullableConvertible?));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(!c.IsLifted);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("ni"));
}
[Test]
public void UserDefinedExplicitConversion_UIntConstant()
{
- string program = @"using System;
-class Convertible {
- public static explicit operator Convertible(long l) {return new Convertible(); }
- public static explicit operator Convertible(uint ui) {return new Convertible(); }
-}
-class Test {
- public void M() {
- var a = $(Convertible)33$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name);
+ // explicit operators from long ("l") and uint ("ui");
+ // (Convertible)33 -- the constant 33 converts to uint, which is more specific
+ var c = ExplicitConstantConversion(33, typeof(UserDefinedExplicitConversionTestCases.ExplicitFromLongOrUInt));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("ui"));
}
[Test]
public void UserDefinedExplicitConversion_NullableUIntConstant()
{
- string program = @"using System;
-class Convertible {
- public static explicit operator Convertible(long? l) {return new Convertible(); }
- public static explicit operator Convertible(uint? ui) {return new Convertible(); }
-}
-class Test {
- public void M() {
- Convertible a = $(Convertible)33$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name);
+ // explicit operators from long? ("l") and uint? ("ui");
+ // (Convertible)33
+ var c = ExplicitConstantConversion(33, typeof(UserDefinedExplicitConversionTestCases.ExplicitFromNullableLongOrNullableUInt));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("ui"));
}
[Test]
public void UseDefinedExplicitConversion_Lifted()
{
- string program = @"
-struct Convertible {
- public static explicit operator Convertible(int i) { return new Convertible(); }
-}
-class Test {
- public void M(int? i) {
- a = $(Convertible?)i$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.That(rr.Conversion.IsLifted);
- Assert.That(rr.Input is LocalResolveResult);
+ // Same conversion as UserDefinedExplicitConversion_Lifted, but through the
+ // ResolveResult-based entry point (the original test cast a local variable).
+ var c = conversions.ExplicitConversion(
+ new ResolveResult(compilation.FindType(typeof(int?))),
+ compilation.FindType(typeof(UserDefinedExplicitConversionTestCases.ExplicitFromInt?)));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.IsLifted);
}
[Test]
public void UserDefinedExplicitConversion_Short_Or_NullableByte_Target()
{
- string program = @"using System;
-class Test {
- public static explicit operator short(Test s) { return 0; }
- public static explicit operator byte?(Test b) { return 0; }
-}
-class Program {
- public static void Main(string[] args)
- {
- int? x = $(int?)new Test()$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("System.Int16", rr.Conversion.Method.ReturnType.FullName);
+ // explicit operators to short ("s") and byte? ("b");
+ // (int?)new Test()
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.ExplicitToShortOrNullableByte), typeof(int?));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.ReturnType.FullName, Is.EqualTo("System.Int16"));
}
[Test]
public void UserDefinedExplicitConversion_Byte_Or_NullableShort_Target()
{
- string program = @"using System;
-class Test {
- public static explicit operator byte(Test b) { return 0; }
- public static explicit operator short?(Test s) { return 0; }
-}
-class Program {
- public static void Main(string[] args)
- {
- int? x = $(int?)new Test()$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("s", rr.Conversion.Method.Parameters[0].Name);
+ // explicit operators to byte ("b") and short? ("s");
+ // (int?)new Test()
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.ExplicitToByteOrNullableShort), typeof(int?));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("s"));
}
[Test]
public void ExplicitConversionOperatorsCanOverrideApplicableImplicitOnes()
{
- string program = @"
-struct Convertible {
- public static explicit operator int(Convertible ci) {return 0; }
- public static implicit operator short(Convertible cs) {return 0; }
-}
-class Test {
- static void Main() {
- int i = $(int)new Convertible()$; // csc uses the explicit conversion operator
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.IsUserDefined);
- Assert.AreEqual("ci", rr.Conversion.Method.Parameters[0].Name);
+ // struct Convertible { explicit operator int(Convertible ci); implicit operator short(Convertible cs); }
+ // int i = (int)new Convertible(); -- csc uses the explicit conversion operator
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.ExplicitIntImplicitShort), typeof(int));
+ Assert.That(c.IsValid);
+ Assert.That(c.IsUserDefined);
+ Assert.That(c.Method.Parameters[0].Name, Is.EqualTo("ci"));
}
[Test]
public void UserDefinedExplicitConversion_ConversionBeforeUserDefinedOperatorIsCorrect()
{
- string program = @"using System;
-class Convertible {
- public static implicit operator Convertible(int l) {return new Convertible(); }
-}
-class Test {
- public void M() {
- long i = 33;
- Convertible a = $(Convertible)i$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.ConversionBeforeUserDefinedOperator.IsValid);
- Assert.That(rr.Conversion.ConversionBeforeUserDefinedOperator.IsExplicit);
- Assert.That(rr.Conversion.ConversionBeforeUserDefinedOperator.IsNumericConversion);
- Assert.That(rr.Conversion.ConversionAfterUserDefinedOperator.IsIdentityConversion);
+ // implicit operator Convertible(int l); casting a long goes long -> int -> Convertible,
+ // with an explicit numeric conversion before the operator.
+ var c = ExplicitConversion(typeof(long), typeof(UserDefinedExplicitConversionTestCases.ImplicitFromInt));
+ Assert.That(c.IsValid);
+ Assert.That(c.ConversionBeforeUserDefinedOperator.IsValid);
+ Assert.That(c.ConversionBeforeUserDefinedOperator.IsExplicit);
+ Assert.That(c.ConversionBeforeUserDefinedOperator.IsNumericConversion);
+ Assert.That(c.ConversionAfterUserDefinedOperator.IsIdentityConversion);
}
[Test]
public void UserDefinedExplicitConversion_ConversionAfterUserDefinedOperatorIsCorrect()
{
- string program = @"using System;
-class Convertible {
- public static implicit operator long(Convertible i) {return 0; }
-}
-class Test {
- public void M() {
- int a = $(int)new Convertible()$;
- }
-}";
- var rr = Resolve(program);
- Assert.That(rr.Conversion.IsValid);
- Assert.That(rr.Conversion.ConversionBeforeUserDefinedOperator.IsIdentityConversion);
- Assert.That(rr.Conversion.ConversionAfterUserDefinedOperator.IsValid);
- Assert.That(rr.Conversion.ConversionAfterUserDefinedOperator.IsExplicit);
- Assert.That(rr.Conversion.ConversionAfterUserDefinedOperator.IsNumericConversion);
- }*/
+ // implicit operator long(Convertible i); casting to int goes Convertible -> long -> int,
+ // with an explicit numeric conversion after the operator.
+ var c = ExplicitConversion(typeof(UserDefinedExplicitConversionTestCases.ImplicitToLong), typeof(int));
+ Assert.That(c.IsValid);
+ Assert.That(c.ConversionBeforeUserDefinedOperator.IsIdentityConversion);
+ Assert.That(c.ConversionAfterUserDefinedOperator.IsValid);
+ Assert.That(c.ConversionAfterUserDefinedOperator.IsExplicit);
+ Assert.That(c.ConversionAfterUserDefinedOperator.IsNumericConversion);
+ }
}
}
diff --git a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemTestCase.cs b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemTestCase.cs
index 92bf4fd83..faf96bdb1 100644
--- a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemTestCase.cs
+++ b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemTestCase.cs
@@ -965,6 +965,252 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
}
}
+ ///
+ /// Fixtures for the ExplicitConversionsTest.UserDefined* tests: types with user-defined
+ /// (mostly explicit) conversion operators. Operator parameter names are significant,
+ /// several tests assert which operator was chosen by its parameter name.
+ ///
+ public static class UserDefinedExplicitConversionTestCases
+ {
+ public class SimpleTarget { }
+
+ public class SimpleSource
+ {
+ public static explicit operator SimpleTarget(SimpleSource c2)
+ {
+ return null;
+ }
+ }
+
+ public class BaseClass { }
+
+ public class DerivedClass : BaseClass { }
+
+ public class TFromDerived
+ {
+ public static explicit operator TFromDerived(DerivedClass s)
+ {
+ return null;
+ }
+ }
+
+ public struct TImplicitToFloat
+ {
+ public static implicit operator float(TImplicitToFloat t)
+ {
+ return 0;
+ }
+ }
+
+ public class TFromDerivedOrBase
+ {
+ public static explicit operator TFromDerivedOrBase(DerivedClass s)
+ {
+ return null;
+ }
+ public static explicit operator TFromDerivedOrBase(BaseClass b)
+ {
+ return null;
+ }
+ }
+
+ public class ExplicitFromIntOrShort
+ {
+ public static explicit operator ExplicitFromIntOrShort(int i)
+ {
+ return new ExplicitFromIntOrShort();
+ }
+ public static explicit operator ExplicitFromIntOrShort(short s)
+ {
+ return new ExplicitFromIntOrShort();
+ }
+ }
+
+ public class ExplicitFromLongOrUInt
+ {
+ public static explicit operator ExplicitFromLongOrUInt(long l)
+ {
+ return new ExplicitFromLongOrUInt();
+ }
+ public static explicit operator ExplicitFromLongOrUInt(uint ui)
+ {
+ return new ExplicitFromLongOrUInt();
+ }
+ }
+
+ public class ExplicitFromIntOrUShort
+ {
+ public static explicit operator ExplicitFromIntOrUShort(int i)
+ {
+ return new ExplicitFromIntOrUShort();
+ }
+ public static explicit operator ExplicitFromIntOrUShort(ushort us)
+ {
+ return new ExplicitFromIntOrUShort();
+ }
+ }
+
+ public class ExplicitFromUIntOrShort
+ {
+ public static explicit operator ExplicitFromUIntOrShort(uint i)
+ {
+ return new ExplicitFromUIntOrShort();
+ }
+ public static explicit operator ExplicitFromUIntOrShort(short us)
+ {
+ return new ExplicitFromUIntOrShort();
+ }
+ }
+
+ public class ExplicitToIntOrShort
+ {
+ public static explicit operator int(ExplicitToIntOrShort i)
+ {
+ return 0;
+ }
+ public static explicit operator short(ExplicitToIntOrShort s)
+ {
+ return 0;
+ }
+ }
+
+ public class ExplicitToIntOrUShort
+ {
+ public static explicit operator int(ExplicitToIntOrUShort i)
+ {
+ return 0;
+ }
+ public static explicit operator ushort(ExplicitToIntOrUShort us)
+ {
+ return 0;
+ }
+ }
+
+ public class ExplicitToLongOrUInt
+ {
+ public static explicit operator long(ExplicitToLongOrUInt l)
+ {
+ return 0;
+ }
+ public static explicit operator uint(ExplicitToLongOrUInt ui)
+ {
+ return 0;
+ }
+ }
+
+ public class ExplicitToULongOrInt
+ {
+ public static explicit operator ulong(ExplicitToULongOrInt l)
+ {
+ return 0;
+ }
+ public static explicit operator int(ExplicitToULongOrInt ui)
+ {
+ return 0;
+ }
+ }
+
+ public class ExplicitAmbiguousA
+ {
+ public static explicit operator ExplicitAmbiguousB(ExplicitAmbiguousA c)
+ {
+ return null;
+ }
+ }
+
+ public class ExplicitAmbiguousB
+ {
+ public static explicit operator ExplicitAmbiguousB(ExplicitAmbiguousA c)
+ {
+ return null;
+ }
+ }
+
+ public struct ExplicitFromInt
+ {
+ public static explicit operator ExplicitFromInt(int i)
+ {
+ return default(ExplicitFromInt);
+ }
+ }
+
+ public struct ExplicitNullableConvertible
+ {
+ public static explicit operator ExplicitNullableConvertible(int i)
+ {
+ return default(ExplicitNullableConvertible);
+ }
+ public static explicit operator ExplicitNullableConvertible?(int? ni)
+ {
+ return default(ExplicitNullableConvertible);
+ }
+ }
+
+ public class ExplicitFromNullableLongOrNullableUInt
+ {
+ public static explicit operator ExplicitFromNullableLongOrNullableUInt(long? l)
+ {
+ return new ExplicitFromNullableLongOrNullableUInt();
+ }
+ public static explicit operator ExplicitFromNullableLongOrNullableUInt(uint? ui)
+ {
+ return new ExplicitFromNullableLongOrNullableUInt();
+ }
+ }
+
+ public class ExplicitToShortOrNullableByte
+ {
+ public static explicit operator short(ExplicitToShortOrNullableByte s)
+ {
+ return 0;
+ }
+ public static explicit operator byte?(ExplicitToShortOrNullableByte b)
+ {
+ return 0;
+ }
+ }
+
+ public class ExplicitToByteOrNullableShort
+ {
+ public static explicit operator byte(ExplicitToByteOrNullableShort b)
+ {
+ return 0;
+ }
+ public static explicit operator short?(ExplicitToByteOrNullableShort s)
+ {
+ return 0;
+ }
+ }
+
+ public struct ExplicitIntImplicitShort
+ {
+ public static explicit operator int(ExplicitIntImplicitShort ci)
+ {
+ return 0;
+ }
+ public static implicit operator short(ExplicitIntImplicitShort cs)
+ {
+ return 0;
+ }
+ }
+
+ public class ImplicitFromInt
+ {
+ public static implicit operator ImplicitFromInt(int l)
+ {
+ return new ImplicitFromInt();
+ }
+ }
+
+ public class ImplicitToLong
+ {
+ public static implicit operator long(ImplicitToLong i)
+ {
+ return 0;
+ }
+ }
+ }
+
///
/// Fixture for ConversionTest.ExpansiveInheritance: a contravariant interface whose
/// implementing interface expands the type argument recursively.