Browse Source

Added unit tests for unary operators, and implemented overload resolution.

newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
8792c243cb
  1. 100
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/UnaryOperatorTests.cs
  2. 27
      ICSharpCode.NRefactory/CSharp/Resolver/ByReferenceResolveResult.cs
  3. 81
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  4. 8
      ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs
  5. 411
      ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs
  6. 42
      ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolutionErrors.cs
  7. 3
      ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  8. 4
      ICSharpCode.NRefactory/TypeSystem/NullableType.cs

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

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
// 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
@ -9,16 +10,97 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -9,16 +10,97 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
[TestFixture]
public class UnaryOperatorTests
{
IProjectContent mscorlib = CecilLoaderTests.Mscorlib;
CSharpResolver resolver;
ConstantResolveResult MakeConstant(object value)
{
return new ConstantResolveResult(value.GetType().ToTypeReference().Resolve(mscorlib), value);
}
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");
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");
}
[SetUp]
public void SetUp()
{
resolver = new CSharpResolver(CecilLoaderTests.Mscorlib);
}
[Test]
public void TestUnaryPlus()
{
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((sbyte)1)));
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((byte)1)));
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((short)1)));
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((ushort)1)));
AssertConstant(65, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant('A')));
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant(1)));
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)));
}
[Test]
public void TestUnaryMinus()
{
AssertConstant(-1, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(1)));
AssertConstant(-1L, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant((uint)1)));
AssertConstant(-2147483648L, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(2147483648)));
AssertConstant(-1.0f, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(1.0f)));
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')));
}
[Test]
public void TestMethod()
{
//var a = ~new X();
char a = 'a';
++a;
a++;
float b= 1;
++b;
b++;
public void TestUnaryMinusUncheckedOverflow()
{
AssertConstant(-2147483648, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(-2147483648)));
}
[Test]
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);
}
[Test]
public void TestBitwiseNot()
{
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant(-2)));
AssertConstant(~'A', resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant('A')));
AssertConstant(~(sbyte)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((sbyte)1)));
AssertConstant(~(byte)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((byte)1)));
AssertConstant(~(short)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((short)1)));
AssertConstant(~(ushort)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((ushort)1)));
AssertConstant(~(uint)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((uint)1)));
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);
}
[Test]
public void TestLogicalNot()
{
AssertConstant(true, resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeConstant(false)));
AssertConstant(false, resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeConstant(true)));
}
void Test(char a)
{
var x = -a;
x.GetType();
}
}

27
ICSharpCode.NRefactory/CSharp/Resolver/ByReferenceResolveResult.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
// 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>
/// Represents the resolve result of an 'ref x' or 'out x' expression.
/// </summary>
public class ByReferenceResolveResult : ResolveResult
{
public bool IsOut { get; private set; }
public bool IsRef { get { return !IsOut;} }
public ByReferenceResolveResult(IType elementType, bool isOut)
: base(new ByReferenceType(elementType))
{
this.IsOut = isOut;
}
public IType ElementType {
get { return ((ByReferenceType)this.Type).ElementType; }
}
}
}

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

