From 7b1530e814381695db7e89f166084735418531e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20K=C3=A4ll=C3=A9n?= Date: Sun, 23 Dec 2012 13:46:32 +0100 Subject: [PATCH] When invoking a method with dynamic arguments (and there is only one applicable method), convert the result of the call to 'dynamic' (http://blogs.msdn.com/b/ericlippert/archive/2012/10/22/a-method-group-of-one.aspx) --- .../Resolver/CSharpResolver.cs | 21 +++++---- .../CSharp/Resolver/DynamicTests.cs | 47 +++++++++++++++---- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs index a65bbced93..d74ca9180a 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs @@ -1948,6 +1948,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } MethodGroupResolveResult mgrr = target as MethodGroupResolveResult; + Func convert = x => x; if (mgrr != null) { if (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic)) { // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable method. @@ -1969,41 +1970,43 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } return new DynamicInvocationResolveResult(new MethodGroupResolveResult(actualTarget, mgrr.MethodName, l, mgrr.TypeArguments), DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames)); } + else { + convert = x => x.Type.Kind == TypeKind.Dynamic ? x : Convert(x, SpecialType.Dynamic); + } } OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, checkForOverflow: checkForOverflow, conversions: conversions); if (or.BestCandidate != null) { if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult)) - return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetType)); + return convert(or.CreateResolveResult(new TypeResolveResult(mgrr.TargetType))); else - return or.CreateResolveResult(mgrr.TargetResult); + return convert(or.CreateResolveResult(mgrr.TargetResult)); } else { // No candidate found at all (not even an inapplicable one). // This can happen with empty method groups (as sometimes used with extension methods) - return new UnknownMethodResolveResult( - mgrr.TargetType, mgrr.MethodName, mgrr.TypeArguments, CreateParameters(arguments, argumentNames)); + return convert(new UnknownMethodResolveResult(mgrr.TargetType, mgrr.MethodName, mgrr.TypeArguments, CreateParameters(arguments, argumentNames))); } } UnknownMemberResolveResult umrr = target as UnknownMemberResolveResult; if (umrr != null) { - return new UnknownMethodResolveResult(umrr.TargetType, umrr.MemberName, umrr.TypeArguments, CreateParameters(arguments, argumentNames)); + return convert(new UnknownMethodResolveResult(umrr.TargetType, umrr.MemberName, umrr.TypeArguments, CreateParameters(arguments, argumentNames))); } UnknownIdentifierResolveResult uirr = target as UnknownIdentifierResolveResult; if (uirr != null && CurrentTypeDefinition != null) { - return new UnknownMethodResolveResult(CurrentTypeDefinition, uirr.Identifier, EmptyList.Instance, CreateParameters(arguments, argumentNames)); + return convert(new UnknownMethodResolveResult(CurrentTypeDefinition, uirr.Identifier, EmptyList.Instance, CreateParameters(arguments, argumentNames))); } IMethod invokeMethod = target.Type.GetDelegateInvokeMethod(); if (invokeMethod != null) { OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); or.AddCandidate(invokeMethod); - return new CSharpInvocationResolveResult( + return convert(new CSharpInvocationResolveResult( target, invokeMethod, //invokeMethod.ReturnType.Resolve(context), or.GetArgumentsWithConversionsAndNames(), or.BestCandidateErrors, isExpandedForm: or.BestCandidateIsExpandedForm, isDelegateInvocation: true, - argumentToParameterMap: or.GetArgumentToParameterMap()); + argumentToParameterMap: or.GetArgumentToParameterMap())); } - return ErrorResult; + return convert(ErrorResult); } List CreateParameters(ResolveResult[] arguments, string[] argumentNames) diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs index 44f7daf1ab..d78982c100 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs @@ -109,20 +109,50 @@ class TestClass { [Test] public void InvocationWithDynamicArgumentWithOneApplicableMethod() { string program = @"using System; +class TestClass { + public int SomeMethod(int a) {} + public int SomeMethod(int a, string b) {} + + void F() { + dynamic obj = null; + var x = $this.SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.IsError, Is.False); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + var irr = rr.Input as CSharpInvocationResolveResult; + Assert.That(irr, Is.Not.Null); + Assert.That(irr.Member.Name, Is.EqualTo("SomeMethod")); + Assert.That(((IParameterizedMember)irr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(irr.Arguments.Count, Is.EqualTo(1)); + var cr = irr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Conversion.IsImplicit, Is.True); + Assert.That(cr.Conversion.IsDynamicConversion, Is.True); + Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); + } + + [Test] + public void InvocationWithDynamicArgumentWithOneApplicableMethodReturningVoid() { + string program = @"using System; class TestClass { public void SomeMethod(int a) {} - public void SomeMethod(int a, string b) {} void F() { dynamic obj = null; var x = $this.SomeMethod(obj)$; } }"; - var rr = Resolve(program); - Assert.That(rr.Member.Name, Is.EqualTo("SomeMethod")); - Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); - Assert.That(rr.Arguments.Count, Is.EqualTo(1)); - var cr = rr.Arguments[0] as ConversionResolveResult; + var rr = Resolve(program); + Assert.That(rr.IsError, Is.False); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + var irr = rr.Input as CSharpInvocationResolveResult; + Assert.That(irr, Is.Not.Null); + Assert.That(irr.Member.Name, Is.EqualTo("SomeMethod")); + Assert.That(((IParameterizedMember)irr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(irr.Arguments.Count, Is.EqualTo(1)); + var cr = irr.Arguments[0] as ConversionResolveResult; Assert.That(cr, Is.Not.Null); Assert.That(cr.Conversion.IsImplicit, Is.True); Assert.That(cr.Conversion.IsDynamicConversion, Is.True); @@ -286,8 +316,9 @@ class TestClass { var x = $this.SomeMethod(obj)$; } }"; - var rr = Resolve(program); - Assert.That(rr.IsError, Is.True); + var rr = Resolve(program); + var irr = rr.Input; + Assert.That(irr.IsError, Is.True); } [Test]