Browse Source

Fixed issues with LogicalAnd and LogicalOr operators.

Add unit tests for overload resolution; fixed an overload resolution bug.
Added some new helper methods.
Various documentation updates.
newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
24e7c50e32
  1. 2
      .gitignore
  2. 29
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs
  3. 106
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/OverloadResolutionTests.cs
  4. 7
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs
  5. 2
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  6. 119
      ICSharpCode.NRefactory.Tests/TypeSystem/ReflectionHelperTests.cs
  7. 68
      ICSharpCode.NRefactory.Tests/Util/CSharpPrimitiveCastTests.cs
  8. 87
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  9. 36
      ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs
  10. 16
      ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs
  11. 9
      ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolutionErrors.cs
  12. 1
      ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  13. 11
      ICSharpCode.NRefactory/TypeSystem/ArrayType.cs
  14. 21
      ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs
  15. 4
      ICSharpCode.NRefactory/TypeSystem/INamedElement.cs
  16. 12
      ICSharpCode.NRefactory/TypeSystem/IType.cs
  17. 2
      ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs
  18. 9
      ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs
  19. 15
      README

2
.gitignore vendored

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
/lib/*.dll
/ICSharpCode.NRefactory.Tests/PartCover/*

29
ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs

@ -257,6 +257,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -257,6 +257,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
AssertConstant(false, resolver.ResolveBinaryOperator(
BinaryOperatorType.GreaterThan, MakeConstant(StringComparison.CurrentCultureIgnoreCase), MakeConstant(StringComparison.Ordinal)));
AssertType(typeof(bool), resolver.ResolveBinaryOperator(
BinaryOperatorType.GreaterThan, MakeResult(typeof(StringComparison?)), MakeResult(typeof(StringComparison?))));
}
[Test]
@ -310,6 +313,32 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -310,6 +313,32 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
BinaryOperatorType.BitwiseOr, MakeConstant(0), MakeConstant(AttributeTargets.Field)));
}
[Test]
public void LogicalAnd()
{
AssertConstant(true, resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalAnd, MakeConstant(true), MakeConstant(true)));
AssertConstant(false, resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalAnd, MakeConstant(false), MakeConstant(true)));
AssertError(typeof(bool), resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalAnd, MakeConstant(false), MakeResult(typeof(bool?))));
}
[Test]
public void LogicalOr()
{
AssertConstant(true, resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalOr, MakeConstant(false), MakeConstant(true)));
AssertConstant(false, resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalOr, MakeConstant(false), MakeConstant(false)));
AssertError(typeof(bool), resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalOr, MakeConstant(false), MakeResult(typeof(bool?))));
}
[Test]
public void NullCoalescing()
{

106
ICSharpCode.NRefactory.Tests/CSharp/Resolver/OverloadResolutionTests.cs

@ -0,0 +1,106 @@ @@ -0,0 +1,106 @@
// 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 System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
[TestFixture]
public class OverloadResolutionTests
{
readonly ITypeResolveContext context = CecilLoaderTests.Mscorlib;
readonly DefaultTypeDefinition dummyClass = new DefaultTypeDefinition(CecilLoaderTests.Mscorlib, string.Empty, "DummyClass");
ResolveResult[] MakeArgumentList(params Type[] argumentTypes)
{
return argumentTypes.Select(t => new ResolveResult(t.ToTypeReference().Resolve(context))).ToArray();
}
DefaultMethod MakeMethod(params Type[] parameterTypes)
{
DefaultMethod m = new DefaultMethod(dummyClass, "Method");
foreach (var type in parameterTypes) {
m.Parameters.Add(new DefaultParameter(type.ToTypeReference(), string.Empty));
}
return m;
}
DefaultMethod MakeParamsMethod(params Type[] parameterTypes)
{
DefaultMethod m = MakeMethod(parameterTypes);
((DefaultParameter)m.Parameters.Last()).IsParams = true;
return m;
}
[Test]
public void PreferIntOverUInt()
{
OverloadResolution r = new OverloadResolution(context, MakeArgumentList(typeof(ushort)));
var c1 = MakeMethod(typeof(int));
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(c1));
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeMethod(typeof(uint))));
Assert.IsFalse(r.IsAmbiguous);
Assert.AreSame(c1, r.BestCandidate);
}
[Test]
public void NullableIntAndNullableUIntIsAmbiguous()
{
OverloadResolution r = new OverloadResolution(context, MakeArgumentList(typeof(ushort?)));
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeMethod(typeof(int?))));
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeMethod(typeof(uint?))));
Assert.AreEqual(OverloadResolutionErrors.AmbiguousMatch, r.BestCandidateErrors);
// then adding a matching overload solves the ambiguity:
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeMethod(typeof(ushort?))));
Assert.AreEqual(OverloadResolutionErrors.None, r.BestCandidateErrors);
Assert.IsNull(r.BestCandidateAmbiguousWith);
}
[Test]
public void ParamsMethodMatchesEmptyArgumentList()
{
OverloadResolution r = new OverloadResolution(context, MakeArgumentList());
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeParamsMethod(typeof(int[]))));
Assert.IsTrue(r.BestCandidateIsExpandedForm);
}
[Test]
public void ParamsMethodMatchesOneArgumentInExpandedForm()
{
OverloadResolution r = new OverloadResolution(context, MakeArgumentList(typeof(int)));
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeParamsMethod(typeof(int[]))));
Assert.IsTrue(r.BestCandidateIsExpandedForm);
}
[Test]
public void ParamsMethodMatchesInUnexpandedForm()
{
OverloadResolution r = new OverloadResolution(context, MakeArgumentList(typeof(int[])));
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeParamsMethod(typeof(int[]))));
Assert.IsFalse(r.BestCandidateIsExpandedForm);
}
[Test]
public void LessArgumentsPassedToParamsIsBetter()
{
OverloadResolution r = new OverloadResolution(context, MakeArgumentList(typeof(int), typeof(int), typeof(int)));
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeParamsMethod(typeof(int[]))));
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeParamsMethod(typeof(int), typeof(int[]))));
Assert.IsFalse(r.IsAmbiguous);
Assert.AreEqual(2, r.BestCandidate.Parameters.Count);
}
[Test]
public void CallInvalidParamsDeclaration()
{
OverloadResolution r = new OverloadResolution(context, MakeArgumentList(typeof(int[,])));
Assert.AreEqual(OverloadResolutionErrors.ArgumentTypeMismatch, r.AddCandidate(MakeParamsMethod(typeof(int))));
Assert.IsFalse(r.BestCandidateIsExpandedForm);
}
}
}

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

@ -13,6 +13,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -13,6 +13,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public abstract class ResolverTestBase
{
protected readonly IProjectContent mscorlib = CecilLoaderTests.Mscorlib;
protected readonly ITypeResolveContext context = CecilLoaderTests.Mscorlib;
protected CSharpResolver resolver;
[SetUp]
@ -23,7 +24,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -23,7 +24,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
protected IType ResolveType(Type type)
{
IType t = type.ToTypeReference().Resolve(mscorlib);
IType t = type.ToTypeReference().Resolve(context);
if (t == SharedTypes.UnknownType)
throw new InvalidOperationException("Could not resolve type");
return t;
@ -63,14 +64,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -63,14 +64,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
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);
Assert.AreEqual(expectedType.ToTypeReference().Resolve(context), 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);
Assert.AreEqual(expectedType.ToTypeReference().Resolve(context), rr.Type);
}
}
}

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

@ -47,6 +47,7 @@ @@ -47,6 +47,7 @@
<Compile Include="CSharp\Resolver\BinaryOperatorTests.cs" />
<Compile Include="CSharp\Resolver\CastTests.cs" />
<Compile Include="CSharp\Resolver\ConversionsTest.cs" />
<Compile Include="CSharp\Resolver\OverloadResolutionTests.cs" />
<Compile Include="CSharp\Resolver\ResolverTestBase.cs" />
<Compile Include="CSharp\Resolver\UnaryOperatorTests.cs" />
<Compile Include="FormattingTests\TestBraceStlye.cs" />
@ -62,6 +63,7 @@ @@ -62,6 +63,7 @@
<Compile Include="TypeSystem\TypeSystemTests.cs" />
<Compile Include="TypeSystem\TypeSystemTests.TestCase.cs" />
<Compile Include="Untested.cs" />
<Compile Include="Util\CSharpPrimitiveCastTests.cs" />
<Compile Include="Util\TreeTraversalTests.cs" />
</ItemGroup>
<ItemGroup>

119
ICSharpCode.NRefactory.Tests/TypeSystem/ReflectionHelperTests.cs

@ -148,7 +148,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -148,7 +148,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
[Test]
public void ParseReflectionNameSimpleTypes()
public void ParseReflectionName()
{
Assert.AreEqual("System.Int32", ReflectionHelper.ParseReflectionName("System.Int32").Resolve(context).ReflectionName);
Assert.AreEqual("System.Int32&", ReflectionHelper.ParseReflectionName("System.Int32&").Resolve(context).ReflectionName);
@ -157,6 +157,123 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -157,6 +157,123 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreEqual("System.Action`1[[System.String]]", ReflectionHelper.ParseReflectionName("System.Action`1[[System.String]]").Resolve(context).ReflectionName);
Assert.AreEqual("System.Action`1[[System.String]]", ReflectionHelper.ParseReflectionName("System.Action`1[[System.String, mscorlib]]").Resolve(context).ReflectionName);
Assert.AreEqual("System.Int32[,,][,]", ReflectionHelper.ParseReflectionName(typeof(int[,][,,]).AssemblyQualifiedName).Resolve(context).ReflectionName);
Assert.AreEqual("System.Environment+SpecialFolder", ReflectionHelper.ParseReflectionName("System.Environment+SpecialFolder").Resolve(context).ReflectionName);
}
[Test]
public void ParseOpenGenericReflectionName()
{
IMethod convertAll = context.GetClass(typeof(List<>)).Methods.Single(m => m.Name == "ConvertAll");
Assert.AreEqual("System.Converter`2[[?],[?]]", ReflectionHelper.ParseReflectionName("System.Converter`2[[`0],[``0]]").Resolve(context).ReflectionName);
Assert.AreEqual("System.Converter`2[[`0],[``0]]", ReflectionHelper.ParseReflectionName("System.Converter`2[[`0],[``0]]", convertAll).Resolve(context).ReflectionName);
}
[Test, ExpectedException(typeof(ArgumentNullException))]
public void ParseNullReflectionName()
{
ReflectionHelper.ParseReflectionName(null);
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName1()
{
ReflectionHelper.ParseReflectionName(string.Empty);
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName2()
{
ReflectionHelper.ParseReflectionName("`");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName3()
{
ReflectionHelper.ParseReflectionName("``");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName4()
{
ReflectionHelper.ParseReflectionName("System.Action`A");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName5()
{
ReflectionHelper.ParseReflectionName("System.Environment+");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName5b()
{
ReflectionHelper.ParseReflectionName("System.Environment+`");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName6()
{
ReflectionHelper.ParseReflectionName("System.Int32[");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName7()
{
ReflectionHelper.ParseReflectionName("System.Int32[`]");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName8()
{
ReflectionHelper.ParseReflectionName("System.Int32[,");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName9()
{
ReflectionHelper.ParseReflectionName("System.Int32]");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName10()
{
ReflectionHelper.ParseReflectionName("System.Int32*a");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName11()
{
ReflectionHelper.ParseReflectionName("System.Action`1[[]]");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName12()
{
ReflectionHelper.ParseReflectionName("System.Action`1[[System.Int32]a]");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName13()
{
ReflectionHelper.ParseReflectionName("System.Action`1[[System.Int32],]");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName14()
{
ReflectionHelper.ParseReflectionName("System.Action`1[[System.Int32]");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName15()
{
ReflectionHelper.ParseReflectionName("System.Action`1[[System.Int32");
}
[Test, ExpectedException(typeof(ReflectionNameParseException))]
public void ParseInvalidReflectionName16()
{
ReflectionHelper.ParseReflectionName("System.Action`1[[System.Int32],[System.String");
}
}
}

68
ICSharpCode.NRefactory.Tests/Util/CSharpPrimitiveCastTests.cs

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
// 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.Util
{
[TestFixture]
public class CSharpPrimitiveCastTests
{
// I know, these tests aren't really clever, more of a way to fake code coverage...
// Well, at least they should ensure the 'tables' in CSharpPrimitiveCast don't contain any typos.
[Test]
public void FloatToInteger()
{
for (int checkedMode = 0; checkedMode < 2; checkedMode++) {
for (TypeCode to = TypeCode.Char; to <= TypeCode.UInt64; to++) {
object val = CSharpPrimitiveCast.Cast(to, 3.9f, checkedMode == 1);
Assert.AreEqual(to, Type.GetTypeCode(val.GetType()));
Assert.AreEqual(3, Convert.ToInt64(val));
}
}
}
[Test]
public void DoubleToInteger()
{
for (int checkedMode = 0; checkedMode < 2; checkedMode++) {
for (TypeCode to = TypeCode.Char; to <= TypeCode.UInt64; to++) {
object val = CSharpPrimitiveCast.Cast(to, 3.9, checkedMode == 1);
Assert.AreEqual(to, Type.GetTypeCode(val.GetType()));
Assert.AreEqual(3, Convert.ToInt64(val));
}
}
}
[Test]
public void DecimalToInteger()
{
for (int checkedMode = 0; checkedMode < 2; checkedMode++) {
for (TypeCode to = TypeCode.Char; to <= TypeCode.UInt64; to++) {
object val = CSharpPrimitiveCast.Cast(to, 3.9m, checkedMode == 1);
Assert.AreEqual(to, Type.GetTypeCode(val.GetType()));
Assert.AreEqual(3, Convert.ToInt64(val));
}
}
}
[Test]
public void IntegerToInteger()
{
for (int checkedMode = 0; checkedMode < 2; checkedMode++) {
for (TypeCode to = TypeCode.Char; to <= TypeCode.UInt64; to++) {
for (TypeCode to2 = TypeCode.Char; to2 <= TypeCode.Decimal; to2++) {
object val = CSharpPrimitiveCast.Cast(to, 3, checkedMode == 1);
Assert.AreEqual(to, Type.GetTypeCode(val.GetType()));
Assert.AreEqual(3, Convert.ToInt64(val));
object val2 = CSharpPrimitiveCast.Cast(to2, val, checkedMode == 1);
Assert.AreEqual(to2, Type.GetTypeCode(val2.GetType()));
Assert.AreEqual(3, Convert.ToInt64(val2));
}
}
}
}
}
}

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

@ -381,7 +381,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -381,7 +381,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
this.baseMethod = baseMethod;
this.ReturnType = NullableType.Create(baseMethod.ReturnType);
this.Parameters.Add(new DefaultParameter(NullableType.Create(baseMethod.Parameters[0].Type), string.Empty));
this.Parameters.Add(MakeNullableParameter(baseMethod.Parameters[0]));
}
public override object Invoke(CSharpResolver resolver, object input)
@ -453,23 +453,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -453,23 +453,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (lhs.Type == SharedTypes.Dynamic || rhs.Type == SharedTypes.Dynamic)
return DynamicResult;
// Handle logical and/or exactly as bitwise and/or:
// - If the user overloads a bitwise operator, that implicitly creates the corresponding logical operator.
// - If both inputs are compile-time constants, it doesn't matter that we don't short-circuit.
// - If inputs aren't compile-time constants, we don't evaluate anything, so again it doesn't matter that we don't short-circuit
if (op == BinaryOperatorType.LogicalAnd) {
op = BinaryOperatorType.BitwiseAnd;
} else if (op == BinaryOperatorType.LogicalOr) {
op = BinaryOperatorType.BitwiseOr;
} else if (op == BinaryOperatorType.NullCoalescing) {
// null coalescing operator is not overloadable and needs to be handled separately
return ResolveNullCoalescingOperator(lhs, rhs);
}
// C# 4.0 spec: §7.3.4 Binary operator overload resolution
string overloadableOperatorName = GetOverloadableOperatorName(op);
if (overloadableOperatorName == null) {
throw new ArgumentException("Invalid value for BinaryOperatorType", "op");
// Handle logical and/or exactly as bitwise and/or:
// - If the user overloads a bitwise operator, that implicitly creates the corresponding logical operator.
// - If both inputs are compile-time constants, it doesn't matter that we don't short-circuit.
// - If inputs aren't compile-time constants, we don't evaluate anything, so again it doesn't matter that we don't short-circuit
if (op == BinaryOperatorType.LogicalAnd) {
overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseAnd);
} else if (op == BinaryOperatorType.LogicalOr) {
overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseOr);
} else if (op == BinaryOperatorType.NullCoalescing) {
// null coalescing operator is not overloadable and needs to be handled separately
return ResolveNullCoalescingOperator(lhs, rhs);
} else {
throw new ArgumentException("Invalid value for BinaryOperatorType", "op");
}
}
// If the type is nullable, get the underlying type:
@ -626,6 +627,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -626,6 +627,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
}
break;
case BinaryOperatorType.LogicalAnd:
methodGroup = logicalAndOperator;
break;
case BinaryOperatorType.LogicalOr:
methodGroup = logicalOrOperator;
break;
default:
throw new InvalidOperationException();
}
@ -896,10 +903,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -896,10 +903,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public override object Invoke(CSharpResolver resolver, object lhs, object rhs)
{
if (lhs == null || rhs == null)
return null;
else
return baseMethod.Invoke(resolver, lhs, rhs);
throw new NotSupportedException(); // cannot use nullables at compile time
}
public IList<IParameter> NonLiftedParameters {
@ -1161,36 +1165,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1161,36 +1165,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public override OperatorMethod Lift()
{
return new LiftedRelationalOperatorMethod(this);
}
}
sealed class LiftedRelationalOperatorMethod : BinaryOperatorMethod, OverloadResolution.ILiftedOperator
{
readonly BinaryOperatorMethod baseMethod;
public LiftedRelationalOperatorMethod(BinaryOperatorMethod baseMethod)
{
this.baseMethod = baseMethod;
this.ReturnType = baseMethod.ReturnType;
this.Parameters.Add(MakeNullableParameter(baseMethod.Parameters[0]));
this.Parameters.Add(MakeNullableParameter(baseMethod.Parameters[1]));
}
public override bool CanEvaluateAtCompileTime {
get { return false; }
}
public override object Invoke(CSharpResolver resolver, object lhs, object rhs)
{
if (lhs == null || rhs == null)
return false;
else
return baseMethod.Invoke(resolver, lhs, rhs);
}
public IList<IParameter> NonLiftedParameters {
get { return baseMethod.Parameters; }
return new LiftedBinaryOperatorMethod(this);
}
}
@ -1236,22 +1211,30 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1236,22 +1211,30 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#endregion
#region Bitwise operators
static readonly OperatorMethod[] logicalAndOperator = {
new LambdaBinaryOperatorMethod<bool, bool> ((a, b) => a & b)
};
static readonly OperatorMethod[] bitwiseAndOperators = Lift(
new LambdaBinaryOperatorMethod<int, int> ((a, b) => a & b),
new LambdaBinaryOperatorMethod<uint, uint> ((a, b) => a & b),
new LambdaBinaryOperatorMethod<long, long> ((a, b) => a & b),
new LambdaBinaryOperatorMethod<ulong, ulong>((a, b) => a & b),
new LambdaBinaryOperatorMethod<bool, bool> ((a, b) => a & b)
logicalAndOperator[0]
);
static readonly OperatorMethod[] logicalOrOperator = {
new LambdaBinaryOperatorMethod<bool, bool> ((a, b) => a | b)
};
static readonly OperatorMethod[] bitwiseOrOperators = Lift(
new LambdaBinaryOperatorMethod<int, int> ((a, b) => a | b),
new LambdaBinaryOperatorMethod<uint, uint> ((a, b) => a | b),
new LambdaBinaryOperatorMethod<long, long> ((a, b) => a | b),
new LambdaBinaryOperatorMethod<ulong, ulong>((a, b) => a | b),
new LambdaBinaryOperatorMethod<bool, bool> ((a, b) => a | b)
logicalOrOperator[0]
);
// Note: the logic for the lifted bool? and bool? is wrong;
// Note: the logic for the lifted bool? bitwise operators is wrong;
// we produce "true | null" = "null" when it should be true. However, this is irrelevant
// because bool? cannot be a compile-time type.

36
ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
// 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;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
/// <summary>
/// Implementation of member lookup (C# 4.0 spec, §7.4).
/// </summary>
public class MemberLookup
{
ITypeResolveContext context;
public MemberLookup(ITypeResolveContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
}
/// <summary>
/// Gets whether the member is considered to be invocable.
/// </summary>
public bool IsInvocable(IMember member)
{
if (member is IEvent || member is IMethod)
return true;
if (member.ReturnType == SharedTypes.Dynamic)
return true;
return member.ReturnType.Resolve(context).IsDelegate();
}
}
}

16
ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs

@ -77,7 +77,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -77,7 +77,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ResolveResult[] arguments;
string[] argumentNames;
Conversions conversions;
List<Candidate> candidates = new List<Candidate>();
//List<Candidate> candidates = new List<Candidate>();
Candidate bestCandidate;
Candidate bestCandidateAmbiguousWith;
@ -107,14 +107,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -107,14 +107,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Candidate c = new Candidate(member, false);
if (CalculateCandidate(c)) {
candidates.Add(c);
//candidates.Add(c);
}
if (member.Parameters.Count > 0 && member.Parameters[member.Parameters.Count - 1].IsParams) {
Candidate expandedCandidate = new Candidate(member, true);
// consider expanded form only if it isn't obviously wrong
if (CalculateCandidate(expandedCandidate)) {
candidates.Add(expandedCandidate);
//candidates.Add(expandedCandidate);
if (expandedCandidate.ErrorCount < c.ErrorCount)
return expandedCandidate.Errors;
@ -144,7 +144,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -144,7 +144,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (candidate.IsExpandedForm && i == candidate.Parameters.Count - 1) {
ArrayType arrayType = type as ArrayType;
if (arrayType != null && arrayType.Dimensions == 1)
type = arrayType;
type = arrayType.ElementType;
else
return false; // error: cannot unpack params-array. abort considering the expanded form for this candidate
}
@ -406,5 +406,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -406,5 +406,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public IParameterizedMember BestCandidateAmbiguousWith {
get { return bestCandidateAmbiguousWith != null ? bestCandidateAmbiguousWith.Member : null; }
}
public bool BestCandidateIsExpandedForm {
get { return bestCandidate != null ? bestCandidate.IsExpandedForm : false; }
}
public bool IsAmbiguous {
get { return bestCandidateAmbiguousWith != null; }
}
}
}

9
ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolutionErrors.cs

@ -9,15 +9,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -9,15 +9,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public enum OverloadResolutionErrors
{
None = 0,
/// <summary>
/// Too many positional arguments (some could not be mapped to any parameter).
/// </summary>
TooManyPositionalArguments = 0x0002,
TooManyPositionalArguments = 0x0001,
/// <summary>
/// A named argument could not be mapped to any parameter
/// </summary>
NoParameterFoundForNamedArgument = 0x0004,
NoParameterFoundForNamedArgument = 0x0002,
/// <summary>
/// Type inference failed for a generic method.
/// </summary>
TypeInferenceFailed = 0x0004,
/// <summary>
/// No argument was mapped to a non-optional parameter
/// </summary>

1
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -144,6 +144,7 @@ @@ -144,6 +144,7 @@
<Compile Include="CSharp\Resolver\Conversions.cs" />
<Compile Include="CSharp\Resolver\CSharpResolver.cs" />
<Compile Include="CSharp\Resolver\ErrorResolveResult.cs" />
<Compile Include="CSharp\Resolver\MemberLookup.cs" />
<Compile Include="CSharp\Resolver\OverloadResolution.cs" />
<Compile Include="CSharp\Resolver\OverloadResolutionErrors.cs" />
<Compile Include="CSharp\Resolver\ResolveResult.cs" />

11
ICSharpCode.NRefactory/TypeSystem/ArrayType.cs

@ -12,9 +12,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -12,9 +12,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
public class ArrayType : TypeWithElementType
{
int dimensions;
readonly int dimensions;
public ArrayType(IType elementType, int dimensions) : base(elementType)
public ArrayType(IType elementType, int dimensions = 1) : base(elementType)
{
if (dimensions <= 0)
throw new ArgumentOutOfRangeException("dimensions", dimensions, "dimensions must be positive");
@ -27,10 +27,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -27,10 +27,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
public override string NameSuffix {
get {
if (dimensions == 0)
return "[]";
else
return "[" + new string(',', dimensions-1) + "]";
return "[" + new string(',', dimensions-1) + "]";
}
}
@ -83,7 +80,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -83,7 +80,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
ITypeReference elementType;
int dimensions;
public ArrayTypeReference(ITypeReference elementType, int dimensions)
public ArrayTypeReference(ITypeReference elementType, int dimensions = 1)
{
if (elementType == null)
throw new ArgumentNullException("elementType");

21
ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs

@ -125,6 +125,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -125,6 +125,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// <summary>
/// Gets whether the type is an delegate type.
/// </summary>
/// <remarks>This method returns <c>false</c> for System.Delegate itself</remarks>
public static bool IsDelegate(this IType type)
{
if (type == null)
@ -132,6 +133,26 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -132,6 +133,26 @@ namespace ICSharpCode.NRefactory.TypeSystem
ITypeDefinition def = type.GetDefinition();
return def != null && def.ClassType == ClassType.Delegate;
}
/// <summary>
/// Gets the invoke method for a delegate type.
/// </summary>
/// <remarks>
/// Returns null if the type is not a delegate type; or if the invoke method could not be found.
/// </remarks>
public static IMethod GetDelegateInvokeMethod(this IType type)
{
if (type == null)
throw new ArgumentNullException("type");
ITypeDefinition def = type.GetDefinition();
if (def != null && def.ClassType == ClassType.Delegate) {
foreach (IMethod method in def.Methods) {
if (method.Name == "Invoke")
return method;
}
}
return null;
}
#endregion
}
}

4
ICSharpCode.NRefactory/TypeSystem/INamedElement.cs

@ -48,6 +48,10 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -48,6 +48,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// <summary>
/// Gets the full reflection name of the element.
/// </summary>
/// <remarks>
/// For types, the reflection name can be parsed back into a ITypeReference by using
/// <see cref="ReflectionHelper.ParseReflectionName"/>.
/// </remarks>
/// <returns>
/// "System.Int32[]" for int[]<br/>
/// "System.Int32[][,]" for C# int[,][]<br/>

12
ICSharpCode.NRefactory/TypeSystem/IType.cs

@ -40,12 +40,16 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -40,12 +40,16 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// <summary>
/// Calls ITypeVisitor.Visit for this type.
/// </summary>
/// <returns>The return value of the ITypeVisitor.Visit call</returns>
IType AcceptVisitor(TypeVisitor visitor);
/// <summary>
/// Calls ITypeVisitor.Visit for all children of this type, and reconstructs this type with the children based
/// by the return values of the visit calls.
/// on the return values of the visit calls.
/// </summary>
/// <returns>A copy of this type, with all children replaced by the return value of the corresponding visitor call.
/// If the visitor returned the original types for all children (or if there are no children), returns <c>this</c>.
/// </returns>
IType VisitChildren(TypeVisitor visitor);
/// <summary>
@ -59,9 +63,15 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -59,9 +63,15 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// <remarks>
/// If the inner class is generic, this method produces <see cref="ParameterizedType"/>s that
/// parameterize each nested class with its own type parameters.
/// TODO: does this make sense? ConstructedType needs it, but maybe it would be better to build
/// those self-parameterized types only in ConstructedType?
/// </remarks>
IEnumerable<IType> GetNestedTypes(ITypeResolveContext context);
// TODO: PERF maybe give GetMethods/GetProperties/etc a filter predicate
// that allows filtering the members pre-substitution?
// that could dramatically decrease the number of substitutions we have to perform
/// <summary>
/// Gets all methods that can be called on this return type.
/// </summary>

2
ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs

@ -25,7 +25,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -25,7 +25,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
{
if (context == null)
throw new ArgumentNullException("context");
// TODO: caching idea: if these lookups are a performance problem and the same GetClassTypeReference
// TODO: PERF: caching idea: if these lookups are a performance problem and the same GetClassTypeReference
// is asked to resolve lots of times in a row, try the following:
// Give ITypeResolveContext a property "object CacheToken { get; }" which is non-null if the
// context supports caching, and returns the same object only as long as the context is unchanged.

9
ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs

@ -227,8 +227,15 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -227,8 +227,15 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// <summary>
/// Parses a reflection name into a type reference.
/// </summary>
/// <param name="reflectionTypeName">The reflection name of the type.</param>
/// <param name="parentEntity">Parent entity, used to find the type parameters for open types.
/// If no entity is provided, type parameters are converted to <see cref="SharedTypes.UnknownType"/>.</param>
/// <exception cref="ReflectionNameParseException">The syntax of the reflection type name is invalid</exception>
/// <returns>A type reference that represents the reflection name.</returns>
public static ITypeReference ParseReflectionName(string reflectionTypeName, IEntity parentEntity = null)
{
if (reflectionTypeName == null)
throw new ArgumentNullException("reflectionTypeName");
int pos = 0;
ITypeReference r = ParseReflectionName(reflectionTypeName, ref pos, parentEntity);
if (pos < reflectionTypeName.Length)
@ -343,7 +350,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -343,7 +350,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
pos++; // end of array
reference = new ArrayTypeReference(reference, dimensions);
} else {
throw new ReflectionNameParseException(pos, "Expected array modifier");
throw new ReflectionNameParseException(pos, "Invalid array modifier");
}
}
break;

15
README

@ -28,7 +28,7 @@ Null-Object pattern: @@ -28,7 +28,7 @@ Null-Object pattern:
this allows code completion to work in the presence of minor semantic errors.
FAQ:
Q: What is the difference between a type and a type definition?
Q: What is the difference between types and type definitions?
A: Basically, a type (IType) is any type in the .NET type system:
- an array (ArrayType)
@ -45,7 +45,8 @@ A: Basically, a type (IType) is any type in the .NET type system: @@ -45,7 +45,8 @@ A: Basically, a type (IType) is any type in the .NET type system:
method. The GetDefinition() method will also return the underlying ITypeDefinition if given a parameterized type,
so "List<int>".GetDefinition() is "List<T>".
Q: What is the difference betweent type references and types?
Q: What is the difference between types and type references?
I've seen lots of duplicated classes (ArrayType vs. ArrayTypeReference, etc.)
NRefactory has the concept of the "project content": every assembly/project is stored independently from other assemblies/projects.
@ -62,6 +63,10 @@ Q: What is the difference betweent type references and types? @@ -62,6 +63,10 @@ Q: What is the difference betweent type references and types?
Note that every type can also be used as type reference - the IType interface derives from ITypeReference.
Every IType simply returns itself when the Resolve()-method is called.
Types are often directly used as references when source and target of the reference are within the same assembly.
Because an ArrayType must have an IType as element type, we also need the ArrayTypeReference to represent an array
of a type that's not yet resolved. When resolved, the ArrayTypeReference produces an array type:
new ArrayTypeReference(r).Resolve(context) = new ArrayType(r.Resolve(context))
A: If you've previously used the .NET Reflection API, the concept of type references is new to you.
@ -81,6 +86,7 @@ A: Please use: @@ -81,6 +86,7 @@ A: Please use:
The approach suggested above will return SharedTypes.UnknownType when the type cannot be resolved, so you
don't run into the risk of getting NullReferenceExceptions.
Q: Is it thread-safe?
A: This question is a bit difficult to answer.
@ -124,3 +130,8 @@ A: This question is a bit difficult to answer. @@ -124,3 +130,8 @@ A: This question is a bit difficult to answer.
The return value "syncContext" can then be used to access the type resolve context without further synchronization overhead.
Once the return value is disposed, the read-locks are released.
Q: What format do the .ToString() methods use?
A: They don't use any particular format. They're merely intended as a debugging aid.
Currently .ToString() usually matches .ReflectionName, but that may change in the future.

Loading…
Cancel
Save