diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpInvocationResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpInvocationResolveResult.cs index 066dc38ae4..3ef49d921c 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpInvocationResolveResult.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpInvocationResolveResult.cs @@ -57,9 +57,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver bool isExpandedForm = false, bool isDelegateInvocation = false, IList argumentToParameterMap = null, - IList initializerStatements = null + IList initializerStatements = null, + IType returnTypeOverride = null ) - : base(targetResult, member, arguments, initializerStatements) + : base(targetResult, member, arguments, initializerStatements, returnTypeOverride) { this.OverloadResolutionErrors = overloadResolutionErrors; this.IsExtensionMethodInvocation = isExtensionMethodInvocation; diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs index cdfbd89c2b..1aa8d4b578 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs @@ -1933,9 +1933,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return new DynamicInvocationResolveResult(target, DynamicInvocationType.Invocation, AddArgumentNamesIfNecessary(arguments, argumentNames)); } + bool isDynamic = arguments.Any(a => a.Type.Kind == TypeKind.Dynamic); MethodGroupResolveResult mgrr = target as MethodGroupResolveResult; if (mgrr != null) { - if (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic)) { + if (isDynamic) { // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable method. var or2 = CreateOverloadResolution(arguments, argumentNames, mgrr.TypeArguments.ToArray()); var applicableMethods = mgrr.MethodsGroupedByDeclaringType.SelectMany(m => m, (x, m) => new { x.DeclaringType, Method = m }).Where(x => OverloadResolution.IsApplicable(or2.AddCandidate(x.Method))).ToList(); @@ -1960,9 +1961,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, checkForOverflow: checkForOverflow, conversions: conversions, allowOptionalParameters: allowOptionalParameters); if (or.BestCandidate != null) { if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult)) - return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetType)); + return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetType), returnTypeOverride: isDynamic ? SpecialType.Dynamic : null); else - return or.CreateResolveResult(mgrr.TargetResult); + return or.CreateResolveResult(mgrr.TargetResult, returnTypeOverride: isDynamic ? SpecialType.Dynamic : null); } else { // No candidate found at all (not even an inapplicable one). // This can happen with empty method groups (as sometimes used with extension methods) @@ -1987,7 +1988,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver or.GetArgumentsWithConversionsAndNames(), or.BestCandidateErrors, isExpandedForm: or.BestCandidateIsExpandedForm, isDelegateInvocation: true, - argumentToParameterMap: or.GetArgumentToParameterMap()); + argumentToParameterMap: or.GetArgumentToParameterMap(), + returnTypeOverride: isDynamic ? SpecialType.Dynamic : null); } return ErrorResult; } diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs index 1dc1b85db7..39473f11b9 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs @@ -933,8 +933,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// /// Statements for Objects/Collections initializer. /// + /// + /// If not null, use this instead of the ReturnType of the member as the type of the created resolve result. /// - public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetResolveResult, IList initializerStatements = null) + /// + public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetResolveResult, IList initializerStatements = null, IType returnTypeOverride = null) { IParameterizedMember member = GetBestCandidateWithSubstitutedTypeArguments(); if (member == null) @@ -949,7 +952,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver this.BestCandidateIsExpandedForm, isDelegateInvocation: false, argumentToParameterMap: this.GetArgumentToParameterMap(), - initializerStatements: initializerStatements); + initializerStatements: initializerStatements, + returnTypeOverride: returnTypeOverride); } #endregion } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs index 44f7daf1ab..11727fb4dd 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs @@ -110,8 +110,8 @@ class TestClass { public void InvocationWithDynamicArgumentWithOneApplicableMethod() { string program = @"using System; class TestClass { - public void SomeMethod(int a) {} - public void SomeMethod(int a, string b) {} + public int SomeMethod(int a) {} + public int SomeMethod(int a, string b) {} void F() { dynamic obj = null; @@ -119,7 +119,9 @@ class TestClass { } }"; var rr = Resolve(program); + Assert.That(rr, Is.Not.Null); Assert.That(rr.Member.Name, Is.EqualTo("SomeMethod")); + Assert.That(rr.Type.Kind == TypeKind.Dynamic); 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; @@ -129,6 +131,32 @@ class TestClass { 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) {} + + 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 InvocationWithDynamicArgumentWhenBothAnOwnAndABaseMethodAreApplicable() { string program = @"using System; @@ -286,7 +314,7 @@ class TestClass { var x = $this.SomeMethod(obj)$; } }"; - var rr = Resolve(program); + var rr = Resolve(program); Assert.That(rr.IsError, Is.True); } diff --git a/ICSharpCode.NRefactory/Semantics/InvocationResolveResult.cs b/ICSharpCode.NRefactory/Semantics/InvocationResolveResult.cs index d274e4d3b5..0f807940c1 100644 --- a/ICSharpCode.NRefactory/Semantics/InvocationResolveResult.cs +++ b/ICSharpCode.NRefactory/Semantics/InvocationResolveResult.cs @@ -45,8 +45,9 @@ namespace ICSharpCode.NRefactory.Semantics public InvocationResolveResult(ResolveResult targetResult, IParameterizedMember member, IList arguments = null, - IList initializerStatements = null) - : base(targetResult, member) + IList initializerStatements = null, + IType returnTypeOverride = null) + : base(targetResult, member, returnTypeOverride) { this.Arguments = arguments ?? EmptyList.Instance; this.InitializerStatements = initializerStatements ?? EmptyList.Instance; diff --git a/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs b/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs index 3a2430856f..3501e7afe9 100644 --- a/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs +++ b/ICSharpCode.NRefactory/Semantics/MemberResolveResult.cs @@ -38,8 +38,8 @@ namespace ICSharpCode.NRefactory.Semantics readonly ResolveResult targetResult; readonly bool isVirtualCall; - public MemberResolveResult(ResolveResult targetResult, IMember member) - : base(member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType) + public MemberResolveResult(ResolveResult targetResult, IMember member, IType returnTypeOverride = null) + : base(returnTypeOverride ?? (member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType)) { this.targetResult = targetResult; this.member = member; @@ -54,8 +54,8 @@ namespace ICSharpCode.NRefactory.Semantics } } - public MemberResolveResult(ResolveResult targetResult, IMember member, bool isVirtualCall) - : base(member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType) + public MemberResolveResult(ResolveResult targetResult, IMember member, bool isVirtualCall, IType returnTypeOverride = null) + : base(returnTypeOverride ?? (member.EntityType == EntityType.Constructor ? member.DeclaringType : member.ReturnType)) { this.targetResult = targetResult; this.member = member;