Browse Source

Added CastTests.

Constant folding: fixed implicit conversions and casts to use C# semantics
newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
b46cfa7e29
  1. 44
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/CastTests.cs
  2. 59
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs
  3. 74
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs
  4. 73
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/UnaryOperatorTests.cs
  5. 2
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  6. 447
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  7. 13
      ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs
  8. 9
      ICSharpCode.NRefactory/TypeSystem/NullableType.cs
  9. 14
      ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs
  10. 18
      README
  11. 18
      doc/TODO

44
ICSharpCode.NRefactory.Tests/CSharp/Resolver/CastTests.cs

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
// assign short name to the fake reflection type
using dynamic = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.Dynamic;
[TestFixture]
public class CastTests : ResolverTestBase
{
[Test]
public void SimpleCast()
{
AssertType(typeof(int), resolver.ResolveCast(ResolveType(typeof(int)), MakeResult(typeof(float))));
AssertType(typeof(string), resolver.ResolveCast(ResolveType(typeof(string)), MakeResult(typeof(object))));
AssertType(typeof(byte), resolver.ResolveCast(ResolveType(typeof(byte)), MakeResult(typeof(dynamic))));
AssertType(typeof(dynamic), resolver.ResolveCast(ResolveType(typeof(dynamic)), MakeResult(typeof(double))));
}
[Test]
public void ConstantValueCast()
{
AssertConstant("Hello", resolver.ResolveCast(ResolveType(typeof(string)), MakeConstant("Hello")));
AssertConstant((byte)1L, resolver.ResolveCast(ResolveType(typeof(byte)), MakeConstant(1L)));
AssertConstant(3, resolver.ResolveCast(ResolveType(typeof(int)), MakeConstant(3.1415)));
AssertConstant(3, resolver.ResolveCast(ResolveType(typeof(int)), MakeConstant(3.99)));
AssertConstant((short)-3, resolver.ResolveCast(ResolveType(typeof(short)), MakeConstant(-3.99f)));
AssertConstant(-3L, resolver.ResolveCast(ResolveType(typeof(long)), MakeConstant(-3.5)));
}
[Test]
public void OverflowingCast()
{
resolver.IsCheckedContext = false;
AssertConstant(uint.MaxValue, resolver.ResolveCast(ResolveType(typeof(uint)), MakeConstant(-1.6)));
resolver.IsCheckedContext = true;
AssertError(typeof(uint), resolver.ResolveCast(ResolveType(typeof(uint)), MakeConstant(-1.6)));
}
}
}

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

