From ad7a0b16461732944de97fc9a765e4dc29e457d0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 5 May 2007 15:47:57 +0000 Subject: [PATCH] Fixed inferring type argument for a method with an IEnumerable parameter when an array is passed to the method. (backported from 3.0.0.2506) git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/2.1@2510 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/Src/Document/TextUtilities.cs | 3 ++ src/Main/Base/Test/GenericResolverTests.cs | 37 ++++++++++++++++++- .../NavigationServiceTestFixture.cs | 2 + .../Project/Src/MemberLookupHelper.cs | 21 +++++++---- 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextUtilities.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextUtilities.cs index fa105b9e46..5b637dab8f 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextUtilities.cs +++ b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Document/TextUtilities.cs @@ -146,6 +146,9 @@ namespace ICSharpCode.TextEditor.Document //// id. is typed on next line after comment one //// Would be better if lexer would parse properly such expressions. However this will cause //// modifications in this area too - to get full comment line and remove it afterwards + if (offset < 0) + return string.Empty; + string resText=document.GetText(offset, textArea.Caret.Offset - offset ).Trim(); int pos=resText.LastIndexOf('\n'); if (pos>=0) { diff --git a/src/Main/Base/Test/GenericResolverTests.cs b/src/Main/Base/Test/GenericResolverTests.cs index 9ad94e6a9f..4fe2e6ad09 100644 --- a/src/Main/Base/Test/GenericResolverTests.cs +++ b/src/Main/Base/Test/GenericResolverTests.cs @@ -22,6 +22,11 @@ namespace ICSharpCode.SharpDevelop.Tests return nrrt.Resolve(program, expression, line); } + RR Resolve(string program, string expression, int line) where RR : ResolveResult + { + return nrrt.Resolve(program, expression, line); + } + ResolveResult ResolveVB(string program, string expression, int line) { return nrrt.ResolveVB(program, expression, line); @@ -167,7 +172,7 @@ class DerivedClass : BaseClass { Assert.AreEqual("System.String", rr.ResolvedType.FullyQualifiedName); } - [Test] + [Test] public void CrossTypeParametersInheritance() { string program = @"using System; @@ -251,5 +256,35 @@ public class GenericClass where T : IDisposable { Assert.IsTrue(rr.ResolvedType is GenericReturnType); } #endregion + + #region Generic methods + [Test] + public void GenericMethodInstanciation() + { + string program = @"using System; +using System.Collections.Generic; +class TestClass { + void Main() { + + } + static T First(IEnumerable input) { + foreach (T e in input) return e; + throw new EmptyCollectionException(); + } +} +"; + MemberResolveResult mrr = Resolve(program, "First(new string[0])", 5); + Assert.AreEqual("System.String", mrr.ResolvedType.FullyQualifiedName); + Assert.AreEqual("System.String", mrr.ResolvedMember.ReturnType.FullyQualifiedName); + + IMethod genericMethod = mrr.ResolvedMember.DeclaringType.Methods[1]; + Assert.AreEqual("T", genericMethod.ReturnType.FullyQualifiedName); + + // ensure that the reference pointing to the specialized method is seen as a reference + // to the generic method. + // Fixing this requires changing the IMember interface: won't fix for 2.x + //Assert.IsTrue(Refactoring.RefactoringService.IsReferenceToMember(genericMethod, mrr)); + } + #endregion } } diff --git a/src/Main/Base/Test/Services_Navigation/NavigationServiceTestFixture.cs b/src/Main/Base/Test/Services_Navigation/NavigationServiceTestFixture.cs index bb7eb022ae..c9a5dc8101 100644 --- a/src/Main/Base/Test/Services_Navigation/NavigationServiceTestFixture.cs +++ b/src/Main/Base/Test/Services_Navigation/NavigationServiceTestFixture.cs @@ -535,6 +535,7 @@ namespace NavigationServiceTests Assert.AreEqual(r, NavigationService.CurrentPosition); } + /* [Test] [Ignore] // this test disabled on purpose - DA /// @@ -571,6 +572,7 @@ namespace NavigationServiceTests Assert.IsTrue(NavigationService.CanNavigateBack); Assert.IsTrue(NavigationService.CanNavigateForwards); } + */ [Test] /// diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs index 030d0d5d0b..3e731cedc1 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs @@ -397,20 +397,25 @@ namespace ICSharpCode.SharpDevelop.Dom /// Returns false when expectedArgument and passedArgument are incompatible, otherwise true /// is returned (true is used both for successful inferring and other kind of errors). /// + /// + /// The C# spec (ยง 25.6.4) has a bug: it says that type inference works if the passedArgument is IEnumerable{T} + /// and the expectedArgument is an array; passedArgument and expectedArgument must be swapped here. + /// public static bool InferTypeArgument(IReturnType expectedArgument, IReturnType passedArgument, IReturnType[] outputArray) { if (expectedArgument == null) return true; - if (passedArgument == null) return true; // TODO: NullTypeReference - if (expectedArgument.IsArrayReturnType) { - IReturnType expectedArrayElementType = expectedArgument.CastToArrayReturnType().ArrayElementType; - if (passedArgument.IsArrayReturnType && expectedArgument.CastToArrayReturnType().ArrayDimensions == passedArgument.CastToArrayReturnType().ArrayDimensions) { - return InferTypeArgument(expectedArrayElementType, passedArgument.CastToArrayReturnType().ArrayElementType, outputArray); - } else if (passedArgument.IsConstructedReturnType) { - switch (passedArgument.FullyQualifiedName) { + if (passedArgument == null || passedArgument == NullReturnType.Instance) return true; + + if (passedArgument.IsArrayReturnType) { + IReturnType passedArrayElementType = passedArgument.CastToArrayReturnType().ArrayElementType; + if (expectedArgument.IsArrayReturnType && expectedArgument.CastToArrayReturnType().ArrayDimensions == passedArgument.CastToArrayReturnType().ArrayDimensions) { + return InferTypeArgument(expectedArgument.CastToArrayReturnType().ArrayElementType, passedArrayElementType, outputArray); + } else if (expectedArgument.IsConstructedReturnType) { + switch (expectedArgument.FullyQualifiedName) { case "System.Collections.Generic.IList": case "System.Collections.Generic.ICollection": case "System.Collections.Generic.IEnumerable": - return InferTypeArgument(expectedArrayElementType, passedArgument.CastToConstructedReturnType().TypeArguments[0], outputArray); + return InferTypeArgument(expectedArgument.CastToConstructedReturnType().TypeArguments[0], passedArrayElementType, outputArray); } } // If P is an array type, and A is not an array type of the same rank,