@ -3,7 +3,9 @@ @@ -3,7 +3,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
@ -32,7 +34,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -32,7 +34,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
#region class OperatorMethod
class OperatorMethod : Immutable, IMethod
class OperatorMethod : Immutable, IParameterizedMember
{
IList<IParameter> parameters = new List<IParameter>();
@ -44,30 +46,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -44,30 +46,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
get; set;
}
IList<IAttribute> IMethod.ReturnTypeAttributes {
get { return EmptyList<IAttribute>.Instance; }
}
IList<ITypeParameter> IMethod.TypeParameters {
get { return EmptyList<ITypeParameter>.Instance; }
}
bool IMethod.IsExtensionMethod {
get { return false; }
}
bool IMethod.IsConstructor {
get { return false; }
}
bool IMethod.IsDestructor {
get { return false; }
}
bool IMethod.IsOperator {
get { return true; }
}
ITypeDefinition IEntity.DeclaringTypeDefinition {
get { return null; }
}
@ -163,6 +141,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -163,6 +141,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
string INamedElement.DotNetName {
get { return "operator"; }
}
public override string ToString()
{
StringBuilder b = new StringBuilder();
b.Append(ReturnType + " operator(");
for (int i = 0; i < parameters.Count; i++) {
if (i > 0)
b.Append(", ");
b.Append(parameters[i].Type);
}
b.Append(')');
return b.ToString();
}
}
#endregion
@ -222,7 +213,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -222,7 +213,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
break;
case UnaryOperatorType.BitNot:
if (type.IsEnum()) {
if (expression.IsCompileTimeConstant && expression.ConstantValue != null) {
if (expression.IsCompileTimeConstant && !isNullable) {
// evaluate as (E)(~(U)x);
var U = expression.ConstantValue.GetType().ToTypeReference().Resolve(context);
var unpackedEnum = new ConstantResolveResult(U, expression.ConstantValue);
@ -237,13 +228,25 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -237,13 +228,25 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
default:
throw new InvalidOperationException();
}
throw new NotImplementedException();
}
object GetUserUnaryOperatorCandidates()
{
// C# 4.0 spec: §7.3.5 Candidate user-defined operators
throw new NotImplementedException();
OverloadResolution r = new OverloadResolution(context, new[] { expression });
foreach (var candidate in methodGroup) {
r.AddCandidate(candidate);
}
UnaryOperatorMethod m = (UnaryOperatorMethod)r.BestCandidate;
IType resultType = m.ReturnType.Resolve(context);
if (r.BestCandidateErrors != OverloadResolutionErrors.None) {
return new ErrorResolveResult(resultType);
} else if (expression.IsCompileTimeConstant && !isNullable) {
object val;
try {
val = m.Invoke(expression.ConstantValue);
} catch (OverflowException) {
return new ErrorResolveResult(resultType);
}
return new ConstantResolveResult(resultType, val);
} else {
return new ResolveResult(resultType);
}
}
static string GetOverloadableOperatorName(UnaryOperatorType op)
@ -279,16 +282,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -279,16 +282,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public LambdaUnaryOperatorMethod(Func<T, T> func)
{
this.ReturnType = typeof(T).ToTypeReference();
this.Parameters.Add(new DefaultParameter(this.ReturnType, string.Empty));
this.func = func;
}
public override object Invoke(object input)
{
return func((T)input);
return func((T)Convert.ChangeType(input, typeof(T)));
}
}
sealed class LiftedUnaryOperatorMethod : UnaryOperatorMethod
sealed class LiftedUnaryOperatorMethod : UnaryOperatorMethod, OverloadResolution.ILiftedOperator
{
UnaryOperatorMethod baseMethod;
@ -355,6 +360,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -355,6 +360,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
new LambdaUnaryOperatorMethod<long>(i => ~i),
new LambdaUnaryOperatorMethod<ulong>(i => ~i)
);
object GetUserUnaryOperatorCandidates()
{
// C# 4.0 spec: §7.3.5 Candidate user-defined operators
throw new NotImplementedException();
}
#endregion
#region ResolveCast

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

@ -64,7 +64,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -64,7 +64,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary>
/// Gets whether there is an identity conversion from <paramref name="fromType"/> to <paramref name="toType"/>
/// </summary>
bool IdentityConversion(IType fromType, IType toType)
public bool IdentityConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.1
return fromType.AcceptVisitor(dynamicErasure).Equals(toType.AcceptVisitor(dynamicErasure));
@ -368,12 +368,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -368,12 +368,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return false;
}
}
static void Test(short a) {}
static void Test(sbyte a)
{
Test(1);
}
#endregion
}
}

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

