1
ļ»æ// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
3
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4
// software and associated documentation files (the "Software"), to deal in the Software
5
// without restriction, including without limitation the rights to use, copy, modify, merge,
6
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7
// to whom the Software is furnished to do so, subject to the following conditions:
9
// The above copyright notice and this permission notice shall be included in all copies or
10
// substantial portions of the Software.
12
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17
// DEALINGS IN THE SOFTWARE.
20
using System.Collections.Generic;
22
using System.Linq.Expressions;
24
using ICSharpCode.NRefactory.Semantics;
25
using ICSharpCode.NRefactory.TypeSystem;
26
using ICSharpCode.NRefactory.TypeSystem.Implementation;
27
using NUnit.Framework;
29
namespace ICSharpCode.NRefactory.CSharp.Resolver
32
public class OverloadResolutionTests
34
readonly ICompilation compilation = new SimpleCompilation(
35
CecilLoaderTests.SystemCore, new[] { CecilLoaderTests.Mscorlib });
37
ResolveResult[] MakeArgumentList(params Type[] argumentTypes)
39
return argumentTypes.Select(t => new ResolveResult(compilation.FindType(t))).ToArray();
42
IMethod MakeMethod(params object[] parameterTypesOrDefaultValues)
44
var context = new SimpleTypeResolveContext(compilation.MainAssembly);
45
return (IMethod)MakeUnresolvedMethod(parameterTypesOrDefaultValues).CreateResolved(context);
48
DefaultUnresolvedMethod MakeUnresolvedMethod(params object[] parameterTypesOrDefaultValues)
50
var m = new DefaultUnresolvedMethod();
52
foreach (var typeOrDefaultValue in parameterTypesOrDefaultValues) {
53
Type type = typeOrDefaultValue as Type;
55
m.Parameters.Add(new DefaultUnresolvedParameter(type.ToTypeReference(), string.Empty));
56
else if (Type.GetTypeCode(typeOrDefaultValue.GetType()) > TypeCode.Object)
57
m.Parameters.Add(new DefaultUnresolvedParameter(typeOrDefaultValue.GetType().ToTypeReference(), string.Empty) {
58
DefaultValue = new SimpleConstantValue(typeOrDefaultValue.GetType().ToTypeReference(), typeOrDefaultValue)
61
throw new ArgumentException(typeOrDefaultValue.ToString());
66
IMethod MakeParamsMethod(params object[] parameterTypesOrDefaultValues)
68
var m = MakeUnresolvedMethod(parameterTypesOrDefaultValues);
69
((DefaultUnresolvedParameter)m.Parameters.Last()).IsParams = true;
70
var context = new SimpleTypeResolveContext(compilation.MainAssembly);
71
return (IMethod)m.CreateResolved(context);
74
IUnresolvedParameter MakeOptionalParameter(ITypeReference type, string name)
76
return new DefaultUnresolvedParameter(type, name) {
77
DefaultValue = new SimpleConstantValue(type, null)
82
public void PreferIntOverUInt()
84
OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(ushort)));
85
var c1 = MakeMethod(typeof(int));
86
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(c1));
87
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeMethod(typeof(uint))));
88
Assert.IsFalse(r.IsAmbiguous);
89
Assert.AreSame(c1, r.BestCandidate);
93
public void PreferUIntOverLong_FromIntLiteral()
95
ResolveResult[] args = { new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 1) };
96
OverloadResolution r = new OverloadResolution(compilation, args);
97
var c1 = MakeMethod(typeof(uint));
98
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(c1));
99
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeMethod(typeof(long))));
100
Assert.IsFalse(r.IsAmbiguous);
101
Assert.AreSame(c1, r.BestCandidate);
105
public void NullableIntAndNullableUIntIsAmbiguous()
107
OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(ushort?)));
108
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeMethod(typeof(int?))));
109
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeMethod(typeof(uint?))));
110
Assert.AreEqual(OverloadResolutionErrors.AmbiguousMatch, r.BestCandidateErrors);
112
// then adding a matching overload solves the ambiguity:
113
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeMethod(typeof(ushort?))));
114
Assert.AreEqual(OverloadResolutionErrors.None, r.BestCandidateErrors);
115
Assert.IsNull(r.BestCandidateAmbiguousWith);
119
public void ParamsMethodMatchesEmptyArgumentList()
121
OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList());
122
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeParamsMethod(typeof(int[]))));
123
Assert.IsTrue(r.BestCandidateIsExpandedForm);
127
public void ParamsMethodMatchesOneArgumentInExpandedForm()
129
OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(int)));
130
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeParamsMethod(typeof(int[]))));
131
Assert.IsTrue(r.BestCandidateIsExpandedForm);
135
public void ParamsMethodMatchesInUnexpandedForm()
137
OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(int[])));
138
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeParamsMethod(typeof(int[]))));
139
Assert.IsFalse(r.BestCandidateIsExpandedForm);
143
public void LessArgumentsPassedToParamsIsBetter()
145
OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(int), typeof(int), typeof(int)));
146
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeParamsMethod(typeof(int[]))));
147
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(MakeParamsMethod(typeof(int), typeof(int[]))));
148
Assert.IsFalse(r.IsAmbiguous);
149
Assert.AreEqual(2, r.BestCandidate.Parameters.Count);
153
public void CallInvalidParamsDeclaration()
155
OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList(typeof(int[,])));
156
Assert.AreEqual(OverloadResolutionErrors.ArgumentTypeMismatch, r.AddCandidate(MakeParamsMethod(typeof(int))));
157
Assert.IsFalse(r.BestCandidateIsExpandedForm);
161
public void PreferMethodWithoutOptionalParameters()
163
var m1 = MakeMethod();
164
var m2 = MakeMethod(1);
166
OverloadResolution r = new OverloadResolution(compilation, MakeArgumentList());
167
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(m1));
168
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(m2));
169
Assert.IsFalse(r.IsAmbiguous);
170
Assert.AreSame(m1, r.BestCandidate);
174
public void SkeetEvilOverloadResolution()
176
// http://msmvps.com/blogs/jon_skeet/archive/2010/11/02/evil-code-overload-resolution-workaround.aspx
178
// static void Foo<T>(T? ignored = default(T?)) where T : struct
179
var m1 = MakeUnresolvedMethod();
180
m1.TypeParameters.Add(new DefaultUnresolvedTypeParameter(EntityType.Method, 0, "T") { HasValueTypeConstraint = true });
181
m1.Parameters.Add(MakeOptionalParameter(
182
NullableType.Create(new TypeParameterReference(EntityType.Method, 0)),
186
// class ClassConstraint<T> where T : class {}
187
var classConstraint = new DefaultUnresolvedTypeDefinition(string.Empty, "ClassConstraint");
188
classConstraint.TypeParameters.Add(new DefaultUnresolvedTypeParameter(EntityType.TypeDefinition, 0, "T") { HasReferenceTypeConstraint = true });
190
// static void Foo<T>(ClassConstraint<T> ignored = default(ClassConstraint<T>))
192
var m2 = MakeUnresolvedMethod();
193
m2.TypeParameters.Add(new DefaultUnresolvedTypeParameter(EntityType.Method, 0, "T") { HasReferenceTypeConstraint = true });
194
m2.Parameters.Add(MakeOptionalParameter(
195
new ParameterizedTypeReference(classConstraint, new[] { new TypeParameterReference(EntityType.Method, 0) }),
199
// static void Foo<T>()
200
var m3 = MakeUnresolvedMethod();
201
m3.TypeParameters.Add(new DefaultUnresolvedTypeParameter(EntityType.Method, 0, "T"));
203
ICompilation compilation = TypeSystemHelper.CreateCompilation(classConstraint);
204
var context = new SimpleTypeResolveContext(compilation.MainAssembly);
205
IMethod resolvedM1 = (IMethod)m1.CreateResolved(context);
206
IMethod resolvedM2 = (IMethod)m2.CreateResolved(context);
207
IMethod resolvedM3 = (IMethod)m3.CreateResolved(context);
210
OverloadResolution o;
211
o = new OverloadResolution(compilation, new ResolveResult[0], typeArguments: new[] { compilation.FindType(typeof(int)) });
212
Assert.AreEqual(OverloadResolutionErrors.None, o.AddCandidate(resolvedM1));
213
Assert.AreEqual(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint, o.AddCandidate(resolvedM2));
214
Assert.AreSame(resolvedM1, o.BestCandidate);
216
// Call: Foo<string>();
217
o = new OverloadResolution(compilation, new ResolveResult[0], typeArguments: new[] { compilation.FindType(typeof(string)) });
218
Assert.AreEqual(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint, o.AddCandidate(resolvedM1));
219
Assert.AreEqual(OverloadResolutionErrors.None, o.AddCandidate(resolvedM2));
220
Assert.AreSame(resolvedM2, o.BestCandidate);
222
// Call: Foo<int?>();
223
o = new OverloadResolution(compilation, new ResolveResult[0], typeArguments: new[] { compilation.FindType(typeof(int?)) });
224
Assert.AreEqual(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint, o.AddCandidate(resolvedM1));
225
Assert.AreEqual(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint, o.AddCandidate(resolvedM2));
226
Assert.AreEqual(OverloadResolutionErrors.None, o.AddCandidate(resolvedM3));
227
Assert.AreSame(resolvedM3, o.BestCandidate);
231
/// A lambda of the form "() => default(returnType)"
233
class MockLambda : LambdaResolveResult
235
IType inferredReturnType;
236
List<IParameter> parameters = new List<IParameter>();
238
public MockLambda(IType returnType)
240
this.inferredReturnType = returnType;
243
public override IList<IParameter> Parameters {
244
get { return parameters; }
247
public override Conversion IsValid(IType[] parameterTypes, IType returnType, CSharpConversions conversions)
249
return conversions.ImplicitConversion(inferredReturnType, returnType);
252
public override bool IsImplicitlyTyped {
253
get { return false; }
256
public override bool IsAnonymousMethod {
257
get { return false; }
260
public override bool HasParameterList {
264
public override bool IsAsync {
265
get { return false; }
268
public override ResolveResult Body {
269
get { throw new NotImplementedException(); }
272
public override IType GetInferredReturnType(IType[] parameterTypes)
274
return inferredReturnType;
279
public void BetterConversionByLambdaReturnValue()
281
var m1 = MakeMethod(typeof(Func<long>));
282
var m2 = MakeMethod(typeof(Func<int>));
284
// M(() => default(byte));
285
ResolveResult[] args = {
286
new MockLambda(compilation.FindType(KnownTypeCode.Byte))
289
OverloadResolution r = new OverloadResolution(compilation, args);
290
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(m1));
291
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(m2));
292
Assert.AreSame(m2, r.BestCandidate);
293
Assert.AreEqual(OverloadResolutionErrors.None, r.BestCandidateErrors);
297
public void BetterConversionByLambdaReturnValue_ExpressionTree()
299
var m1 = MakeMethod(typeof(Func<long>));
300
var m2 = MakeMethod(typeof(Expression<Func<int>>));
302
// M(() => default(byte));
303
ResolveResult[] args = {
304
new MockLambda(compilation.FindType(KnownTypeCode.Byte))
307
OverloadResolution r = new OverloadResolution(compilation, args);
308
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(m1));
309
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(m2));
310
Assert.AreSame(m2, r.BestCandidate);
311
Assert.AreEqual(OverloadResolutionErrors.None, r.BestCandidateErrors);
315
public void Lambda_DelegateAndExpressionTreeOverloadsAreAmbiguous()
317
var m1 = MakeMethod(typeof(Func<int>));
318
var m2 = MakeMethod(typeof(Expression<Func<int>>));
320
// M(() => default(int));
321
ResolveResult[] args = {
322
new MockLambda(compilation.FindType(KnownTypeCode.Int32))
325
OverloadResolution r = new OverloadResolution(compilation, args);
326
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(m1));
327
Assert.AreEqual(OverloadResolutionErrors.None, r.AddCandidate(m2));
328
Assert.AreEqual(OverloadResolutionErrors.AmbiguousMatch, r.BestCandidateErrors);