@ -9,6 +9,10 @@ using NUnit.Framework; @@ -9,6 +9,10 @@ using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
// assign short names to the fake reflection types
using Null = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.Null;
using dynamic = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.Dynamic;
[TestFixture]
public class ConversionsTest
{
@ -22,18 +26,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -22,18 +26,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return conversions.ImplicitConversion(from2, to2);
}
bool ImplicitConversion(Type from, IType to)
{
IType from2 = from.ToTypeReference().Resolve(mscorlib);
return conversions.ImplicitConversion(from2, to);
}
bool ImplicitConversion(IType from, Type to)
{
IType to2 = to.ToTypeReference().Resolve(mscorlib);
return conversions.ImplicitConversion(from, to2);
}
[Test]
public void IdentityConversions()
{
@ -50,24 +42,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -50,24 +42,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
[Test]
public void DynamicIdentityConversions()
{
Assert.IsTrue(ImplicitConversion(typeof(object), SharedTypes.Dynamic));
Assert.IsTrue(ImplicitConversion(SharedTypes.Dynamic, typeof(object)));
Assert.IsTrue(ImplicitConversion(typeof(object), typeof(ReflectionHelper.Dynamic)));
Assert.IsTrue(ImplicitConversion(typeof(ReflectionHelper.Dynamic), typeof(object)));
}
[Test]
public void ComplexDynamicIdentityConversions()
{
var listOfDynamic = new ParameterizedType(mscorlib.GetClass(typeof(List<>)), new [] { SharedTypes.Dynamic });
Assert.IsTrue(ImplicitConversion(typeof(List<object>), listOfDynamic));
Assert.IsTrue(ImplicitConversion(listOfDynamic, typeof(List<object>)));
Assert.IsFalse(ImplicitConversion(typeof(List<string>), listOfDynamic));
Assert.IsFalse(ImplicitConversion(listOfDynamic, typeof(List<string>)));
Assert.IsTrue(ImplicitConversion(typeof(List<object>), typeof(List<dynamic>)));
Assert.IsTrue(ImplicitConversion(typeof(List<dynamic>), typeof(List<object>)));
Assert.IsFalse(ImplicitConversion(typeof(List<string>), typeof(List<dynamic>)));
Assert.IsFalse(ImplicitConversion(typeof(List<dynamic>), typeof(List<string>)));
var complexTypeInvolvingDynamic = new ParameterizedType(mscorlib.GetClass(typeof(List<>)), new [] { new ArrayType(listOfDynamic, 1) });
// complexTypeInvolvingDynamic = List<List<dynamic>[]>
Assert.IsTrue(ImplicitConversion(complexTypeInvolvingDynamic, typeof(List<List<object>[]>)));
Assert.IsTrue(ImplicitConversion(typeof(List<List<object>[]>), complexTypeInvolvingDynamic));
Assert.IsFalse(ImplicitConversion(typeof(List<List<object>[,]>), complexTypeInvolvingDynamic));
Assert.IsTrue(ImplicitConversion(typeof(List<List<dynamic>[]>), typeof(List<List<object>[]>)));
Assert.IsTrue(ImplicitConversion(typeof(List<List<object>[]>), typeof(List<List<dynamic>[]>)));
Assert.IsFalse(ImplicitConversion(typeof(List<List<object>[,]>), typeof(List<List<dynamic>[]>)));
}
[Test]
@ -114,13 +103,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -114,13 +103,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
[Test]
public void NullLiteralConversions()
{
Assert.IsTrue(ImplicitConversion(SharedTypes.Null, typeof(int?)));
Assert.IsTrue(ImplicitConversion(SharedTypes.Null, typeof(char?)));
Assert.IsFalse(ImplicitConversion(SharedTypes.Null, typeof(int)));
Assert.IsTrue(ImplicitConversion(SharedTypes.Null, typeof(object)));
Assert.IsTrue(conversions.ImplicitConversion(SharedTypes.Null, SharedTypes.Dynamic));
Assert.IsTrue(ImplicitConversion(SharedTypes.Null, typeof(string)));
Assert.IsTrue(ImplicitConversion(SharedTypes.Null, typeof(int[])));
Assert.IsTrue(ImplicitConversion(typeof(Null), typeof(int?)));
Assert.IsTrue(ImplicitConversion(typeof(Null), typeof(char?)));
Assert.IsFalse(ImplicitConversion(typeof(Null), typeof(int)));
Assert.IsTrue(ImplicitConversion(typeof(Null), typeof(object)));
Assert.IsTrue(ImplicitConversion(typeof(Null), typeof(dynamic)));
Assert.IsTrue(ImplicitConversion(typeof(Null), typeof(string)));
Assert.IsTrue(ImplicitConversion(typeof(Null), typeof(int[])));
}
[Test]
@ -137,10 +126,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -137,10 +126,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
[Test]
public void SimpleDynamicConversions()
{
Assert.IsTrue(ImplicitConversion(typeof(string), SharedTypes.Dynamic));
Assert.IsTrue(ImplicitConversion(SharedTypes.Dynamic, typeof(string)));
Assert.IsTrue(ImplicitConversion(typeof(int), SharedTypes.Dynamic));
Assert.IsTrue(ImplicitConversion(SharedTypes.Dynamic, typeof(int)));
Assert.IsTrue(ImplicitConversion(typeof(string), typeof(dynamic)));
Assert.IsTrue(ImplicitConversion(typeof(dynamic), typeof(string)));
Assert.IsTrue(ImplicitConversion(typeof(int), typeof(dynamic)));
Assert.IsTrue(ImplicitConversion(typeof(dynamic), typeof(int)));
}
[Test]

74
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using ICSharpCode.NRefactory.TypeSystem;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
/// <summary>
/// Base class with helper functions for resolver unit tests.
/// </summary>
public abstract class ResolverTestBase
{
protected readonly IProjectContent mscorlib = CecilLoaderTests.Mscorlib;
protected CSharpResolver resolver;
[SetUp]
public virtual void SetUp()
{
resolver = new CSharpResolver(CecilLoaderTests.Mscorlib);
}
protected IType ResolveType(Type type)
{
IType t = type.ToTypeReference().Resolve(mscorlib);
if (t == SharedTypes.UnknownType)
throw new InvalidOperationException("Could not resolve type");
return t;
}
protected ConstantResolveResult MakeConstant(object value)
{
IType type = ResolveType(value.GetType());
if (type.IsEnum())
value = Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()));
return new ConstantResolveResult(type, value);
}
protected ResolveResult MakeResult(Type type)
{
return new ResolveResult(ResolveType(type));
}
protected void AssertConstant(object expectedValue, ResolveResult rr)
{
Assert.IsFalse(rr.IsError, rr.ToString() + " is an error");
Assert.IsTrue(rr.IsCompileTimeConstant, rr.ToString() + " is not a compile-time constant");
Type expectedType = expectedValue.GetType();
Assert.AreEqual(ResolveType(expectedType), rr.Type, "ResolveResult.Type is wrong");
if (expectedType.IsEnum) {
Assert.AreEqual(Enum.GetUnderlyingType(expectedType), rr.ConstantValue.GetType(), "ResolveResult.ConstantValue has wrong Type");
Assert.AreEqual(Convert.ChangeType(expectedValue, Enum.GetUnderlyingType(expectedType)), rr.ConstantValue);
} else {
Assert.AreEqual(expectedType, rr.ConstantValue.GetType(), "ResolveResult.ConstantValue has wrong Type");
Assert.AreEqual(expectedValue, rr.ConstantValue);
}
}
protected void AssertType(Type expectedType, ResolveResult rr)
{
Assert.IsFalse(rr.IsError, rr.ToString() + " is an error");
Assert.IsFalse(rr.IsCompileTimeConstant, rr.ToString() + " is a compile-time constant");
Assert.AreEqual(expectedType.ToTypeReference().Resolve(mscorlib), rr.Type);
}
protected void AssertError(Type expectedType, ResolveResult rr)
{
Assert.IsTrue(rr.IsError, rr.ToString() + " is not an error, but an error was expected");
Assert.IsFalse(rr.IsCompileTimeConstant, rr.ToString() + " is a compile-time constant");
Assert.AreEqual(expectedType.ToTypeReference().Resolve(mscorlib), rr.Type);
}
}
}

73
ICSharpCode.NRefactory.Tests/CSharp/Resolver/UnaryOperatorTests.cs

