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.
21
using ICSharpCode.NRefactory.Semantics;
22
using ICSharpCode.NRefactory.TypeSystem;
23
using ICSharpCode.NRefactory.TypeSystem.Implementation;
24
using NUnit.Framework;
26
namespace ICSharpCode.NRefactory.CSharp.Resolver
29
public class InvocationTests : ResolverTestBase
32
public void MethodCallTest()
34
string program = @"class A {
44
InvocationResolveResult result = Resolve<CSharpInvocationResolveResult>(program);
45
Assert.AreEqual("A.TargetMethod", result.Member.FullName);
46
Assert.AreEqual("System.Int32", result.Type.ReflectionName);
50
public void InvalidMethodCall()
52
string program = @"class A {
53
void Method(string b) {
54
$b.ThisMethodDoesNotExistOnString(b)$;
58
UnknownMethodResolveResult result = Resolve<UnknownMethodResolveResult>(program);
59
Assert.AreEqual("ThisMethodDoesNotExistOnString", result.MemberName);
60
Assert.AreEqual("System.String", result.TargetType.FullName);
61
Assert.AreEqual(1, result.Parameters.Count);
62
Assert.AreEqual("b", result.Parameters[0].Name);
63
Assert.AreEqual("System.String", result.Parameters[0].Type.ReflectionName);
65
Assert.AreSame(SpecialType.UnknownType, result.Type);
69
public void OverriddenMethodCall()
71
string program = @"class A {
73
$new B().GetRandomNumber()$;
76
public abstract int GetRandomNumber();
79
public override int GetRandomNumber() {
80
return 4; // chosen by fair dice roll.
81
// guaranteed to be random
85
InvocationResolveResult result = Resolve<CSharpInvocationResolveResult>(program);
86
Assert.AreEqual("B.GetRandomNumber", result.Member.FullName);
90
public void OverriddenMethodCall2()
92
string program = @"class A {
94
$new B().GetRandomNumber(""x"", this)$;
97
public abstract int GetRandomNumber(string a, A b);
100
public override int GetRandomNumber(string b, A a) {
105
InvocationResolveResult result = Resolve<CSharpInvocationResolveResult>(program);
106
Assert.AreEqual("B.GetRandomNumber", result.Member.FullName);
110
public void ThisMethodCallTest()
112
string program = @"class A {
114
$this.TargetMethod()$;
122
InvocationResolveResult result = Resolve<CSharpInvocationResolveResult>(program);
123
Assert.AreEqual("A.TargetMethod", result.Member.FullName);
124
Assert.AreEqual("System.Int32", result.Type.ReflectionName);
128
public void VoidTest()
130
string program = @"using System;
137
Assert.AreEqual("System.Void", Resolve(program).Type.ReflectionName);
141
public void EventCallTest()
143
string program = @"using System;
146
$TestEvent(this, EventArgs.Empty)$;
149
public event EventHandler TestEvent;
152
Assert.AreEqual("System.Void", Resolve(program).Type.ReflectionName);
156
public void DelegateCallTest()
158
string program = @"using System; using System.Reflection;
160
void Method(ModuleResolveEventHandler eh) {
161
$eh(this, new ResolveEventArgs())$;
165
Assert.AreEqual("System.Reflection.Module", Resolve(program).Type.ReflectionName);
169
public void DelegateReturnedFromMethodCallTest()
171
string program = @"using System;
176
abstract Predicate<string> GetHandler();
179
Assert.AreEqual("System.Boolean", Resolve(program).Type.ReflectionName);
184
public void MethodGroupResolveTest()
186
string program = @"class A {
191
void TargetMethod(int a) { }
192
void TargetMethod<T>(T a) { }
195
MethodGroupResolveResult result = Resolve<MethodGroupResolveResult>(program, "TargetMethod", 3);
196
Assert.AreEqual("TargetMethod", result.Name);
197
Assert.AreEqual(2, result.Methods.Count);
199
result = Resolve<MethodGroupResolveResult>(program, "TargetMethod<string>", 3);
200
Assert.AreEqual("TargetMethod", result.Name);
201
Assert.AreEqual(1, result.Methods[0].Count);
202
Assert.AreEqual("System.String", result.GetMethodIfSingleOverload().Parameters[0].ReturnType.FullyQualifiedName);
207
public void TestOverloadingByRef()
209
string program = @"using System;
211
public static void Main() {
216
static void T(int x) {}
217
static void T(ref int y) {}
220
InvocationResolveResult mrr = Resolve<CSharpInvocationResolveResult>(program.Replace("T(a)", "$T(a)$"));
221
Assert.IsFalse(mrr.Member.Parameters[0].IsRef);
223
mrr = Resolve<CSharpInvocationResolveResult>(program.Replace("T(ref a)", "$T(ref a)$"));
224
Assert.IsTrue(mrr.Member.Parameters[0].IsRef);
228
public void AddedOverload()
230
string program = @"class BaseClass {
231
static void Main(DerivedClass d) {
234
public void Test(int a) { }
236
class DerivedClass : BaseClass {
237
public void Test(object a) { }
239
InvocationResolveResult mrr = Resolve<CSharpInvocationResolveResult>(program);
240
Assert.AreEqual("DerivedClass.Test", mrr.Member.FullName);
244
public void AddedOverloadOnInterface()
247
interface IBase { void Method(int a); }
248
interface IDerived { void Method(object a); }
250
static void Main(IDerived d) {
254
InvocationResolveResult mrr = Resolve<CSharpInvocationResolveResult>(program);
255
Assert.AreEqual("IDerived.Method", mrr.Member.FullName);
259
public void AddedNonApplicableOverload()
261
string program = @"class BaseClass {
262
static void Main(DerivedClass d) {
265
public void Test(int a) { }
267
class DerivedClass : BaseClass {
268
public void Test(string a) { }
270
InvocationResolveResult mrr = Resolve<CSharpInvocationResolveResult>(program);
271
Assert.AreEqual("BaseClass.Test", mrr.Member.FullName);
273
mrr = Resolve<CSharpInvocationResolveResult>(program.Replace("(3)", "(\"3\")"));
274
Assert.AreEqual("DerivedClass.Test", mrr.Member.FullName);
278
public void OverrideShadowed()
280
string program = @"using System;
283
$new DerivedClass().Test(3)$;
285
public virtual void Test(int a) { }
287
class MiddleClass : BaseClass {
288
public void Test(object a) { }
290
class DerivedClass : MiddleClass {
291
public override void Test(int a) { }
294
InvocationResolveResult mrr = Resolve<CSharpInvocationResolveResult>(program);
295
Assert.AreEqual("MiddleClass.Test", mrr.Member.FullName);
299
public void SubstituteClassAndMethodTypeParametersAtOnce()
301
string program = @"class C<X> { static void M<T>(X a, T b) { $C<T>.M(b, a)$; } }";
302
var rr = Resolve<CSharpInvocationResolveResult>(program);
303
Assert.IsFalse(rr.IsError);
305
var m = (IMethod)rr.Member;
306
Assert.AreEqual("X", m.TypeArguments.Single().Name);
307
Assert.AreEqual("T", m.Parameters[0].Type.Name);
308
Assert.AreEqual("X", m.Parameters[1].Type.Name);
312
public void MemberHiddenOnOneAccessPath()
314
// If a member is hidden in any access path, it is hidden in all access paths
316
interface IBase { int F { get; } }
317
interface ILeft: IBase { new int F { get; } }
318
interface IRight: IBase { void G(); }
319
interface IDerived: ILeft, IRight {}
321
void Test(IDerived d) { var a = $d.F$; }
323
var rr = Resolve<MemberResolveResult>(program);
324
Assert.AreEqual("ILeft.F", rr.Member.FullName);
328
public void PropertyClashesWithMethod()
331
interface IList { int Count { get; set; } }
332
interface ICounter { void Count(int i); }
333
interface IListCounter: IList, ICounter {}
335
void Test(IListCounter x) { var a = $x.Count$; }
337
var rr = Resolve<MethodGroupResolveResult>(program);
338
Assert.IsFalse(rr.IsError);
339
Assert.AreEqual("ICounter.Count", rr.Methods.Single().FullName);
343
public void OverloadAmbiguousWithMethodInTwoInterfaces()
346
interface ILeft { void Method(); }
347
interface IRight { void Method(); }
348
interface IBoth : ILeft, IRight {}
350
void Test(IBoth x) { $x.Method()$; }
352
var rr = Resolve<CSharpInvocationResolveResult>(program);
353
Assert.IsTrue(rr.IsError);
354
Assert.AreEqual(OverloadResolutionErrors.AmbiguousMatch, rr.OverloadResolutionErrors);
358
public void AddedOverloadInOneInterfaceAndBetterOverloadInOtherInterface1()
361
interface IBase { void Method(int x); }
362
interface ILeft : IBase { void Method(object x); }
363
interface IRight { void Method(int x); }
364
interface IBoth : ILeft, IRight {}
366
void Test(IBoth x) { $x.Method(1)$; }
368
// IBase.Method is "hidden" because ILeft.Method is also applicable,
369
// so IRight.Method is unambiguously the chosen overload.
370
var rr = Resolve<CSharpInvocationResolveResult>(program);
371
Assert.IsFalse(rr.IsError);
372
Assert.AreEqual("IRight.Method", rr.Member.FullName);
376
public void AddedOverloadInOneInterfaceAndBetterOverloadInOtherInterface2()
378
// repeat the above test with Left/Right swapped to make sure we aren't order-sensitive
380
interface IBase { void Method(int x); }
381
interface ILeft : IBase { void Method(object x); }
382
interface IRight { void Method(int x); }
383
interface IBoth : IRight, ILeft {}
385
void Test(IBoth x) { $x.Method(1)$; }
387
var rr = Resolve<CSharpInvocationResolveResult>(program);
388
Assert.IsFalse(rr.IsError);
389
Assert.AreEqual("IRight.Method", rr.Member.FullName);
393
public void AddedOverloadHidesCommonBaseMethod_Generic1()
399
interface ILeft : IBase<int> { void Method(object x); }
400
interface IRight : IBase<int> { }
401
interface IBoth : ILeft, IRight {}
403
void Test(IBoth x) { $x.Method(1)$; }
405
var rr = Resolve<CSharpInvocationResolveResult>(program);
406
Assert.IsFalse(rr.IsError);
407
Assert.AreEqual("ILeft.Method", rr.Member.FullName);
411
public void AddedOverloadHidesCommonBaseMethod_Generic2()
417
interface ILeft : IBase<int> { void Method(object x); }
418
interface IRight : IBase<int> { }
419
interface IBoth : ILeft, IRight {}
421
void Test(IBoth x) { $x.Method(1)$; }
423
var rr = Resolve<CSharpInvocationResolveResult>(program);
424
Assert.IsFalse(rr.IsError);
425
Assert.AreEqual("ILeft.Method", rr.Member.FullName);
429
public void AddedOverloadDoesNotHideCommonBaseMethodWithDifferentTypeArgument1()
435
interface ILeft : IBase<int> { void Method(object x); }
436
interface IRight : IBase<long> { }
437
interface IBoth : IRight, ILeft {}
439
void Test(IBoth x) { $x.Method(1)$; }
441
var rr = Resolve<CSharpInvocationResolveResult>(program);
442
Assert.IsFalse(rr.IsError);
443
Assert.AreEqual("IBase`1[[System.Int64]]", rr.Member.DeclaringType.ReflectionName);
447
public void AddedOverloadDoesNotHideCommonBaseMethodWithDifferentTypeArgument2()
453
interface ILeft : IBase<int> { void Method(object x); }
454
interface IRight : IBase<long> { }
455
interface IBoth : IRight, ILeft {}
457
void Test(IBoth x) { $x.Method(1)$; }
459
var rr = Resolve<CSharpInvocationResolveResult>(program);
460
Assert.IsFalse(rr.IsError);
461
Assert.AreEqual("IBase`1[[System.Int64]]", rr.Member.DeclaringType.ReflectionName);
465
public void AmbiguityBetweenMemberAndMethodIsNotAnError()
468
interface ILeft { void Method(object x); }
469
interface IRight { Action<object> Method { get; } }
470
interface IBoth : ILeft, IRight {}
472
void Test(IBoth x) { $x.Method(null)$; }
474
var rr = Resolve<CSharpInvocationResolveResult>(program);
475
Assert.IsFalse(rr.IsError);
476
Assert.AreEqual("ILeft.Method", rr.Member.FullName);
480
public void AcceptVisitor()
483
interface IVisitor<in T, out S> { }
484
class Test : IVisitor<object, object> {
486
$Accept(this, null)$;
488
S Accept<T, S>(IVisitor<T, S> v, T input) { }
490
var rr = Resolve<CSharpInvocationResolveResult>(program);
491
Assert.IsFalse(rr.IsError);
492
var typeArguments = ((IMethod)rr.Member).TypeArguments;
493
Assert.AreEqual("System.Object", typeArguments[0].ReflectionName);
494
Assert.AreEqual("System.Object", typeArguments[1].ReflectionName);
498
public void FirstParameterToExtensionMethod()
502
public static class Ex {
503
public static void F(this X x, int y, int z) {}
512
var rr = Resolve<CSharpInvocationResolveResult>(program);
513
Assert.IsFalse(rr.IsError);
514
Assert.That(rr.IsExtensionMethodInvocation, Is.True);
515
Assert.That(rr.Arguments[0], Is.InstanceOf<LocalResolveResult>());
516
Assert.That(((LocalResolveResult)rr.Arguments[0]).Variable.Name, Is.EqualTo("a"));
517
Assert.That(rr.Arguments[1], Is.InstanceOf<LocalResolveResult>());
518
Assert.That(((LocalResolveResult)rr.Arguments[1]).Variable.Name, Is.EqualTo("b"));
519
Assert.That(rr.Arguments[2], Is.InstanceOf<LocalResolveResult>());
520
Assert.That(((LocalResolveResult)rr.Arguments[2]).Variable.Name, Is.EqualTo("c"));
522
Assert.That(rr.TargetResult, Is.InstanceOf<TypeResolveResult>());
526
public void BaseInvocation()
530
public virtual void F(int x, int y) {}
533
public override void F(int x, int y) {}
538
var rr = Resolve<CSharpInvocationResolveResult>(program);
539
Assert.IsFalse(rr.IsError);
540
Assert.IsFalse(rr.IsVirtualCall);
544
public void NamedArgument()
548
public void F(int x) {}
553
var narr = Resolve<NamedArgumentResolveResult>(program);
554
Assert.IsInstanceOf<ConstantResolveResult>(narr.Argument);
555
Assert.AreEqual("x", narr.ParameterName);
556
Assert.AreEqual("Test.F", narr.Member.FullName);
557
Assert.AreSame(narr.Member.Parameters.Single(), narr.Parameter);
561
public void NamedArgumentInInvocation()
565
public void F(int x) {}
570
var rr = Resolve<CSharpInvocationResolveResult>(program);
571
Assert.IsInstanceOf<NamedArgumentResolveResult>(rr.Arguments.Single());
572
var narr = (NamedArgumentResolveResult)rr.Arguments.Single();
573
Assert.IsInstanceOf<ConstantResolveResult>(narr.Argument);
574
Assert.AreEqual("x", narr.ParameterName);
575
Assert.AreEqual("Test.F", narr.Member.FullName);
576
Assert.AreSame(narr.Member.Parameters.Single(), narr.Parameter);
578
// but GetArgumentsForCall() should directly return the constant:
579
Assert.IsInstanceOf<ConstantResolveResult>(rr.GetArgumentsForCall().Single());
583
public void UnknownNamedArgument()
587
public void F(int x) {}
592
var narr = Resolve<NamedArgumentResolveResult>(program);
593
Assert.IsInstanceOf<ConstantResolveResult>(narr.Argument);
594
Assert.AreEqual("y", narr.ParameterName);
595
Assert.IsNull(narr.Parameter);
599
public void NamedArgumentInMissingMethod()
607
var narr = Resolve<NamedArgumentResolveResult>(program);
608
Assert.IsInstanceOf<ConstantResolveResult>(narr.Argument);
609
Assert.AreEqual("x", narr.ParameterName);
610
Assert.IsNull(narr.Parameter);
614
public void GenericMethodInvocationWithConstraintMismatch()
628
void M<T>(T arg) where T : IA
634
var rr = Resolve<CSharpInvocationResolveResult>(program);
635
Assert.AreEqual(OverloadResolutionErrors.MethodConstraintsNotSatisfied, rr.OverloadResolutionErrors);
636
Assert.IsTrue(rr.IsError);
640
public void MethodCanBeInvokedWithNullableTypeArgument1() {
652
var rr = Resolve<CSharpInvocationResolveResult>(program);
653
Assert.IsFalse(rr.IsError);
657
public void MethodCanBeInvokedWithNullableTypeArgument2() {
669
var rr = Resolve<CSharpInvocationResolveResult>(program);
670
Assert.IsFalse(rr.IsError);
674
public void MethodCanBeInvokedWithNullableTypeArgument3() {
677
static T F<T, U>() where T : U {
686
var rr = Resolve<CSharpInvocationResolveResult>(program);
687
Assert.IsFalse(rr.IsError);
691
public void MethodWithStructContraintCanBeInvokedWithValueType() {
694
static T F<T>() where T : struct {
703
var rr = Resolve<CSharpInvocationResolveResult>(program);
704
Assert.IsFalse(rr.IsError);
708
public void MethodWithStructContraintCannotBeInvokedWithNullableValueType() {
711
static T F<T>() where T : struct {
720
var rr = Resolve<CSharpInvocationResolveResult>(program);
721
Assert.IsTrue(rr.IsError);
722
Assert.AreEqual(OverloadResolutionErrors.MethodConstraintsNotSatisfied, rr.OverloadResolutionErrors);
726
public void CanConstructGenericTypeWithNullableTypeArgument() {
735
var rr = Resolve<CSharpInvocationResolveResult>(program);
736
Assert.IsFalse(rr.IsError);
740
public void OverloadResolutionIsAmbiguousEvenIfNotDelegateCompatible() {
743
static void M(Func<int> o) {}
744
static void M(Action o) {}
746
static int K() { return 0; }
752
// K is only delegate-compatible with one of the overloads; yet we get an invalid match.
753
// This is because the conversion exists even though it is invalid.
754
var rr = Resolve<CSharpInvocationResolveResult>(program);
755
Assert.AreEqual(OverloadResolutionErrors.AmbiguousMatch, rr.OverloadResolutionErrors);
759
public void IndexerWithMoreSpecificParameterTypesIsPreferred()
763
public static void Test(B<object> b) {
768
public string this[T key] {
769
get { return ""1""; }
771
public int this[object key] {
775
var rr = Resolve<CSharpInvocationResolveResult>(program);
776
Assert.IsFalse(rr.IsError);
777
Assert.AreEqual("System.Int32", rr.Member.ReturnType.FullName);
781
public void MethodWithMoreSpecificParameterTypesIsPreferred()
785
public static void Test(B<object> b) {
790
public string M(T key) {
793
public int M(object key) {
797
var rr = Resolve<CSharpInvocationResolveResult>(program);
798
Assert.IsFalse(rr.IsError);
799
Assert.AreEqual("System.Int32", rr.Member.ReturnType.FullName);