Browse Source

Implemented explicit conversions - invalid casts now resolve to Conversion.None.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
26409db2bb
  1. 126
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs
  2. 2
      ICSharpCode.NRefactory.ConsistencyCheck/Program.cs
  3. 49
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs
  4. 454
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs
  5. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  6. 14
      ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs
  7. 12
      ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
  8. 4
      ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs

126
ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs

@ -218,8 +218,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -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 @@ -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 @@ -429,17 +422,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
// conversion from single-dimensional array S[] to IList<T>:
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<object>)
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<object>)
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 @@ -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 @@ -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

2
ICSharpCode.NRefactory.ConsistencyCheck/Program.cs

@ -57,7 +57,7 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck @@ -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);

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

@ -50,13 +50,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -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 @@ -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 @@ -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 @@ -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 @@ -523,26 +516,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Assert.IsFalse(conversions.ImplicitConversion(type1, type2).IsValid);
}
[Test]
public void ExplicitUserDefinedConversion()
{
var rr = Resolve<ConversionResolveResult>(@"
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()
{

454
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs

@ -0,0 +1,454 @@ @@ -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<dynamic>?), typeof(ArraySegment<object>?)));
Assert.AreEqual(C.ImplicitNullableConversion, ExplicitConversion(typeof(ArraySegment<dynamic>), typeof(ArraySegment<object>?)));
Assert.AreEqual(C.ExplicitNullableConversion, ExplicitConversion(typeof(ArraySegment<dynamic>?), typeof(ArraySegment<object>)));
}
[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<char>), typeof(string)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(IEnumerable<int>), typeof(string)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(IEnumerable<object>), typeof(string)));
Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(string), typeof(IEnumerable<char>)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(string), typeof(IEnumerable<int>)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(string), typeof(IEnumerable<object>)));
}
[Test]
public void ExplicitReferenceConversion_NonSealedClass()
{
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(object), typeof(List<string>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<object>), typeof(List<string>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<string>), typeof(List<string>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<int>), typeof(List<string>)));
Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(List<string>), typeof(IEnumerable<object>)));
Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(List<string>), typeof(IEnumerable<string>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(List<string>), typeof(IEnumerable<int>)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(List<string>), typeof(List<object>)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(List<string>), typeof(List<int>)));
}
[Test]
public void ExplicitReferenceConversion_Interfaces()
{
Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<string>), typeof(IEnumerable<object>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<int>), typeof(IEnumerable<object>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<object>), typeof(IEnumerable<string>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<object>), typeof(IEnumerable<int>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<object>), 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<string>), typeof(string[])));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<object>), typeof(string[])));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<string>), typeof(object[])));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<string>), typeof(dynamic[])));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(IEnumerable<int>), typeof(int[])));
Assert.AreEqual(C.None, ExplicitConversion(typeof(IEnumerable<string>), typeof(object[,])));
Assert.AreEqual(C.None, ExplicitConversion(typeof(IEnumerable<short>), 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<object>), typeof(Action<string>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Action<string>), typeof(Action<object>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Func<object>), typeof(Func<string>)));
Assert.AreEqual(C.ImplicitReferenceConversion, ExplicitConversion(typeof(Func<string>), typeof(Func<object>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Action<IFormattable>), typeof(Action<IConvertible>)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(Action<IFormattable>), typeof(Action<int>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Action<string>), typeof(Action<IEnumerable<int>>)));
Assert.AreEqual(C.ExplicitReferenceConversion, ExplicitConversion(typeof(Func<IFormattable>), typeof(Func<IConvertible>)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(Func<IFormattable>), typeof(Func<int>)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(Func<string>), typeof(Func<IEnumerable<int>>)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(Func<string>), typeof(Func<IEnumerable<int>>)));
}
[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<object>), 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<object>), 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<ConversionResolveResult>(program).Conversion;
}
[Test]
public void ObjectToTypeParameter()
{
string program = @"using System;
class Test {
public void M<T>(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<T>(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<T>(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 t) {
IDisposable d = $(IDisposable)t$;
}
}";
Assert.AreEqual(C.BoxingConversion, ResolveCast(program));
}
[Test]
public void ValueTypeToTypeParameter()
{
string program = @"using System;
class Test {
public void M<T>(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, U>(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, U>(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, U>(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, U>(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, U>(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, U>(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, U>(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, U>(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, U>(T[] t) where U : class, T {
U[] u = $(U[])t$;
}
}";
Assert.AreEqual(C.ExplicitReferenceConversion, ResolveCast(program));
}
[Test]
public void SimpleUserDefinedConversion()
{
var rr = Resolve<ConversionResolveResult>(@"
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);
}
}
}

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

@ -202,6 +202,7 @@ @@ -202,6 +202,7 @@
<Compile Include="CSharp\Resolver\ComTests.cs" />
<Compile Include="CSharp\Resolver\ConditionalOperatorTests.cs" />
<Compile Include="CSharp\Resolver\DynamicTests.cs" />
<Compile Include="CSharp\Resolver\ExplicitConversionsTest.cs" />
<Compile Include="CSharp\Resolver\ExtensionMethodTests.cs" />
<Compile Include="CSharp\Resolver\FindReferencesTest.cs" />
<Compile Include="CSharp\Resolver\InvocationTests.cs" />

14
ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs

@ -307,5 +307,19 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -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);
}
}
}

12
ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs

@ -1627,11 +1627,13 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -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)

4
ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs

@ -151,6 +151,10 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -151,6 +151,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
return typeArguments[index];
}
/// <summary>
/// Gets the definition of the generic type.
/// For <c>ParameterizedType</c>, this method never returns null.
/// </summary>
public ITypeDefinition GetDefinition()
{
return genericType;

Loading…
Cancel
Save