diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs index 9c2898592c..5923b3390a 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs @@ -218,8 +218,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (toType == null) throw new ArgumentNullException("toType"); - if (fromType.Kind == TypeKind.Dynamic) - return Conversion.ExplicitDynamicConversion; Conversion c = ImplicitConversion(fromType, toType); if (c.IsValid) return c; @@ -236,23 +234,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (ExplicitEnumerationConversion(fromType, toType)) return Conversion.EnumerationConversion(false, false); Conversion c = ExplicitNullableConversion(fromType, toType); - if (c.IsValid) - return c; - c = UserDefinedExplicitConversion(fromType, toType); if (c.IsValid) return c; 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; - } + c = ExplicitTypeParameterConversion(fromType, toType); + if (c.IsValid) + return c; if (ExplicitPointerConversion(fromType, toType)) return Conversion.ExplicitPointerConversion; - return Conversion.None; + return UserDefinedExplicitConversion(fromType, toType); } #endregion @@ -429,17 +422,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } // conversion from single-dimensional array S[] to IList: ParameterizedType toPT = toType as ParameterizedType; - if (fromArray.Dimensions == 1 && toPT != null && toPT.TypeParameterCount == 1 - && toPT.Namespace == "System.Collections.Generic" - && (toPT.Name == "IList" || toPT.Name == "ICollection" || toPT.Name == "IEnumerable" || toPT.Name == "IReadOnlyList")) - { - // array covariance plays a part here as well (string[] is IList) - return IdentityConversion(fromArray.ElementType, toPT.GetTypeArgument(0)) - || ImplicitReferenceConversion(fromArray.ElementType, toPT.GetTypeArgument(0), subtypeCheckNestingDepth); + if (fromArray.Dimensions == 1 && toPT != null) { + KnownTypeCode tc = toPT.GetDefinition().KnownTypeCode; + if (tc == KnownTypeCode.IListOfT || tc == KnownTypeCode.ICollectionOfT || tc == KnownTypeCode.IEnumerableOfT || tc == KnownTypeCode.IReadOnlyListOfT) { + // array covariance plays a part here as well (string[] is IList) + return IdentityConversion(fromArray.ElementType, toPT.GetTypeArgument(0)) + || ImplicitReferenceConversion(fromArray.ElementType, toPT.GetTypeArgument(0), subtypeCheckNestingDepth); + } } // conversion from any array to System.Array and the interfaces it implements: IType systemArray = compilation.FindType(KnownTypeCode.Array); - return systemArray.Kind != TypeKind.Unknown && (systemArray.Equals(toType) || ImplicitReferenceConversion(systemArray, toType, subtypeCheckNestingDepth)); + return ImplicitReferenceConversion(systemArray, toType, subtypeCheckNestingDepth); } // now comes the hard part: traverse the inheritance chain and figure out generics+variance @@ -516,13 +509,87 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { // C# 4.0 spec: §6.2.4 - // reference conversions are possible only if both types are known to be reference types - if (!(fromType.IsReferenceType == true && toType.IsReferenceType == true)) + // test that the types are reference types: + if (toType.IsReferenceType != true) + return false; + if (fromType.IsReferenceType != true) { + // special case: + // converting from F to T is a reference conversion where T : class, F + // (because F actually must be a reference type as well, even though C# doesn't treat it as one) + if (fromType.Kind == TypeKind.TypeParameter) + return IsSubtypeOf(toType, fromType, 0); 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 currently don't care about reporting those. - return true; + if (toType.Kind == TypeKind.Array) { + ArrayType toArray = (ArrayType)toType; + if (fromType.Kind == TypeKind.Array) { + // Array covariance + ArrayType fromArray = (ArrayType)fromType; + if (fromArray.Dimensions != toArray.Dimensions) + return false; + return ExplicitReferenceConversion(fromArray.ElementType, toArray.ElementType); + } + ParameterizedType pt = fromType as ParameterizedType; + if (pt != null && toArray.Dimensions == 1) { + KnownTypeCode tc = pt.GetDefinition().KnownTypeCode; + if (tc == KnownTypeCode.IListOfT || tc == KnownTypeCode.ICollectionOfT || tc == KnownTypeCode.IEnumerableOfT || tc == KnownTypeCode.IReadOnlyListOfT) { + return ExplicitReferenceConversion(pt.GetTypeArgument(0), toArray.ElementType) + || IdentityConversion(pt.GetTypeArgument(0), toArray.ElementType); + } + } + // Otherwise treat the array like a sealed class - require implicit conversion in the opposite direction + return IsImplicitReferenceConversion(toType, fromType); + } else if (fromType.Kind == TypeKind.Delegate && toType.Kind == TypeKind.Delegate) { + ITypeDefinition def = fromType.GetDefinition(); + if (def == null || !def.Equals(toType.GetDefinition())) + return false; + ParameterizedType ps = fromType as ParameterizedType; + ParameterizedType pt = toType as ParameterizedType; + if (ps == null || pt == null) { + // non-generic delegate - return true for the identity conversion + return ps == null && pt == null; + } + for (int i = 0; i < def.TypeParameters.Count; i++) { + IType si = ps.GetTypeArgument(i); + IType ti = pt.GetTypeArgument(i); + if (IdentityConversion(si, ti)) + continue; + ITypeParameter xi = def.TypeParameters[i]; + switch (xi.Variance) { + case VarianceModifier.Covariant: + if (!ExplicitReferenceConversion(si, ti)) + return false; + break; + case VarianceModifier.Contravariant: + if (!(si.IsReferenceType == true && ti.IsReferenceType == true)) + return false; + break; + default: + return false; + } + } + return true; + } else if (IsSealedReferenceType(fromType) || fromType.Kind == TypeKind.Array) { + // If the source type is sealed, explicit conversions can't do anything more than implicit ones + return IsImplicitReferenceConversion(fromType, toType); + } else if (IsSealedReferenceType(toType)) { + // The the target type is sealed, there must be an implicit conversion in the opposite direction + return IsImplicitReferenceConversion(toType, fromType); + } else { + if (fromType.Kind == TypeKind.Interface || toType.Kind == TypeKind.Interface) + return true; + else + return IsImplicitReferenceConversion(toType, fromType) + || IsImplicitReferenceConversion(fromType, toType); + } + } + + bool IsSealedReferenceType(IType type) + { + TypeKind kind = type.Kind; + return kind == TypeKind.Class && type.GetDefinition().IsSealed + || kind == TypeKind.Delegate || kind == TypeKind.Anonymous; } #endregion @@ -592,13 +659,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return IsSubtypeOf(fromType, toType, 0); } - bool ExplicitTypeParameterConversion(IType fromType, IType toType) + Conversion ExplicitTypeParameterConversion(IType fromType, IType toType) { if (toType.Kind == TypeKind.TypeParameter) { - return fromType.Kind == TypeKind.TypeParameter || fromType.IsReferenceType == true; + // Explicit type parameter conversions that aren't also + // reference conversions are considered to be unboxing conversions + if (fromType.Kind == TypeKind.Interface || IsSubtypeOf(toType, fromType, 0)) + return Conversion.UnboxingConversion; } else { - return fromType.Kind == TypeKind.TypeParameter && toType.Kind == TypeKind.Interface; + if (fromType.Kind == TypeKind.TypeParameter && toType.Kind == TypeKind.Interface) + return Conversion.BoxingConversion; } + return Conversion.None; } #endregion diff --git a/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs b/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs index beea0ac218..88ee1489c2 100644 --- a/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs +++ b/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs @@ -57,7 +57,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck solution.AllFiles.Count(), solution.Projects.Count); - VisitorBenchmark.Run(solution.AllFiles.Select(f => f.SyntaxTree)); + //VisitorBenchmark.Run(solution.AllFiles.Select(f => f.SyntaxTree)); using (new Timer("ID String test... ")) TypeSystemTests.IDStringConsistencyCheck(solution); using (new Timer("Resolve unresolved members... ")) TypeSystemTests.ResolvedUnresolvedMembers(solution); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs index a5b6657138..4702395891 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs @@ -50,13 +50,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return conversions.ImplicitConversion(from2, to2); } - Conversion ExplicitConversion(Type from, Type to) - { - IType from2 = compilation.FindType(from); - IType to2 = compilation.FindType(to); - return conversions.ExplicitConversion(from2, to2); - } - [Test] public void IdentityConversions() { @@ -105,6 +98,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Assert.AreEqual(C.ImplicitNumericConversion, ImplicitConversion(typeof(uint), typeof(long))); } + [Test] + public void EnumerationConversion() + { + ResolveResult zero = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 0); + ResolveResult one = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 1); + C implicitEnumerationConversion = C.EnumerationConversion(true, false); + Assert.AreEqual(implicitEnumerationConversion, conversions.ImplicitConversion(zero, compilation.FindType(typeof(StringComparison)))); + Assert.AreEqual(C.None, conversions.ImplicitConversion(one, compilation.FindType(typeof(StringComparison)))); + } + [Test] public void NullableConversions() { @@ -164,7 +167,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver [Test] public void ConversionFromDynamic() { - // There is no conversion from the type 'dynamic' to other types (except object). + // There is no conversion from the type 'dynamic' to other types (except the identity conversion to object). // Such conversions only exists from dynamic expression. // This is an important distinction for type inference (see TypeInferenceTests.IEnumerableCovarianceWithDynamic) Assert.AreEqual(C.None, ImplicitConversion(typeof(dynamic), typeof(string))); @@ -243,16 +246,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver 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() { @@ -523,26 +516,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Assert.IsFalse(conversions.ImplicitConversion(type1, type2).IsValid); } - [Test] - public void ExplicitUserDefinedConversion() - { - 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.IsTrue(rr.Conversion.IsUserDefined); - Assert.AreEqual("op_Explicit", rr.Conversion.Method.Name); - } - [Test] public void ImplicitTypeParameterConversion() { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs new file mode 100644 index 0000000000..307375ac9f --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs @@ -0,0 +1,454 @@ +// 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 NUnit.Framework; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Semantics; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + using dynamic = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.Dynamic; + using C = Conversion; + + [TestFixture] + public class ExplicitConversionsTest : ResolverTestBase + { + CSharpConversions conversions; + + public override void SetUp() + { + base.SetUp(); + conversions = new CSharpConversions(compilation); + } + + Conversion ExplicitConversion(Type from, Type to) + { + IType from2 = compilation.FindType(from); + IType to2 = compilation.FindType(to); + return conversions.ExplicitConversion(from2, to2); + } + + [Test] + public void PointerConversion() + { + 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 ConversionFromDynamic() + { + // Explicit dynamic conversion is for resolve results only; + // otherwise it's an explicit reference / unboxing conversion + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(dynamic), typeof(string))); + Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(dynamic), typeof(int))); + + var dynamicRR = new ResolveResult(SpecialType.Dynamic); + Assert.AreEqual(C.ExplicitDynamicConversion, conversions.ExplicitConversion(dynamicRR, compilation.FindType(typeof(string)))); + Assert.AreEqual(C.ExplicitDynamicConversion, conversions.ExplicitConversion(dynamicRR, compilation.FindType(typeof(int)))); + } + + [Test] + public void NumericConversions() + { + Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(sbyte), typeof(uint))); + Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(sbyte), typeof(char))); + Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(byte), typeof(char))); + Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(byte), typeof(sbyte))); + // if an implicit conversion exists, ExplicitConversion() should return that + Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(byte), typeof(int))); + Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(double), typeof(float))); + Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(double), typeof(decimal))); + Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(decimal), typeof(double))); + Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(int), typeof(decimal))); + + Assert.AreEqual(C.None, ExplicitConversion(typeof(bool), typeof(int))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(int), typeof(bool))); + } + + [Test] + public void EnumerationConversions() + { + var explicitEnumerationConversion = C.EnumerationConversion(false, false); + Assert.AreEqual(explicitEnumerationConversion, ExplicitConversion(typeof(sbyte), typeof(StringComparison))); + Assert.AreEqual(explicitEnumerationConversion, ExplicitConversion(typeof(char), typeof(StringComparison))); + Assert.AreEqual(explicitEnumerationConversion, ExplicitConversion(typeof(int), typeof(StringComparison))); + Assert.AreEqual(explicitEnumerationConversion, ExplicitConversion(typeof(decimal), typeof(StringComparison))); + Assert.AreEqual(explicitEnumerationConversion, ExplicitConversion(typeof(StringComparison), typeof(char))); + Assert.AreEqual(explicitEnumerationConversion, ExplicitConversion(typeof(StringComparison), typeof(int))); + Assert.AreEqual(explicitEnumerationConversion, ExplicitConversion(typeof(StringComparison), typeof(decimal))); + Assert.AreEqual(explicitEnumerationConversion, ExplicitConversion(typeof(StringComparison), typeof(StringSplitOptions))); + } + + [Test] + public void NullableConversion_BasedOnIdentityConversion() + { + Assert.AreEqual(C.IdentityConversion, ExplicitConversion(typeof(ArraySegment?), typeof(ArraySegment?))); + Assert.AreEqual(C.ImplicitNullableConversion, ExplicitConversion(typeof(ArraySegment), typeof(ArraySegment?))); + Assert.AreEqual(C.ExplicitNullableConversion, ExplicitConversion(typeof(ArraySegment?), typeof(ArraySegment))); + } + + [Test] + public void NullableConversion_BasedOnImplicitNumericConversion() + { + Assert.AreEqual(C.ImplicitLiftedNumericConversion, ExplicitConversion(typeof(int?), typeof(long?))); + Assert.AreEqual(C.ImplicitLiftedNumericConversion, ExplicitConversion(typeof(int), typeof(long?))); + Assert.AreEqual(C.ExplicitLiftedNumericConversion, ExplicitConversion(typeof(int?), typeof(long))); + } + + [Test] + public void NullableConversion_BasedOnImplicitEnumerationConversion() + { + ResolveResult zero = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 0); + ResolveResult one = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 1); + Assert.AreEqual(C.EnumerationConversion(true, true), conversions.ExplicitConversion(zero, compilation.FindType(typeof(StringComparison?)))); + Assert.AreEqual(C.EnumerationConversion(false, true), conversions.ExplicitConversion(one, compilation.FindType(typeof(StringComparison?)))); + } + + [Test] + public void NullableConversion_BasedOnExplicitNumericConversion() + { + Assert.AreEqual(C.ExplicitLiftedNumericConversion, ExplicitConversion(typeof(int?), typeof(short?))); + Assert.AreEqual(C.ExplicitLiftedNumericConversion, ExplicitConversion(typeof(int), typeof(short?))); + Assert.AreEqual(C.ExplicitLiftedNumericConversion, ExplicitConversion(typeof(int?), typeof(short))); + } + + [Test] + public void NullableConversion_BasedOnExplicitEnumerationConversion() + { + C c = C.EnumerationConversion(false, true); // c = explicit lifted enumeration conversion + Assert.AreEqual(c, ExplicitConversion(typeof(int?), typeof(StringComparison?))); + Assert.AreEqual(c, ExplicitConversion(typeof(int), typeof(StringComparison?))); + Assert.AreEqual(c, ExplicitConversion(typeof(int?), typeof(StringComparison))); + + Assert.AreEqual(c, ExplicitConversion(typeof(StringComparison?), typeof(int?))); + Assert.AreEqual(c, ExplicitConversion(typeof(StringComparison), typeof(int?))); + Assert.AreEqual(c, ExplicitConversion(typeof(StringComparison?), typeof(int))); + + Assert.AreEqual(c, ExplicitConversion(typeof(StringComparison?), typeof(StringSplitOptions?))); + Assert.AreEqual(c, ExplicitConversion(typeof(StringComparison), typeof(StringSplitOptions?))); + Assert.AreEqual(c, ExplicitConversion(typeof(StringComparison?), typeof(StringSplitOptions))); + } + + [Test] + public void ExplicitReferenceConversion_SealedClass() + { + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(object), typeof(string))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(string))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(IEnumerable), typeof(string))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(IEnumerable), typeof(string))); + Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(string), typeof(IEnumerable))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(string), typeof(IEnumerable))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(string), typeof(IEnumerable))); + } + + [Test] + public void ExplicitReferenceConversion_NonSealedClass() + { + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(object), typeof(List))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(List))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(List))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(List))); + + Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(List), typeof(IEnumerable))); + Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(List), typeof(IEnumerable))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(List), typeof(IEnumerable))); + + Assert.AreEqual(C.None, ExplicitConversion(typeof(List), typeof(List))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(List), typeof(List))); + } + + [Test] + public void ExplicitReferenceConversion_Interfaces() + { + Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(IEnumerable))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(IEnumerable))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(IEnumerable))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(IEnumerable))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(IConvertible))); + } + + [Test] + public void ExplicitReferenceConversion_Arrays() + { + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(object[]), typeof(string[]))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(dynamic[]), typeof(string[]))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(object[]), typeof(int[]))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(short[]), typeof(int[]))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Array), typeof(int[]))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(ICloneable), typeof(int[]))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(string[]))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(string[]))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(object[]))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(dynamic[]))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable), typeof(int[]))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(IEnumerable), typeof(object[,]))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(IEnumerable), typeof(object[]))); + } + + [Test] + public void ExplicitReferenceConversion_Delegates() + { + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(MulticastDelegate), typeof(Action))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Delegate), typeof(Action))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(ICloneable), typeof(Action))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(System.Threading.ThreadStart), typeof(Action))); + } + + [Test] + public void ExplicitReferenceConversion_GenericDelegates() + { + Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(Action), typeof(Action))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Action), typeof(Action))); + + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Func), typeof(Func))); + Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(Func), typeof(Func))); + + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Action), typeof(Action))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(Action), typeof(Action))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Action), typeof(Action>))); + + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Func), typeof(Func))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(Func), typeof(Func))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(Func), typeof(Func>))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(Func), typeof(Func>))); + } + + [Test] + public void UnboxingConversion() + { + Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(object), typeof(int))); + Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(object), typeof(decimal))); + Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(ValueType), typeof(int))); + Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(IFormattable), typeof(int))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(IEnumerable), typeof(int))); + Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(Enum), typeof(StringComparison))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(Enum), typeof(int))); + } + + [Test] + public void LiftedUnboxingConversion() + { + Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(object), typeof(int?))); + Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(object), typeof(decimal?))); + Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(ValueType), typeof(int?))); + Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(IFormattable), typeof(int?))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(IEnumerable), typeof(int?))); + Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(Enum), typeof(StringComparison?))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(Enum), typeof(int?))); + } + Conversion ResolveCast(string program) + { + return Resolve(program).Conversion; + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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)); + } + + [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.IsTrue(rr.Conversion.IsUserDefined); + Assert.AreEqual("op_Explicit", rr.Conversion.Method.Name); + } + + } +} diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj index 082a61b054..6aef80bf33 100644 --- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj +++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj @@ -202,6 +202,7 @@ + diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs index 91118310a2..a649f89f45 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs @@ -307,5 +307,19 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.IsNotNull(c, "System.Func<,> not found"); Assert.AreEqual("mscorlib", c.ParentAssembly.AssemblyName); } + + public void DelegateIsClass() + { + var @delegate = compilation.FindType(KnownTypeCode.Delegate).GetDefinition(); + Assert.AreEqual(TypeKind.Class, @delegate); + Assert.IsFalse(@delegate.IsSealed); + } + + public void MulticastDelegateIsClass() + { + var multicastDelegate = compilation.FindType(KnownTypeCode.MulticastDelegate).GetDefinition(); + Assert.AreEqual(TypeKind.Class, multicastDelegate); + Assert.IsFalse(multicastDelegate.IsSealed); + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index 0f6c6d6741..9a98ed520c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -1627,11 +1627,13 @@ namespace ICSharpCode.NRefactory.TypeSystem static bool IsDelegate(TypeDefinition type) { - if (type.BaseType == null) - return false; - else - return type.BaseType.FullName == "System.Delegate" - || type.BaseType.FullName == "System.MulticastDelegate"; + if (type.BaseType != null && type.BaseType.Namespace == "System") { + if (type.BaseType.Name == "MulticastDelegate") + return true; + if (type.BaseType.Name == "Delegate" && type.Name != "MulticastDelegate") + return true; + } + return false; } static bool IsModule(TypeDefinition type) diff --git a/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs b/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs index ad84d1218a..e42af1af1f 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs @@ -151,6 +151,10 @@ namespace ICSharpCode.NRefactory.TypeSystem return typeArguments[index]; } + /// + /// Gets the definition of the generic type. + /// For ParameterizedType, this method never returns null. + /// public ITypeDefinition GetDefinition() { return genericType;