diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs index 5923b3390a..0d62ae1e7a 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs @@ -421,14 +421,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver && ImplicitReferenceConversion(fromArray.ElementType, toArray.ElementType, subtypeCheckNestingDepth); } // conversion from single-dimensional array S[] to IList: - ParameterizedType toPT = toType as ParameterizedType; - 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); - } + IType toTypeArgument = UnpackGenericArrayInterface(toType); + if (fromArray.Dimensions == 1 && toTypeArgument != null) { + // array covariance plays a part here as well (string[] is IList) + return IdentityConversion(fromArray.ElementType, toTypeArgument) + || ImplicitReferenceConversion(fromArray.ElementType, toTypeArgument, subtypeCheckNestingDepth); } // conversion from any array to System.Array and the interfaces it implements: IType systemArray = compilation.FindType(KnownTypeCode.Array); @@ -439,6 +436,22 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return IsSubtypeOf(fromType, toType, subtypeCheckNestingDepth); } + /// + /// For IList{T}, ICollection{T}, IEnumerable{T} and IReadOnlyList{T}, returns T. + /// Otherwise, returns null. + /// + IType UnpackGenericArrayInterface(IType interfaceType) + { + ParameterizedType pt = interfaceType as ParameterizedType; + if (pt != null) { + KnownTypeCode tc = pt.GetDefinition().KnownTypeCode; + if (tc == KnownTypeCode.IListOfT || tc == KnownTypeCode.ICollectionOfT || tc == KnownTypeCode.IEnumerableOfT || tc == KnownTypeCode.IReadOnlyListOfT) { + return pt.GetTypeArgument(0); + } + } + return null; + } + // Determines whether s is a subtype of t. // Helper method used for ImplicitReferenceConversion, BoxingConversion and ImplicitTypeParameterConversion @@ -530,16 +543,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver 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); - } + IType fromTypeArgument = UnpackGenericArrayInterface(fromType); + if (fromTypeArgument != null && toArray.Dimensions == 1) { + return ExplicitReferenceConversion(fromTypeArgument, toArray.ElementType) + || IdentityConversion(fromTypeArgument, 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.Array) { + ArrayType fromArray = (ArrayType)fromType; + IType toTypeArgument = UnpackGenericArrayInterface(toType); + if (toTypeArgument != null && fromArray.Dimensions == 1) { + return ExplicitReferenceConversion(fromArray.ElementType, toTypeArgument); + } + // Otherwise treat the array like a sealed class + return IsImplicitReferenceConversion(fromType, toType); } else if (fromType.Kind == TypeKind.Delegate && toType.Kind == TypeKind.Delegate) { ITypeDefinition def = fromType.GetDefinition(); if (def == null || !def.Equals(toType.GetDefinition())) @@ -570,7 +588,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } return true; - } else if (IsSealedReferenceType(fromType) || fromType.Kind == TypeKind.Array) { + } else if (IsSealedReferenceType(fromType)) { // If the source type is sealed, explicit conversions can't do anything more than implicit ones return IsImplicitReferenceConversion(fromType, toType); } else if (IsSealedReferenceType(toType)) { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/ArrayCreateExpressionTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/ArrayCreateExpressionTests.cs index 7e52a53d6a..4efc4309a2 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/ArrayCreateExpressionTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/ArrayCreateExpressionTests.cs @@ -158,7 +158,7 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.Expression }, ace.Initializer.Children.Select(c => c.Role).ToArray()); } - [Test, Ignore("Parser bug")] + [Test] public void ArrayInitializerWithCommaAtEnd() { var ace = ParseUtilCSharp.ParseExpression("new [] { 1, }"); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs index b910319c65..c72d67312b 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs @@ -197,6 +197,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver 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[]))); + } + + [Test] + public void ExplicitReferenceConversion_InterfaceToArray() + { 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[]))); @@ -207,6 +212,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Assert.AreEqual(C.None, ExplicitConversion(typeof(IEnumerable), typeof(object[]))); } + [Test] + public void ExplicitReferenceConversion_ArrayToInterface() + { + Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(int[]), typeof(ICloneable))); + Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(string[]), typeof(IEnumerable))); + Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(string[]), typeof(IEnumerable))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(object[]), typeof(IEnumerable))); + Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(dynamic[]), typeof(IEnumerable))); + Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(int[]), typeof(IEnumerable))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(object[,]), typeof(IEnumerable))); + Assert.AreEqual(C.None, ExplicitConversion(typeof(object[]), typeof(IEnumerable))); + } + [Test] public void ExplicitReferenceConversion_Delegates() {