@ -7,30 +7,39 @@ using NUnit.Framework; @@ -7,30 +7,39 @@ using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
// assign short name to the fake reflection type
using dynamic = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.Dynamic;
[TestFixture]
public class UnaryOperatorTests
public class UnaryOperatorTests : ResolverTestBase
{
IProjectContent mscorlib = CecilLoaderTests.Mscorlib;
CSharpResolver resolver;
ConstantResolveResult MakeConstant(object value)
[Test]
public void TestAddressOf()
{
return new ConstantResolveResult(value.GetType().ToTypeReference().Resolve(mscorlib), value);
AssertType(typeof(int*), resolver.ResolveUnaryOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(int))));
AssertType(typeof(byte**), resolver.ResolveUnaryOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(byte*))));
AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(dynamic))));
}
void AssertConstant(object expectedValue, ResolveResult rr)
[Test]
public void TestDereference()
{
Assert.IsFalse(rr.IsError, rr.ToString() + " is an error");
Assert.IsTrue(rr.IsCompileTimeConstant, rr.ToString() + " is not a compile-time constant");
Assert.AreEqual(expectedValue, rr.ConstantValue);
Assert.AreEqual(expectedValue.GetType(), rr.ConstantValue.GetType(), "ResolveResult.ConstantValue has wrong Type");
Assert.AreEqual(expectedValue.GetType().ToTypeReference().Resolve(mscorlib), rr.Type, "ResolveResult.Type is wrong");
AssertType(typeof(int), resolver.ResolveUnaryOperator(UnaryOperatorType.Dereference, MakeResult(typeof(int*))));
AssertType(typeof(long*), resolver.ResolveUnaryOperator(UnaryOperatorType.Dereference, MakeResult(typeof(long**))));
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Dereference, MakeResult(typeof(int))).IsError);
AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.Dereference, MakeResult(typeof(dynamic))));
}
[SetUp]
public void SetUp()
[Test]
public void TestIncrementDecrement()
{
resolver = new CSharpResolver(CecilLoaderTests.Mscorlib);
AssertType(typeof(byte), resolver.ResolveUnaryOperator(UnaryOperatorType.Increment, MakeResult(typeof(byte))));
AssertType(typeof(ulong), resolver.ResolveUnaryOperator(UnaryOperatorType.Decrement, MakeResult(typeof(ulong))));
AssertType(typeof(short?), resolver.ResolveUnaryOperator(UnaryOperatorType.PostDecrement, MakeResult(typeof(short?))));
AssertType(typeof(TypeCode), resolver.ResolveUnaryOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(TypeCode))));
AssertType(typeof(TypeCode?), resolver.ResolveUnaryOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(TypeCode?))));
AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(dynamic))));
AssertError(typeof(object), resolver.ResolveUnaryOperator(UnaryOperatorType.Increment, MakeResult(typeof(object))));
}
[Test]
@ -45,6 +54,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -45,6 +54,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
AssertConstant((uint)1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((uint)1)));
AssertConstant(1L, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((long)1)));
AssertConstant((ulong)1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((ulong)1)));
AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeResult(typeof(dynamic))));
}
[Test]
@ -57,6 +68,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -57,6 +68,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
AssertConstant(-1.0, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(1.0)));
AssertConstant(1m, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(-1m)));
AssertConstant(-65, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant('A')));
AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeResult(typeof(dynamic))));
}
[Test]
@ -69,10 +82,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -69,10 +82,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public void TestUnaryMinusCheckedOverflow()
{
resolver.IsCheckedContext = true;
ResolveResult rr = resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(-2147483648));
Assert.AreEqual("System.Int32", rr.Type.DotNetName);
Assert.IsTrue(rr.IsError);
Assert.IsFalse(rr.IsCompileTimeConstant);
AssertError(typeof(int), resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(-2147483648)));
}
[Test]
@ -88,6 +98,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -88,6 +98,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
AssertConstant(~(long)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((long)1)));
AssertConstant(~(ulong)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((ulong)1)));
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant(1.0)).IsError);
AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeResult(typeof(dynamic))));
AssertType(typeof(long), resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeResult(typeof(uint))));
}
[Test]
@ -95,11 +108,29 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -95,11 +108,29 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
AssertConstant(true, resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeConstant(false)));
AssertConstant(false, resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeConstant(true)));
AssertType(typeof(dynamic), resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeResult(typeof(dynamic))));
}
[Test]
public void TestInvalidUnaryOperatorsOnEnum()
{
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeConstant(StringComparison.Ordinal)).IsError);
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant(StringComparison.Ordinal)).IsError);
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(StringComparison.Ordinal)).IsError);
}
[Test]
public void TestBitwiseNotOnEnum()
{
AssertConstant(~StringComparison.Ordinal, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant(StringComparison.Ordinal)));
AssertConstant(~StringComparison.CurrentCultureIgnoreCase, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant(StringComparison.CurrentCultureIgnoreCase)));
AssertType(typeof(StringComparison), resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeResult(typeof(StringComparison))));
AssertType(typeof(StringComparison?), resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeResult(typeof(StringComparison?))));
}
void Test(char a)
void Test(LoaderOptimization a)
{
var x = -a;
var x = ~a;
x.GetType();
}
}

2
ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -43,7 +43,9 @@ @@ -43,7 +43,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CSharp\Resolver\CastTests.cs" />
<Compile Include="CSharp\Resolver\ConversionsTest.cs" />
<Compile Include="CSharp\Resolver\ResolverTestBase.cs" />
<Compile Include="CSharp\Resolver\UnaryOperatorTests.cs" />
<Compile Include="FormattingTests\TestBraceStlye.cs" />
<Compile Include="FormattingTests\TestFormattingBugs.cs" />

447
ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs

@ -181,9 +181,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -181,9 +181,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
// If the type is nullable, get the underlying type:
IType type = NullableType.GetUnderlyingType(expression.Type);
bool isNullable = type != null;
if (!isNullable)
type = expression.Type;
bool isNullable = NullableType.IsNullable(expression.Type);
// the operator is overloadable:
// TODO: implicit support for user operators
@ -198,10 +196,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -198,10 +196,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// C# 4.0 spec: §7.6.9 Postfix increment and decrement operators
// C# 4.0 spec: §7.7.5 Prefix increment and decrement operators
TypeCode code = ReflectionHelper.GetTypeCode(type);
if (type.IsEnum() || (code >= TypeCode.SByte && code <= TypeCode.Decimal))
if ((code >= TypeCode.SByte && code <= TypeCode.Decimal) || type.IsEnum())
return new ResolveResult(expression.Type);
else
return ErrorResult;
return new ErrorResolveResult(expression.Type);
case UnaryOperatorType.Plus:
methodGroup = unaryPlusOperators;
break;
@ -217,7 +215,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -217,7 +215,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// evaluate as (E)(~(U)x);
var U = expression.ConstantValue.GetType().ToTypeReference().Resolve(context);
var unpackedEnum = new ConstantResolveResult(U, expression.ConstantValue);
return ResolveCast(expression.Type, ResolveUnaryOperator(op, unpackedEnum));
return CheckErrorAndResolveCast(expression.Type, ResolveUnaryOperator(op, unpackedEnum));
} else {
return new ResolveResult(expression.Type);
}
@ -239,7 +237,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -239,7 +237,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} else if (expression.IsCompileTimeConstant && !isNullable) {
object val;
try {
val = m.Invoke(expression.ConstantValue);
val = m.Invoke(this, expression.ConstantValue);
} catch (OverflowException) {
return new ErrorResolveResult(resultType);
}
@ -273,7 +271,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -273,7 +271,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
abstract class UnaryOperatorMethod : OperatorMethod
{
public abstract object Invoke(object input);
public abstract object Invoke(CSharpResolver resolver, object input);
}
sealed class LambdaUnaryOperatorMethod<T> : UnaryOperatorMethod
@ -287,9 +285,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -287,9 +285,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.func = func;
}
public override object Invoke(object input)
public override object Invoke(CSharpResolver resolver, object input)
{
return func((T)Convert.ChangeType(input, typeof(T)));
return func((T)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T)), input));
}
}
@ -304,12 +302,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -304,12 +302,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.Parameters.Add(new DefaultParameter(NullableType.Create(baseMethod.Parameters[0].Type), string.Empty));
}
public override object Invoke(object input)
public override object Invoke(CSharpResolver resolver, object input)
{
if (input == null)
return null;
else
return baseMethod.Invoke(input);
return baseMethod.Invoke(resolver, input);
}
}
@ -364,6 +362,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -364,6 +362,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
object GetUserUnaryOperatorCandidates()
{
// C# 4.0 spec: §7.3.5 Candidate user-defined operators
// TODO: implement user-defined operators
throw new NotImplementedException();
}
#endregion
@ -371,16 +370,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -371,16 +370,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region ResolveCast
public ResolveResult ResolveCast(IType targetType, ResolveResult expression)
{
if (expression.IsError)
return new ErrorResolveResult(targetType);
// C# 4.0 spec: §7.7.6 Cast expressions
if (expression.IsCompileTimeConstant) {
TypeCode code = ReflectionHelper.GetTypeCode(targetType);
if (code >= TypeCode.Boolean && code <= TypeCode.Decimal) {
if (code >= TypeCode.Boolean && code <= TypeCode.Decimal && expression.ConstantValue != null) {
try {
return new ConstantResolveResult(targetType, Convert.ChangeType(expression.ConstantValue, code, CultureInfo.InvariantCulture));
} catch (InvalidCastException) {
return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue));
} catch (OverflowException) {
return new ErrorResolveResult(targetType);
}
} else if (code == TypeCode.String) {
@ -390,10 +386,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -390,10 +386,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return new ErrorResolveResult(targetType);
} else if (targetType.IsEnum()) {
code = ReflectionHelper.GetTypeCode(targetType.GetEnumUnderlyingType(context));
if (code >= TypeCode.SByte && code <= TypeCode.UInt64) {
if (code >= TypeCode.SByte && code <= TypeCode.UInt64 && expression.ConstantValue != null) {
try {
return new ConstantResolveResult(targetType, Convert.ChangeType(expression.ConstantValue, code, CultureInfo.InvariantCulture));
} catch (InvalidCastException) {
return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue));
} catch (OverflowException) {
return new ErrorResolveResult(targetType);
}
}
@ -401,6 +397,415 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -401,6 +397,415 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
return new ResolveResult(targetType);
}
ResolveResult CheckErrorAndResolveCast(IType targetType, ResolveResult expression)
{
if (expression.IsError)
return expression;
else
return ResolveCast(targetType, expression);
}
#region CSharpPrimitiveCast
/// <summary>
/// Performs a conversion between primitive types.
/// Unfortunately we cannot use Convert.ChangeType because it has different semantics
/// (e.g. rounding behavior for floats, overflow, etc.), so we write down every possible primitive C# cast
/// and let the compiler figure out the exact semantics.
/// And we have to do everything twice, once in a checked-block, once in an unchecked-block.
/// </summary>
object CSharpPrimitiveCast(TypeCode targetType, object input)
{
if (IsCheckedContext)
return CSharpPrimitiveCastChecked(targetType, input);
else
return CSharpPrimitiveCastUnchecked(targetType, input);
}
static object CSharpPrimitiveCastChecked(TypeCode targetType, object input)
{
checked {
TypeCode sourceType = Type.GetTypeCode(input.GetType());
if (sourceType == targetType)
return input;
switch (targetType) {
case TypeCode.Char:
switch (sourceType) {
case TypeCode.SByte: return (char)(sbyte)input;
case TypeCode.Byte: return (char)(byte)input;
case TypeCode.Int16: return (char)(short)input;
case TypeCode.UInt16: return (char)(ushort)input;
case TypeCode.Int32: return (char)(int)input;
case TypeCode.UInt32: return (char)(uint)input;
case TypeCode.Int64: return (char)(long)input;
case TypeCode.UInt64: return (char)(ulong)input;
case TypeCode.Single: return (char)(float)input;
case TypeCode.Double: return (char)(double)input;
case TypeCode.Decimal: return (char)(decimal)input;
}
break;
case TypeCode.SByte:
switch (sourceType) {
case TypeCode.Char: return (sbyte)(char)input;
case TypeCode.Byte: return (sbyte)(byte)input;
case TypeCode.Int16: return (sbyte)(short)input;
case TypeCode.UInt16: return (sbyte)(ushort)input;
case TypeCode.Int32: return (sbyte)(int)input;
case TypeCode.UInt32: return (sbyte)(uint)input;
case TypeCode.Int64: return (sbyte)(long)input;
case TypeCode.UInt64: return (sbyte)(ulong)input;
case TypeCode.Single: return (sbyte)(float)input;
case TypeCode.Double: return (sbyte)(double)input;
case TypeCode.Decimal: return (sbyte)(decimal)input;
}
break;
case TypeCode.Byte:
switch (sourceType) {
case TypeCode.Char: return (byte)(char)input;
case TypeCode.SByte: return (byte)(sbyte)input;
case TypeCode.Int16: return (byte)(short)input;
case TypeCode.UInt16: return (byte)(ushort)input;
case TypeCode.Int32: return (byte)(int)input;
case TypeCode.UInt32: return (byte)(uint)input;
case TypeCode.Int64: return (byte)(long)input;
case TypeCode.UInt64: return (byte)(ulong)input;
case TypeCode.Single: return (byte)(float)input;
case TypeCode.Double: return (byte)(double)input;
case TypeCode.Decimal: return (byte)(decimal)input;
}
break;
case TypeCode.Int16:
switch (sourceType) {
case TypeCode.Char: return (short)(char)input;
case TypeCode.SByte: return (short)(sbyte)input;
case TypeCode.Byte: return (short)(byte)input;
case TypeCode.UInt16: return (short)(ushort)input;
case TypeCode.Int32: return (short)(int)input;
case TypeCode.UInt32: return (short)(uint)input;
case TypeCode.Int64: return (short)(long)input;
case TypeCode.UInt64: return (short)(ulong)input;
case TypeCode.Single: return (short)(float)input;
case TypeCode.Double: return (short)(double)input;
case TypeCode.Decimal: return (short)(decimal)input;
}
break;
case TypeCode.UInt16:
switch (sourceType) {
case TypeCode.Char: return (ushort)(char)input;
case TypeCode.SByte: return (ushort)(sbyte)input;
case TypeCode.Byte: return (ushort)(byte)input;
case TypeCode.Int16: return (ushort)(short)input;
case TypeCode.Int32: return (ushort)(int)input;
case TypeCode.UInt32: return (ushort)(uint)input;
case TypeCode.Int64: return (ushort)(long)input;
case TypeCode.UInt64: return (ushort)(ulong)input;
case TypeCode.Single: return (ushort)(float)input;
case TypeCode.Double: return (ushort)(double)input;
case TypeCode.Decimal: return (ushort)(decimal)input;
}
break;
case TypeCode.Int32:
switch (sourceType) {
case TypeCode.Char: return (int)(char)input;
case TypeCode.SByte: return (int)(sbyte)input;
case TypeCode.Byte: return (int)(byte)input;
case TypeCode.Int16: return (int)(short)input;
case TypeCode.UInt16: return (int)(ushort)input;
case TypeCode.UInt32: return (int)(uint)input;
case TypeCode.Int64: return (int)(long)input;
case TypeCode.UInt64: return (int)(ulong)input;
case TypeCode.Single: return (int)(float)input;
case TypeCode.Double: return (int)(double)input;
case TypeCode.Decimal: return (int)(decimal)input;
}
break;
case TypeCode.UInt32:
switch (sourceType) {
case TypeCode.Char: return (uint)(char)input;
case TypeCode.SByte: return (uint)(sbyte)input;
case TypeCode.Byte: return (uint)(byte)input;
case TypeCode.Int16: return (uint)(short)input;
case TypeCode.UInt16: return (uint)(ushort)input;
case TypeCode.Int32: return (uint)(int)input;
case TypeCode.Int64: return (uint)(long)input;
case TypeCode.UInt64: return (uint)(ulong)input;
case TypeCode.Single: return (uint)(float)input;
case TypeCode.Double: return (uint)(double)input;
case TypeCode.Decimal: return (uint)(decimal)input;
}
break;
case TypeCode.Int64:
switch (sourceType) {
case TypeCode.Char: return (long)(char)input;
case TypeCode.SByte: return (long)(sbyte)input;
case TypeCode.Byte: return (long)(byte)input;
case TypeCode.Int16: return (long)(short)input;
case TypeCode.UInt16: return (long)(ushort)input;
case TypeCode.Int32: return (long)(int)input;
case TypeCode.UInt32: return (long)(uint)input;
case TypeCode.UInt64: return (long)(ulong)input;
case TypeCode.Single: return (long)(float)input;
case TypeCode.Double: return (long)(double)input;
case TypeCode.Decimal: return (long)(decimal)input;
}
break;
case TypeCode.UInt64:
switch (sourceType) {
case TypeCode.Char: return (ulong)(char)input;
case TypeCode.SByte: return (ulong)(sbyte)input;
case TypeCode.Byte: return (ulong)(byte)input;
case TypeCode.Int16: return (ulong)(short)input;
case TypeCode.UInt16: return (ulong)(ushort)input;
case TypeCode.Int32: return (ulong)(int)input;
case TypeCode.UInt32: return (ulong)(uint)input;
case TypeCode.Int64: return (ulong)(long)input;
case TypeCode.Single: return (ulong)(float)input;
case TypeCode.Double: return (ulong)(double)input;
case TypeCode.Decimal: return (ulong)(decimal)input;
}
break;
case TypeCode.Single:
switch (sourceType) {
case TypeCode.Char: return (float)(char)input;
case TypeCode.SByte: return (float)(sbyte)input;
case TypeCode.Byte: return (float)(byte)input;
case TypeCode.Int16: return (float)(short)input;
case TypeCode.UInt16: return (float)(ushort)input;
case TypeCode.Int32: return (float)(int)input;
case TypeCode.UInt32: return (float)(uint)input;
case TypeCode.Int64: return (float)(long)input;
case TypeCode.UInt64: return (float)(ulong)input;
case TypeCode.Double: return (float)(double)input;
case TypeCode.Decimal: return (float)(decimal)input;
}
break;
case TypeCode.Double:
switch (sourceType) {
case TypeCode.Char: return (double)(char)input;
case TypeCode.SByte: return (double)(sbyte)input;
case TypeCode.Byte: return (double)(byte)input;
case TypeCode.Int16: return (double)(short)input;
case TypeCode.UInt16: return (double)(ushort)input;
case TypeCode.Int32: return (double)(int)input;
case TypeCode.UInt32: return (double)(uint)input;
case TypeCode.Int64: return (double)(long)input;
case TypeCode.UInt64: return (double)(ulong)input;
case TypeCode.Single: return (double)(float)input;
case TypeCode.Decimal: return (double)(decimal)input;
}
break;
case TypeCode.Decimal:
switch (sourceType) {
case TypeCode.Char: return (decimal)(char)input;
case TypeCode.SByte: return (decimal)(sbyte)input;
case TypeCode.Byte: return (decimal)(byte)input;
case TypeCode.Int16: return (decimal)(short)input;
case TypeCode.UInt16: return (decimal)(ushort)input;
case TypeCode.Int32: return (decimal)(int)input;
case TypeCode.UInt32: return (decimal)(uint)input;
case TypeCode.Int64: return (decimal)(long)input;
case TypeCode.UInt64: return (decimal)(ulong)input;
case TypeCode.Single: return (decimal)(float)input;
case TypeCode.Double: return (decimal)(double)input;
}
break;
}
throw new InvalidCastException("Cast from " + sourceType + " to " + targetType + "not supported.");
}
}
static object CSharpPrimitiveCastUnchecked(TypeCode targetType, object input)
{
unchecked {
TypeCode sourceType = Type.GetTypeCode(input.GetType());
if (sourceType == targetType)
return input;
switch (targetType) {
case TypeCode.Char:
switch (sourceType) {
case TypeCode.SByte: return (char)(sbyte)input;
case TypeCode.Byte: return (char)(byte)input;
case TypeCode.Int16: return (char)(short)input;
case TypeCode.UInt16: return (char)(ushort)input;
case TypeCode.Int32: return (char)(int)input;
case TypeCode.UInt32: return (char)(uint)input;
case TypeCode.Int64: return (char)(long)input;
case TypeCode.UInt64: return (char)(ulong)input;
case TypeCode.Single: return (char)(float)input;
case TypeCode.Double: return (char)(double)input;
case TypeCode.Decimal: return (char)(decimal)input;
}
break;
case TypeCode.SByte:
switch (sourceType) {
case TypeCode.Char: return (sbyte)(char)input;
case TypeCode.Byte: return (sbyte)(byte)input;
case TypeCode.Int16: return (sbyte)(short)input;
case TypeCode.UInt16: return (sbyte)(ushort)input;
case TypeCode.Int32: return (sbyte)(int)input;
case TypeCode.UInt32: return (sbyte)(uint)input;
case TypeCode.Int64: return (sbyte)(long)input;
case TypeCode.UInt64: return (sbyte)(ulong)input;
case TypeCode.Single: return (sbyte)(float)input;
case TypeCode.Double: return (sbyte)(double)input;
case TypeCode.Decimal: return (sbyte)(decimal)input;
}
break;
case TypeCode.Byte:
switch (sourceType) {
case TypeCode.Char: return (byte)(char)input;
case TypeCode.SByte: return (byte)(sbyte)input;
case TypeCode.Int16: return (byte)(short)input;
case TypeCode.UInt16: return (byte)(ushort)input;
case TypeCode.Int32: return (byte)(int)input;
case TypeCode.UInt32: return (byte)(uint)input;
case TypeCode.Int64: return (byte)(long)input;
case TypeCode.UInt64: return (byte)(ulong)input;
case TypeCode.Single: return (byte)(float)input;
case TypeCode.Double: return (byte)(double)input;
case TypeCode.Decimal: return (byte)(decimal)input;
}
break;
case TypeCode.Int16:
switch (sourceType) {
case TypeCode.Char: return (short)(char)input;
case TypeCode.SByte: return (short)(sbyte)input;
case TypeCode.Byte: return (short)(byte)input;
case TypeCode.UInt16: return (short)(ushort)input;
case TypeCode.Int32: return (short)(int)input;
case TypeCode.UInt32: return (short)(uint)input;
case TypeCode.Int64: return (short)(long)input;
case TypeCode.UInt64: return (short)(ulong)input;
case TypeCode.Single: return (short)(float)input;
case TypeCode.Double: return (short)(double)input;
case TypeCode.Decimal: return (short)(decimal)input;
}
break;
case TypeCode.UInt16:
switch (sourceType) {
case TypeCode.Char: return (ushort)(char)input;
case TypeCode.SByte: return (ushort)(sbyte)input;
case TypeCode.Byte: return (ushort)(byte)input;
case TypeCode.Int16: return (ushort)(short)input;
case TypeCode.Int32: return (ushort)(int)input;
case TypeCode.UInt32: return (ushort)(uint)input;
case TypeCode.Int64: return (ushort)(long)input;
case TypeCode.UInt64: return (ushort)(ulong)input;
case TypeCode.Single: return (ushort)(float)input;
case TypeCode.Double: return (ushort)(double)input;
case TypeCode.Decimal: return (ushort)(decimal)input;
}
break;
case TypeCode.Int32:
switch (sourceType) {
case TypeCode.Char: return (int)(char)input;
case TypeCode.SByte: return (int)(sbyte)input;
case TypeCode.Byte: return (int)(byte)input;
case TypeCode.Int16: return (int)(short)input;
case TypeCode.UInt16: return (int)(ushort)input;
case TypeCode.UInt32: return (int)(uint)input;
case TypeCode.Int64: return (int)(long)input;
case TypeCode.UInt64: return (int)(ulong)input;
case TypeCode.Single: return (int)(float)input;
case TypeCode.Double: return (int)(double)input;
case TypeCode.Decimal: return (int)(decimal)input;
}
break;
case TypeCode.UInt32:
switch (sourceType) {
case TypeCode.Char: return (uint)(char)input;
case TypeCode.SByte: return (uint)(sbyte)input;
case TypeCode.Byte: return (uint)(byte)input;
case TypeCode.Int16: return (uint)(short)input;
case TypeCode.UInt16: return (uint)(ushort)input;
case TypeCode.Int32: return (uint)(int)input;
case TypeCode.Int64: return (uint)(long)input;
case TypeCode.UInt64: return (uint)(ulong)input;
case TypeCode.Single: return (uint)(float)input;
case TypeCode.Double: return (uint)(double)input;
case TypeCode.Decimal: return (uint)(decimal)input;
}
break;
case TypeCode.Int64:
switch (sourceType) {
case TypeCode.Char: return (long)(char)input;
case TypeCode.SByte: return (long)(sbyte)input;
case TypeCode.Byte: return (long)(byte)input;
case TypeCode.Int16: return (long)(short)input;
case TypeCode.UInt16: return (long)(ushort)input;
case TypeCode.Int32: return (long)(int)input;
case TypeCode.UInt32: return (long)(uint)input;
case TypeCode.UInt64: return (long)(ulong)input;
case TypeCode.Single: return (long)(float)input;
case TypeCode.Double: return (long)(double)input;
case TypeCode.Decimal: return (long)(decimal)input;
}
break;
case TypeCode.UInt64:
switch (sourceType) {
case TypeCode.Char: return (ulong)(char)input;
case TypeCode.SByte: return (ulong)(sbyte)input;
case TypeCode.Byte: return (ulong)(byte)input;
case TypeCode.Int16: return (ulong)(short)input;
case TypeCode.UInt16: return (ulong)(ushort)input;
case TypeCode.Int32: return (ulong)(int)input;
case TypeCode.UInt32: return (ulong)(uint)input;
case TypeCode.Int64: return (ulong)(long)input;
case TypeCode.Single: return (ulong)(float)input;
case TypeCode.Double: return (ulong)(double)input;
case TypeCode.Decimal: return (ulong)(decimal)input;
}
break;
case TypeCode.Single:
switch (sourceType) {
case TypeCode.Char: return (float)(char)input;
case TypeCode.SByte: return (float)(sbyte)input;
case TypeCode.Byte: return (float)(byte)input;
case TypeCode.Int16: return (float)(short)input;
case TypeCode.UInt16: return (float)(ushort)input;
case TypeCode.Int32: return (float)(int)input;
case TypeCode.UInt32: return (float)(uint)input;
case TypeCode.Int64: return (float)(long)input;
case TypeCode.UInt64: return (float)(ulong)input;
case TypeCode.Double: return (float)(double)input;
case TypeCode.Decimal: return (float)(decimal)input;
}
break;
case TypeCode.Double:
switch (sourceType) {
case TypeCode.Char: return (double)(char)input;
case TypeCode.SByte: return (double)(sbyte)input;
case TypeCode.Byte: return (double)(byte)input;
case TypeCode.Int16: return (double)(short)input;
case TypeCode.UInt16: return (double)(ushort)input;
case TypeCode.Int32: return (double)(int)input;
case TypeCode.UInt32: return (double)(uint)input;
case TypeCode.Int64: return (double)(long)input;
case TypeCode.UInt64: return (double)(ulong)input;
case TypeCode.Single: return (double)(float)input;
case TypeCode.Decimal: return (double)(decimal)input;
}
break;
case TypeCode.Decimal:
switch (sourceType) {
case TypeCode.Char: return (decimal)(char)input;
case TypeCode.SByte: return (decimal)(sbyte)input;
case TypeCode.Byte: return (decimal)(byte)input;
case TypeCode.Int16: return (decimal)(short)input;
case TypeCode.UInt16: return (decimal)(ushort)input;
case TypeCode.Int32: return (decimal)(int)input;
case TypeCode.UInt32: return (decimal)(uint)input;
case TypeCode.Int64: return (decimal)(long)input;
case TypeCode.UInt64: return (decimal)(ulong)input;
case TypeCode.Single: return (decimal)(float)input;
case TypeCode.Double: return (decimal)(double)input;
}
break;
}
throw new InvalidCastException("Cast from " + sourceType + " to " + targetType + "not supported.");
}
}
#endregion
#endregion
}
}