@ -0,0 +1,411 @@ @@ -0,0 +1,411 @@
// 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.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.NRefactory.Util;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
/// <summary>
/// Description of OverloadResolution.
/// </summary>
public class OverloadResolution
{
sealed class Candidate
{
public readonly IParameterizedMember Member;
/// <summary>
/// Returns the normal form candidate, if this is an expanded candidate.
/// </summary>
public readonly bool IsExpandedForm;
public readonly IType[] ParameterTypes;
/// <summary>
/// argument index -> parameter index; -1 for arguments that could not be mapped
/// </summary>
public int[] ArgumentToParameterMap;
public OverloadResolutionErrors Errors;
public int ErrorCount;
public bool HasUnmappedOptionalParameters;
public IList<IParameter> Parameters { get { return Member.Parameters; } }
public bool IsGenericMethod {
get {
IMethod method = Member as IMethod;
return method != null && method.TypeParameters.Count > 0;
}
}
public int ArgumentsPassedToParamsArray {
get {
int count = 0;
if (IsExpandedForm) {
int paramsParameterIndex = this.Parameters.Count - 1;
foreach (int parameterIndex in ArgumentToParameterMap) {
if (parameterIndex == paramsParameterIndex)
count++;
}
}
return count;
}
}
public Candidate(IParameterizedMember member, bool isExpanded)
{
this.Member = member;
this.IsExpandedForm = isExpanded;
this.ParameterTypes = new IType[member.Parameters.Count];
}
public void AddError(OverloadResolutionErrors newError)
{
this.Errors |= newError;
this.ErrorCount++;
}
}
ITypeResolveContext context;
ResolveResult[] arguments;
string[] argumentNames;
Conversions conversions;
List<Candidate> candidates = new List<Candidate>();
Candidate bestCandidate;
Candidate bestCandidateAmbiguousWith;
#region Constructor
public OverloadResolution(ITypeResolveContext context, ResolveResult[] arguments, string[] argumentNames = null)
{
if (context == null)
throw new ArgumentNullException("context");
if (arguments == null)
throw new ArgumentNullException("arguments");
if (argumentNames == null)
argumentNames = new string[arguments.Length];
else if (argumentNames.Length != arguments.Length)
throw new ArgumentException("argumentsNames.Length must be equal to arguments.Length");
this.context = context;
this.arguments = arguments;
this.argumentNames = argumentNames;
this.conversions = new Conversions(context);
}
#endregion
#region AddCandidate
public OverloadResolutionErrors AddCandidate(IParameterizedMember member)
{
if (member == null)
throw new ArgumentNullException("member");
Candidate c = new Candidate(member, false);
if (CalculateCandidate(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);
if (expandedCandidate.ErrorCount < c.ErrorCount)
return expandedCandidate.Errors;
}
}
return c.Errors;
}
/// <summary>
/// Calculates applicability etc. for the candidate.
/// </summary>
/// <returns>True if the calculation was successful, false if the candidate should be removed without reporting an error</returns>
bool CalculateCandidate(Candidate candidate)
{
if (!ResolveParameterTypes(candidate))
return false;
MapCorrespondingParameters(candidate);
CheckApplicability(candidate);
ConsiderIfNewCandidateIsBest(candidate);
return true;
}
bool ResolveParameterTypes(Candidate candidate)
{
for (int i = 0; i < candidate.Parameters.Count; i++) {
IType type = candidate.Parameters[i].Type.Resolve(context);
if (candidate.IsExpandedForm && i == candidate.Parameters.Count - 1) {
ArrayType arrayType = type as ArrayType;
if (arrayType != null && arrayType.Dimensions == 1)
type = arrayType;
else
return false; // error: cannot unpack params-array. abort considering the expanded form for this candidate
}
candidate.ParameterTypes[i] = type;
}
return true;
}
#endregion
#region MapCorrespondingParameters
void MapCorrespondingParameters(Candidate candidate)
{
// C# 4.0 spec: §7.5.1.1 Corresponding parameters
candidate.ArgumentToParameterMap = new int[arguments.Length];
for (int i = 0; i < arguments.Length; i++) {
candidate.ArgumentToParameterMap[i] = -1;
if (argumentNames[i] == null) {
// positional argument
if (i < candidate.ParameterTypes.Length) {
candidate.ArgumentToParameterMap[i] = i;
} else if (candidate.IsExpandedForm) {
candidate.ArgumentToParameterMap[i] = candidate.ParameterTypes.Length - 1;
} else {
candidate.AddError(OverloadResolutionErrors.TooManyPositionalArguments);
}
} else {
// named argument
for (int j = 0; j < candidate.Member.Parameters.Count; j++) {
if (argumentNames[i] == candidate.Member.Parameters[j].Name) {
candidate.ArgumentToParameterMap[i] = j;
}
}
if (candidate.ArgumentToParameterMap[i] < 0)
candidate.AddError(OverloadResolutionErrors.NoParameterFoundForNamedArgument);
}
}
}
#endregion
#region CheckApplicability
void CheckApplicability(Candidate candidate)
{
// C# 4.0 spec: §7.5.3.1 Applicable function member
// Test whether parameters were mapped the correct number of arguments:
int[] argumentCountPerParameter = new int[candidate.ParameterTypes.Length];
foreach (int parameterIndex in candidate.ArgumentToParameterMap) {
if (parameterIndex >= 0)
argumentCountPerParameter[parameterIndex]++;
}
for (int i = 0; i < argumentCountPerParameter.Length; i++) {
if (candidate.IsExpandedForm && i == argumentCountPerParameter.Length - 1)
continue; // any number of arguments is fine for the params-array
if (argumentCountPerParameter[i] == 0) {
if (candidate.Parameters[i].IsOptional)
candidate.HasUnmappedOptionalParameters = true;
else
candidate.AddError(OverloadResolutionErrors.MissingArgumentForRequiredParameter);
} else if (argumentCountPerParameter[i] > 1) {
candidate.AddError(OverloadResolutionErrors.MultipleArgumentsForSingleParameter);
}
}
// Test whether argument passing mode matches the parameter passing mode
for (int i = 0; i < arguments.Length; i++) {
int parameterIndex = candidate.ArgumentToParameterMap[i];
if (parameterIndex < 0) continue;
ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult;
if (brrr != null) {
if ((brrr.IsOut && !candidate.Parameters[parameterIndex].IsOut) || (brrr.IsRef && !candidate.Parameters[parameterIndex].IsRef))
candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch);
} else {
if (candidate.Parameters[parameterIndex].IsOut || candidate.Parameters[parameterIndex].IsRef)
candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch);
}
if (!conversions.ImplicitConversion(arguments[i], candidate.ParameterTypes[parameterIndex]))
candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch);
}
}
#endregion
#region BetterFunctionMember
/// <summary>
/// Returns 1 if c1 is better than c2; 2 if c2 is better than c1; or 0 if neither is better.
/// </summary>
int BetterFunctionMember(Candidate c1, Candidate c2)
{
if (c1.ErrorCount < c2.ErrorCount) return 1;
if (c1.ErrorCount > c2.ErrorCount) return 2;
// C# 4.0 spec: §7.5.3.2 Better function member
bool c1IsBetter = false;
bool c2IsBetter = false;
for (int i = 0; i < arguments.Length; i++) {
int p1 = c1.ArgumentToParameterMap[i];
int p2 = c2.ArgumentToParameterMap[i];
if (p1 >= 0 && p2 < 0) {
c1IsBetter = true;
} else if (p1 < 0 && p2 >= 0) {
c2IsBetter = true;
} else {
switch (conversions.BetterConversion(arguments[i], c1.ParameterTypes[p1], c2.ParameterTypes[p2])) {
case 1:
c1IsBetter = true;
break;
case 2:
c2IsBetter = true;
break;
}
}
}
if (c1IsBetter && !c2IsBetter)
return 1;
if (!c1IsBetter && c2IsBetter)
return 2;
if (!c1IsBetter && !c2IsBetter) {
// we need the tie-breaking rules
// non-generic methods are better
if (!c1.IsGenericMethod && c2.IsGenericMethod)
return 1;
else if (c1.IsGenericMethod && !c2.IsGenericMethod)
return 2;
// non-expanded members are better
if (!c1.IsExpandedForm && c2.IsExpandedForm)
return 1;
else if (c1.IsExpandedForm && !c2.IsExpandedForm)
return 2;
// prefer the member with less arguments mapped to the params-array
int r = c1.ArgumentsPassedToParamsArray.CompareTo(c2.ArgumentsPassedToParamsArray);
if (r < 0) return 1;
else if (r > 0) return 2;
// prefer the member where no default values need to be substituted
if (!c1.HasUnmappedOptionalParameters && c2.HasUnmappedOptionalParameters)
return 1;
else if (c1.HasUnmappedOptionalParameters && !c2.HasUnmappedOptionalParameters)
return 2;
// compare the formal parameters
r = MoreSpecificFormalParameters(c1, c2);
if (r != 0)
return r;
// prefer non-lifted operators
if (!(c1.Member is ILiftedOperator) && (c2.Member is ILiftedOperator))
return 1;
if ((c1.Member is ILiftedOperator) && !(c2.Member is ILiftedOperator))
return 2;
}
return 0;
}
/// <summary>
/// Implement this interface to give overload resolution a hint that the member represents a lifted operator,
/// which is used in the tie-breaking rules.
/// </summary>
public interface ILiftedOperator : IParameterizedMember
{
}
int MoreSpecificFormalParameters(Candidate c1, Candidate c2)
{
// prefer the member with more formal parmeters (in case both have different number of optional parameters)
int r = c1.Parameters.Count.CompareTo(c2.Parameters.Count);
if (r > 0) return 1;
else if (r < 0) return 2;
bool c1IsBetter = false;
bool c2IsBetter = false;
for (int i = 0; i < c1.Parameters.Count; i++) {
switch (MoreSpecificFormalParameter(c1.Parameters[i].Type.Resolve(context), c2.Parameters[i].Type.Resolve(context))) {
case 1:
c1IsBetter = true;
break;
case 2:
c2IsBetter = true;
break;
}
}
if (c1IsBetter && !c2IsBetter)
return 1;
if (!c1IsBetter && c2IsBetter)
return 2;
return 0;
}
static int MoreSpecificFormalParameter(IType t1, IType t2)
{
if ((t1 is ITypeParameter) && !(t2 is ITypeParameter))
return 2;
if ((t2 is ITypeParameter) && !(t1 is ITypeParameter))
return 1;
ParameterizedType p1 = t1 as ParameterizedType;
ParameterizedType p2 = t2 as ParameterizedType;
if (p1 != null && p2 != null && p1.TypeParameterCount == p2.TypeParameterCount) {
bool t1IsBetter = false;
bool t2IsBetter = false;
for (int i = 0; i < p1.TypeParameterCount; i++) {
switch (MoreSpecificFormalParameter(p1.TypeArguments[i], p2.TypeArguments[i])) {
case 1:
t1IsBetter = true;
break;
case 2:
t2IsBetter = true;
break;
}
}
if (t1IsBetter && !t2IsBetter)
return 1;
if (!t1IsBetter && t2IsBetter)
return 2;
}
TypeWithElementType tew1 = t1 as TypeWithElementType;
TypeWithElementType tew2 = t2 as TypeWithElementType;
if (tew1 != null && tew2 != null) {
return MoreSpecificFormalParameter(tew1.ElementType, tew2.ElementType);
}
return 0;
}
#endregion
#region ConsiderIfNewCandidateIsBest
void ConsiderIfNewCandidateIsBest(Candidate candidate)
{
if (bestCandidate == null) {
bestCandidate = candidate;
} else {
switch (BetterFunctionMember(candidate, bestCandidate)) {
case 0:
bestCandidateAmbiguousWith = candidate;
break;
case 1:
bestCandidate = candidate;
bestCandidateAmbiguousWith = null;
break;
// case 2: best candidate stays best
}
}
}
#endregion
public IParameterizedMember BestCandidate {
get { return bestCandidate.Member; }
}
public OverloadResolutionErrors BestCandidateErrors {
get {
OverloadResolutionErrors err = bestCandidate.Errors;
if (bestCandidateAmbiguousWith != null)
err |= OverloadResolutionErrors.AmbiguousMatch;
return err;
}
}
public IParameterizedMember BestCandidateAmbiguousWith {
get { return bestCandidateAmbiguousWith.Member; }
}
}
}

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

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
// 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;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
[Flags]
public enum OverloadResolutionErrors
{
None = 0,
/// <summary>
/// Too many positional arguments (some could not be mapped to any parameter).
/// </summary>
TooManyPositionalArguments = 0x0002,
/// <summary>
/// A named argument could not be mapped to any parameter
/// </summary>
NoParameterFoundForNamedArgument = 0x0004,
/// <summary>
/// No argument was mapped to a non-optional parameter
/// </summary>
MissingArgumentForRequiredParameter = 0x0008,
/// <summary>
/// Several arguments were mapped to a single (non-params-array) parameter
/// </summary>
MultipleArgumentsForSingleParameter = 0x0010,
/// <summary>
/// 'ref'/'out' passing mode doesn't match for at least 1 parameter
/// </summary>
ParameterPassingModeMismatch = 0x0020,
/// <summary>
/// Argument type cannot be converted to parameter type
/// </summary>
ArgumentTypeMismatch = 0x0040,
/// <summary>
/// There is no unique best overload
/// </summary>
AmbiguousMatch = 0x0080
}
}

