From 5c57f8fd0de15d6c8529789781b5c79dfdf13c46 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 8 Oct 2010 22:58:18 +0200 Subject: [PATCH] Add support for integer literal conversions. --- .../CSharp/Resolver/ConversionsTest.cs | 86 +++++++++++++++++ .../TypeSystem/CecilLoaderTests.cs | 2 +- .../CSharp/Resolver/ConstantResolveResult.cs | 22 +++++ .../CSharp/Resolver/Conversions.cs | 48 ++++++++-- .../ICSharpCode.NRefactory.csproj | 1 + .../TypeSystem/Implementation/AbstractType.cs | 4 +- .../Implementation/DefaultTypeParameter.cs | 96 +++++++++++++++++-- .../TypeSystem/SharedTypes.cs | 15 --- 8 files changed, 241 insertions(+), 33 deletions(-) create mode 100644 ICSharpCode.NRefactory/CSharp/Resolver/ConstantResolveResult.cs diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs index 44a2f306e5..7858e713a7 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs @@ -115,6 +115,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Assert.IsTrue(ImplicitConversion(SharedTypes.Dynamic, typeof(int))); } + [Test] + public void ParameterizedTypeConversions() + { + Assert.IsTrue(ImplicitConversion(typeof(List), typeof(ICollection))); + Assert.IsTrue(ImplicitConversion(typeof(IList), typeof(ICollection))); + Assert.IsFalse(ImplicitConversion(typeof(List), typeof(ICollection))); + Assert.IsFalse(ImplicitConversion(typeof(IList), typeof(ICollection))); + } + [Test] public void ArrayConversions() { @@ -153,5 +162,82 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Assert.IsFalse(ImplicitConversion(typeof(Func), typeof(Func))); Assert.IsFalse(ImplicitConversion(typeof(Func), typeof(Func))); } + + bool IntegerLiteralConversion(object value, Type to) + { + IType fromType = value.GetType().ToTypeReference().Resolve(mscorlib); + ConstantResolveResult crr = new ConstantResolveResult(fromType, value); + IType to2 = to.ToTypeReference().Resolve(mscorlib); + return conversions.ImplicitConversion(crr, to2); + } + + [Test] + public void IntegerLiteralToEnumConversions() + { + Assert.IsTrue(IntegerLiteralConversion(0, typeof(LoaderOptimization))); + Assert.IsTrue(IntegerLiteralConversion(0L, typeof(LoaderOptimization))); + Assert.IsTrue(IntegerLiteralConversion(0, typeof(LoaderOptimization?))); + Assert.IsFalse(IntegerLiteralConversion(0, typeof(string))); + Assert.IsFalse(IntegerLiteralConversion(1, typeof(LoaderOptimization))); + } + + [Test] + public void ImplicitConstantExpressionConversion() + { + Assert.IsTrue(IntegerLiteralConversion(0, typeof(int))); + Assert.IsTrue(IntegerLiteralConversion(0, typeof(ushort))); + Assert.IsTrue(IntegerLiteralConversion(0, typeof(sbyte))); + + Assert.IsTrue (IntegerLiteralConversion(-1, typeof(int))); + Assert.IsFalse(IntegerLiteralConversion(-1, typeof(ushort))); + Assert.IsTrue (IntegerLiteralConversion(-1, typeof(sbyte))); + + Assert.IsTrue (IntegerLiteralConversion(200, typeof(int))); + Assert.IsTrue (IntegerLiteralConversion(200, typeof(ushort))); + Assert.IsFalse(IntegerLiteralConversion(200, typeof(sbyte))); + } + + [Test] + public void ImplicitLongConstantExpressionConversion() + { + Assert.IsFalse(IntegerLiteralConversion(0L, typeof(int))); + Assert.IsTrue(IntegerLiteralConversion(0L, typeof(long))); + Assert.IsTrue(IntegerLiteralConversion(0L, typeof(ulong))); + + Assert.IsTrue(IntegerLiteralConversion(-1L, typeof(long))); + Assert.IsFalse(IntegerLiteralConversion(-1L, typeof(ulong))); + } + + [Test] + public void ImplicitConstantExpressionConversionToNullable() + { + Assert.IsTrue(IntegerLiteralConversion(0, typeof(uint?))); + Assert.IsTrue(IntegerLiteralConversion(0, typeof(short?))); + Assert.IsTrue(IntegerLiteralConversion(0, typeof(byte?))); + + Assert.IsFalse(IntegerLiteralConversion(-1, typeof(uint?))); + Assert.IsTrue (IntegerLiteralConversion(-1, typeof(short?))); + Assert.IsFalse(IntegerLiteralConversion(-1, typeof(byte?))); + + Assert.IsTrue(IntegerLiteralConversion(200, typeof(uint?))); + Assert.IsTrue(IntegerLiteralConversion(200, typeof(short?))); + Assert.IsTrue(IntegerLiteralConversion(200, typeof(byte?))); + + Assert.IsFalse(IntegerLiteralConversion(0L, typeof(uint?))); + Assert.IsTrue (IntegerLiteralConversion(0L, typeof(long?))); + Assert.IsTrue (IntegerLiteralConversion(0L, typeof(ulong?))); + + Assert.IsTrue(IntegerLiteralConversion(-1L, typeof(long?))); + Assert.IsFalse(IntegerLiteralConversion(-1L, typeof(ulong?))); + } + + [Test] + public void ImplicitConstantExpressionConversionNumberInterfaces() + { + Assert.IsTrue(IntegerLiteralConversion(0, typeof(IFormattable))); + Assert.IsTrue(IntegerLiteralConversion(0, typeof(IComparable))); + Assert.IsFalse(IntegerLiteralConversion(0, typeof(IComparable))); + Assert.IsFalse(IntegerLiteralConversion(0, typeof(IComparable))); + } } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs index de91bcaa2a..d2dbe37130 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs @@ -17,7 +17,7 @@ namespace ICSharpCode.NRefactory.TypeSystem ITypeResolveContext ctx = Mscorlib; [TestFixtureSetUp] - public void SetUp() + public void FixtureSetUp() { // use "IncludeInternalMembers" so that Cecil results match C# parser results CecilLoader loader = new CecilLoader() { IncludeInternalMembers = true }; diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ConstantResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ConstantResolveResult.cs new file mode 100644 index 0000000000..91289c7426 --- /dev/null +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ConstantResolveResult.cs @@ -0,0 +1,22 @@ + +using System; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + public class ConstantResolveResult + { + public IType Type { get; set; } + public object Value { get; set; } + + public ConstantResolveResult(IType type, object value) + { + if (type == null) + throw new ArgumentNullException("type"); + if (value == null) + throw new ArgumentNullException("value"); + this.Type = type; + this.Value = value; + } + } +} diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs b/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs index f81880893c..14e836ca2a 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs @@ -23,6 +23,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } #region ImplicitConversion + public bool ImplicitConversion(ConstantResolveResult resolveResult, IType toType) + { + if (resolveResult == null) + throw new ArgumentNullException("resolveResult"); + ConstantResolveResult crr = resolveResult as ConstantResolveResult; + if (crr != null && (ImplicitEnumerationConversion(crr, toType) || ImplicitConstantExpressionConversion(crr, toType))) + return true; + return ImplicitConversion(resolveResult.Type, toType); + } + public bool ImplicitConversion(IType fromType, IType toType) { if (fromType == null) @@ -36,8 +46,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return true; if (ImplicitReferenceConversion(fromType, toType)) return true; - if (ImplicitEnumerationConversion(fromType, toType)) - return true; if (ImplicitNullableConversion(fromType, toType)) return true; if (NullLiteralConversion(fromType, toType)) @@ -46,8 +54,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return true; if (ImplicitDynamicConversion(fromType, toType)) return true; - if (ImplicitConstantExpressionConversion(fromType, toType)) - return true; return false; } #endregion @@ -117,10 +123,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #endregion #region ImplicitEnumerationConversion - bool ImplicitEnumerationConversion(IType fromType, IType toType) + bool ImplicitEnumerationConversion(ConstantResolveResult rr, IType toType) { // C# 4.0 spec: §6.1.3 - // TODO: implement ImplicitEnumerationConversion and ImplicitConstantExpressionConversion + TypeCode constantType = ReflectionHelper.GetTypeCode(rr.Type); + if (constantType >= TypeCode.SByte && constantType <= TypeCode.Decimal && Convert.ToDecimal(rr.Value) == 0) { + toType = UnpackNullable(toType) ?? toType; + ITypeDefinition toDef = toType.GetDefinition(); + return toDef.ClassType == ClassType.Enum; + } return false; } #endregion @@ -271,10 +282,31 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #endregion #region ImplicitConstantExpressionConversion - bool ImplicitConstantExpressionConversion(IType fromType, IType toType) + bool ImplicitConstantExpressionConversion(ConstantResolveResult rr, IType toType) { // C# 4.0 spec: §6.1.9 - // TODO: implement ImplicitEnumerationConversion and ImplicitConstantExpressionConversion + TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type); + TypeCode toTypeCode = ReflectionHelper.GetTypeCode(UnpackNullable(toType) ?? toType); + if (fromTypeCode == TypeCode.Int64) { + long val = (long)rr.Value; + return val >= 0 && toTypeCode == TypeCode.UInt64; + } else if (fromTypeCode == TypeCode.Int32) { + int val = (int)rr.Value; + switch (toTypeCode) { + case TypeCode.SByte: + return val >= SByte.MinValue && val <= SByte.MaxValue; + case TypeCode.Byte: + return val >= Byte.MinValue && val <= Byte.MaxValue; + case TypeCode.Int16: + return val >= Int16.MinValue && val <= Int16.MaxValue; + case TypeCode.UInt16: + return val >= UInt16.MinValue && val <= UInt16.MaxValue; + case TypeCode.UInt32: + return val >= 0; + case TypeCode.UInt64: + return val >= 0; + } + } return false; } #endregion diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 168e2366cc..3e357d9134 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -139,6 +139,7 @@ + diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs index b16f69300e..c6ee99d70c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs @@ -10,7 +10,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// /// Default implementation for IType interface. /// - public abstract class AbstractType : AbstractFreezable, IType + public abstract class AbstractType : IType { public virtual string FullName { get { @@ -51,7 +51,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return null; } - public IType Resolve(ITypeResolveContext context) + IType ITypeReference.Resolve(ITypeResolveContext context) { return this; } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs index 1f27e67e14..57c54dec4a 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using ICSharpCode.NRefactory.Util; namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -10,7 +11,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// /// Default implementation of . /// - public class DefaultTypeParameter : AbstractType, ITypeParameter, ISupportsInterning + public class DefaultTypeParameter : AbstractFreezable, ITypeParameter, ISupportsInterning { IEntity parent; @@ -58,11 +59,19 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation this.name = name; } - public override string Name { + public string Name { get { return name; } } - public override string DotNetName { + string INamedElement.FullName { + get { return name; } + } + + string INamedElement.Namespace { + get { return string.Empty; } + } + + public string DotNetName { get { if (parent is IMethod) return "``" + index.ToString(); @@ -71,7 +80,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } - public override bool? IsReferenceType { + public bool? IsReferenceType { get { switch (flags.Data & (FlagReferenceTypeConstraint | FlagValueTypeConstraint)) { case FlagReferenceTypeConstraint: @@ -84,6 +93,24 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } + int IType.TypeParameterCount { + get { return 0; } + } + + IType IType.DeclaringType { + get { return null; } + } + + ITypeDefinition IType.GetDefinition() + { + return null; + } + + IType ITypeReference.Resolve(ITypeResolveContext context) + { + return this; + } + public override int GetHashCode() { int hashCode = parent.GetHashCode(); @@ -93,7 +120,12 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return hashCode; } - public override bool Equals(IType other) + public override bool Equals(object obj) + { + return Equals(obj as IType); + } + + public bool Equals(IType other) { DefaultTypeParameter p = other as DefaultTypeParameter; if (p == null) @@ -174,11 +206,16 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation get { return null; } } - public override IType AcceptVisitor(TypeVisitor visitor) + public IType AcceptVisitor(TypeVisitor visitor) { return visitor.VisitTypeParameter(this); } + public IType VisitChildren(TypeVisitor visitor) + { + return this; + } + DefaultTypeDefinition GetDummyClassForTypeParameter() { DefaultTypeDefinition c = new DefaultTypeDefinition(ParentClass ?? ParentMethod.DeclaringTypeDefinition, this.Name); @@ -193,7 +230,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return c; } - public override IEnumerable GetConstructors(ITypeResolveContext context) + public IEnumerable GetConstructors(ITypeResolveContext context) { if (HasDefaultConstructorConstraint || HasValueTypeConstraint) { return new [] { DefaultMethod.CreateDefaultConstructor(GetDummyClassForTypeParameter()) }; @@ -202,6 +239,51 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } + public IEnumerable GetMethods(ITypeResolveContext context) + { + // TODO: get methods from constraints + IType objectType = context.GetClass("System.Object", 0, StringComparer.Ordinal); + IEnumerable objectMethods; + if (objectType != null) + objectMethods = objectType.GetMethods(context); + else + objectMethods = EmptyList.Instance; + + // don't return static methods (those cannot be called from type parameter) + return objectMethods.Where(m => !m.IsStatic); + } + + public IEnumerable GetProperties(ITypeResolveContext context) + { + return EmptyList.Instance; + } + + public IEnumerable GetFields(ITypeResolveContext context) + { + return EmptyList.Instance; + } + + public IEnumerable GetEvents(ITypeResolveContext context) + { + return EmptyList.Instance; + } + + IEnumerable IType.GetNestedTypes(ITypeResolveContext context) + { + return EmptyList.Instance; + } + + public IEnumerable GetBaseTypes(ITypeResolveContext context) + { + IType defaultBaseType = context.GetClass(HasValueTypeConstraint ? "System.ValueType" : "System.Object", 0, StringComparer.Ordinal); + if (defaultBaseType != null) + yield return defaultBaseType; + + foreach (ITypeReference constraint in this.Constraints) { + yield return constraint.Resolve(context); + } + } + void ISupportsInterning.PrepareForInterning(IInterningProvider provider) { constraints = provider.InternList(constraints); diff --git a/ICSharpCode.NRefactory/TypeSystem/SharedTypes.cs b/ICSharpCode.NRefactory/TypeSystem/SharedTypes.cs index 31685327ce..d787dd2e1f 100644 --- a/ICSharpCode.NRefactory/TypeSystem/SharedTypes.cs +++ b/ICSharpCode.NRefactory/TypeSystem/SharedTypes.cs @@ -54,11 +54,6 @@ namespace ICSharpCode.NRefactory.TypeSystem /// sealed class UnknownTypeImpl : AbstractType { - public UnknownTypeImpl() - { - Freeze(); - } - public override string Name { get { return "?"; } } @@ -83,11 +78,6 @@ namespace ICSharpCode.NRefactory.TypeSystem /// sealed class NullType : AbstractType { - public NullType() - { - Freeze(); - } - public override string Name { get { return "null"; } } @@ -112,11 +102,6 @@ namespace ICSharpCode.NRefactory.TypeSystem /// sealed class DynamicType : AbstractType { - public DynamicType() - { - Freeze(); - } - public override string Name { get { return "dynamic"; } }