13
ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs

@ -130,8 +130,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -130,8 +130,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// C# 4.0 spec: §6.1.3
TypeCode constantType = ReflectionHelper.GetTypeCode(rr.Type);
if (constantType >= TypeCode.SByte && constantType <= TypeCode.Decimal && Convert.ToDecimal(rr.ConstantValue) == 0) {
toType = NullableType.GetUnderlyingType(toType) ?? toType;
return toType.IsEnum();
return NullableType.GetUnderlyingType(toType).IsEnum();
}
return false;
}
@ -141,9 +140,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -141,9 +140,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
bool ImplicitNullableConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.4
IType t = NullableType.GetUnderlyingType(toType);
if (t != null) {
IType s = NullableType.GetUnderlyingType(fromType) ?? fromType;
if (NullableType.IsNullable(toType)) {
IType t = NullableType.GetUnderlyingType(toType);
IType s = NullableType.GetUnderlyingType(fromType); // might or might not be nullable
return IdentityConversion(s, t) || ImplicitNumericConversion(s, t);
} else {
return false;
@ -260,7 +259,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -260,7 +259,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
bool BoxingConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.7
fromType = NullableType.GetUnderlyingType(fromType) ?? fromType;
fromType = NullableType.GetUnderlyingType(fromType);
return fromType.IsReferenceType == false && toType.IsReferenceType == true && IsSubtypeOf(fromType, toType);
}
#endregion
@ -278,7 +277,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -278,7 +277,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
// C# 4.0 spec: §6.1.9
TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type);
TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType) ?? toType);
TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType));
if (fromTypeCode == TypeCode.Int64) {
long val = (long)rr.ConstantValue;
return val >= 0 && toTypeCode == TypeCode.UInt64;

9
ICSharpCode.NRefactory/TypeSystem/NullableType.cs

@ -16,12 +16,15 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -16,12 +16,15 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
public static bool IsNullable(IType type)
{
return GetUnderlyingType(type) != null;
if (type == null)
throw new ArgumentNullException("type");
ParameterizedType pt = type as ParameterizedType;
return pt != null && pt.TypeArguments.Count == 1 && pt.FullName == "System.Nullable";
}
/// <summary>
/// Returns the element type, if <paramref name="type"/> is a nullable type.
/// Otherwise, returns null.
/// Otherwise, returns the type itself.
/// </summary>
public static IType GetUnderlyingType(IType type)
{
@ -31,7 +34,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -31,7 +34,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
if (pt != null && pt.TypeArguments.Count == 1 && pt.FullName == "System.Nullable")
return pt.TypeArguments[0];
else
return null;
return type;
}
/// <summary>

14
ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs

@ -12,6 +12,16 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -12,6 +12,16 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
public static class ReflectionHelper
{
/// <summary>
/// A reflection class used to represent <c>null</c>.
/// </summary>
public sealed class Null {}
/// <summary>
/// A reflection class used to represent <c>dynamic</c>.
/// </summary>
public sealed class Dynamic {}
/// <summary>
/// Retrieves a class.
/// </summary>
@ -89,6 +99,10 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -89,6 +99,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
return SharedTypes.UnknownType;
}
} else if (type.DeclaringType != null) {
if (type == typeof(Dynamic))
return SharedTypes.Dynamic;
else if (type == typeof(Null))
return SharedTypes.Null;
ITypeReference baseTypeRef = ToTypeReference(type.DeclaringType, entity);
int typeParameterCount;
string name = SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount);

