diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs index 3b707f000f..cc0ba36f0d 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs @@ -1618,7 +1618,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver throw new NotSupportedException("Invalid value for NameLookupMode"); } if (result is UnknownMemberResolveResult) { - var extensionMethods = GetExtensionMethods(target.Type, identifier, typeArguments, true); + // We intentionally use all extension methods here, not just the eligible ones. + // Proper eligibility checking is only possible for the full invocation + // (after we know the remaining arguments). + // The eligibility check in GetExtensionMethods is only intended for code completion. + var extensionMethods = GetExtensionMethods(identifier, typeArguments); if (extensionMethods.Count > 0) { return new MethodGroupResolveResult(target, identifier, EmptyList.Instance, typeArguments) { extensionMethods = extensionMethods @@ -1755,7 +1759,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (method.TypeParameters.Count != typeArguments.Count) continue; SpecializedMethod sm = new SpecializedMethod(method, new TypeParameterSubstitution(null, typeArguments)); - if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) + if (IsEligibleExtensionMethod(compilation, conversions, targetType, sm, false, out inferredTypes)) outputGroup.Add(sm); } else { if (IsEligibleExtensionMethod(compilation, conversions, targetType, method, true, out inferredTypes)) { diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs index 037119dd21..11fd0e6519 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs @@ -132,6 +132,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// Gets all candidate extension methods. /// Note: this includes candidates that are not eligible due to an inapplicable /// this argument. + /// The candidates will only be specialized if the type arguments were provided explicitly. /// /// /// The results are stored in nested lists because they are grouped by using scope. @@ -155,6 +156,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return extensionMethods ?? Enumerable.Empty>(); } + /// + /// Gets the eligible extension methods. + /// + /// + /// Specifies whether to produce a + /// when type arguments could be inferred from . This parameter + /// is only used for inferred types and has no effect if the type parameters are + /// specified explicitly. + /// + /// + /// The results are stored in nested lists because they are grouped by using scope. + /// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }", + /// the return value will be + /// new List { + /// new List { all extensions from MoreExtensions }, + /// new List { all extensions from SomeExtensions } + /// } + /// public IEnumerable> GetEligibleExtensionMethods(bool substituteInferredTypes) { var result = new List>(); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs index 098deaa421..e81a2c5866 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs @@ -17,8 +17,10 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Linq; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; using NUnit.Framework; namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -118,5 +120,83 @@ public static class XC { Assert.AreEqual(1, inferredTypes.Length); Assert.AreEqual("System.String", inferredTypes[0].ReflectionName); } + + + [Test] + public void InferTypeFromOverwrittenMethodArguments() + { + string program = @"using System.Collections.Generic; + using System.Linq; + + public class A { } + + public class B : A { } + + class Program + { + static void Main(string[] args) + { + IEnumerable list = new List(); + var arr = $list.ToArray()$; + } + } +"; + var rr = Resolve(program); + Assert.AreEqual("A[]", rr.Type.ReflectionName); + Assert.AreEqual("System.Linq.Enumerable.ToArray", rr.Member.FullName); + Assert.AreEqual("A", ((SpecializedMethod)rr.Member).TypeArguments.Single().ReflectionName); + } + + [Test] + public void TypeInferenceBasedOnTargetTypeAndArgumentType() + { + string program = @"using System.Collections.Generic; + using System.Linq; + + public class A { } + public class B : A { } + + static class Program + { + static void Main(A a, B b) + { + var x = $b.Choose(a)$; + } + + public static T Choose(this T a, T b) { } + } +"; + var rr = Resolve(program); + Assert.AreEqual("A", rr.Type.ReflectionName); + } + + [Test] + public void PartiallySpecializedMethod() + { + string program = @"using System.Collections.Generic; + using System.Linq; + + public class A { } + public class B : A { } + + static class Program + { + static void Main(A a, B b) + { + var x = $b.Choose$(a); + } + + public static T Choose(this T a, T b) { } + } +"; + var rr = Resolve(program); + Assert.IsFalse(rr.Methods.Any()); + // We deliberately do not specialize the method unless partial specialization is requested explicitly. + // This is because the actual type (when considering the whole invocation, not just the method group) + // is actually A. + Assert.AreEqual("``0", rr.GetExtensionMethods().Single().Single().ReturnType.ReflectionName); + Assert.AreEqual("``0", rr.GetEligibleExtensionMethods(false).Single().Single().ReturnType.ReflectionName); + Assert.AreEqual("B", rr.GetEligibleExtensionMethods(true).Single().Single().ReturnType.ReflectionName); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs index b1145458d3..66ec024aea 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LocalTypeInferenceTests.cs @@ -210,35 +210,5 @@ static class ExtMethods { var rr = Resolve(program); Assert.AreEqual("System.Int32", rr.Type.ReflectionName); } - - [Test] - public void InfertypeFromOverwrittenMethodArguments() - { - string program = @"using System.Collections.Generic; - using System.Linq; - - public class A - { - } - - public class B : A - { - } - - class Program - { - static void Main(string[] args) - { - IEnumerable list = new List(); - var arr = list.ToArray(); - $arr$.ToString(); - } - } -"; - var lrr = Resolve(program); - Assert.AreEqual("A[]", lrr.Type.FullName); - } - - } }