3
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -139,10 +139,13 @@ @@ -139,10 +139,13 @@
<Compile Include="CSharp\Parser\mcs\MonoSymbolWriter.cs" />
<Compile Include="CSharp\Parser\mcs\outline.cs" />
<Compile Include="CSharp\Parser\mcs\roottypes.cs" />
<Compile Include="CSharp\Resolver\ByReferenceResolveResult.cs" />
<Compile Include="CSharp\Resolver\ConstantResolveResult.cs" />
<Compile Include="CSharp\Resolver\Conversions.cs" />
<Compile Include="CSharp\Resolver\CSharpResolver.cs" />
<Compile Include="CSharp\Resolver\ErrorResolveResult.cs" />
<Compile Include="CSharp\Resolver\OverloadResolution.cs" />
<Compile Include="CSharp\Resolver\OverloadResolutionErrors.cs" />
<Compile Include="CSharp\Resolver\ResolveResult.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TypeSystem\Accessibility.cs" />

4
ICSharpCode.NRefactory/TypeSystem/NullableType.cs

@ -25,6 +25,8 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -25,6 +25,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
public static IType GetUnderlyingType(IType type)
{
if (type == null)
throw new ArgumentNullException("type");
ParameterizedType pt = type as ParameterizedType;
if (pt != null && pt.TypeArguments.Count == 1 && pt.FullName == "System.Nullable")
return pt.TypeArguments[0];
@ -56,6 +58,8 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -56,6 +58,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
public static ITypeReference Create(ITypeReference elementType)
{
if (elementType == null)
throw new ArgumentNullException("elementType");
return new ParameterizedTypeReference(NullableReference, new [] { elementType });
}
}

Loading…
Cancel
Save