18
README

@ -8,3 +8,21 @@ ICSharpCode.NRefactory.TypeSystem.Implementation: @@ -8,3 +8,21 @@ ICSharpCode.NRefactory.TypeSystem.Implementation:
ICSharpCode.NRefactory.CSharp.Dom:
Abstract Syntax Tree for C#
ICSharpCode.NRefactory.CSharp.Resolver:
Semantic analysis for C#
Null-Object pattern:
The NRefactory library makes extensive use of the null object pattern.
As a reult, NullReferenceExceptions should be very rare when working with this library.
In the type system, both ITypeReference and IType use SharedTypes.UnknownType to represent unknown types.
Unless the method is documented otherwise, no method or property returning a ITypeReference or IType will return null.
When adding to this library, to try to keep such uses of null rare.
Note that the null object pattern is not used for ITypeDefinition:
IProjectContent.GetClass() returns null when a type is not found. Take care to abort your operation or substitute UnknownType
instead of passing the null to code expecting an IType.
The pattern also extends to the C# resolver, which always produces a ResolveResult, even in error cases.
Use ResolveResult.IsError to detect resolver errors. Also note that many resolver errors still have a meaningful type attached,
this allows code completion to work in the presence of minor semantic errors.

18
doc/TODO

@ -1,13 +1,15 @@ @@ -1,13 +1,15 @@
TypeSystem API:
* ITypeReference extends IFreezable?
either:
- make sure all ITypeReference implementations correctly freeze their contained types
or:
- remove IFreezable from type references
* For clarity: move all ITypeReference members except for Resolve() to IType?
* Decide on the fate of ISupportsInterning
* Decide on the fate of ISupportsInterning (depends on how we're going to implement persistence)
* Try to build SharedTypes for void, int, etc.
Take care of equality with the real System.Void, System.Int32 etc.
This seems to be hard/impossible to do, see comment in SharedTypes.cs.
I'm trying to work without those types now.
Note that having shared type *references* is possible (typeof(int).ToTypeReference())
* Implement the C# parser producing the DOM
* Implement ResolveVisitor
* Implement all the nasty context-dependent stuff (local variables, lambdas) that CSharpResolver doesn't do (yet)
Where should that go? I'd like to keep it out of CSharpResolver, that class is bloated enough with the pure logic.

Loading…
Cancel
Save