diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs
index 5af732ddb1..64582af816 100644
--- a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs
+++ b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs
@@ -100,8 +100,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
///
- /// Gets the methods that were found.
+ /// Gets the methods that were found, grouped by their declaring type.
/// This list does not include extension methods.
+ /// Base types come first in the list.
///
public IEnumerable MethodsGroupedByDeclaringType {
get { return methodLists; }
diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs
index 7ce0e04064..9193307010 100644
--- a/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs
+++ b/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs
@@ -235,6 +235,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// This method implements the logic that causes applicable methods in derived types to hide
/// all methods in base types.
///
+ /// The methods, grouped by declaring type. Base types must come first in the list.
public void AddMethodLists(IList methodLists)
{
if (methodLists == null)
diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MemberLookupTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MemberLookupTests.cs
new file mode 100644
index 0000000000..ce35506467
--- /dev/null
+++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MemberLookupTests.cs
@@ -0,0 +1,125 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.IO;
+using System.Linq;
+
+using ICSharpCode.NRefactory.Semantics;
+using ICSharpCode.NRefactory.TypeSystem;
+using NUnit.Framework;
+
+namespace ICSharpCode.NRefactory.CSharp.Resolver
+{
+ [TestFixture]
+ public class MemberLookupTests : ResolverTestBase
+ {
+ MemberLookup lookup;
+
+ public override void SetUp()
+ {
+ base.SetUp();
+ lookup = new MemberLookup(context, null, project);
+ }
+
+ CSharpParsedFile Parse(string program)
+ {
+ CompilationUnit cu = new CSharpParser().Parse(new StringReader(program));
+ CSharpParsedFile parsedFile = new TypeSystemConvertVisitor(project, "test.cs").Convert(cu);
+ project.UpdateProjectContent(null, parsedFile);
+ return parsedFile;
+ }
+
+ [Test]
+ public void GroupMethodsByDeclaringType()
+ {
+ string program = @"
+class Base {
+ public virtual void Method() {}
+}
+class Middle : Base {
+ public void Method(int p) {}
+}
+class Derived : Middle {
+ public override void Method() {}
+}";
+ ITypeDefinition derived = Parse(program).TopLevelTypeDefinitions[2];
+ var rr = lookup.Lookup(new ResolveResult(derived), "Method", EmptyList.Instance, true) as MethodGroupResolveResult;
+ Assert.AreEqual(2, rr.MethodsGroupedByDeclaringType.Count());
+
+ var baseGroup = rr.MethodsGroupedByDeclaringType.ElementAt(0);
+ Assert.AreEqual("Base", baseGroup.DeclaringType.ReflectionName);
+ Assert.AreEqual(1, baseGroup.Count);
+ Assert.AreEqual("Derived.Method", baseGroup[0].FullName);
+
+ var middleGroup = rr.MethodsGroupedByDeclaringType.ElementAt(1);
+ Assert.AreEqual("Middle", middleGroup.DeclaringType.ReflectionName);
+ Assert.AreEqual(1, middleGroup.Count);
+ Assert.AreEqual("Middle.Method", middleGroup[0].FullName);
+ }
+
+ [Test]
+ public void MethodInGenericClassOverriddenByConcreteMethod()
+ {
+ string program = @"
+class Base {
+ public virtual void Method(T a) {}
+}
+class Derived : Base {
+ public override void Method(int a) {}
+ public override void Method(string a) {}
+}";
+ ITypeDefinition derived = Parse(program).TopLevelTypeDefinitions[1];
+ var rr = lookup.Lookup(new ResolveResult(derived), "Method", EmptyList.Instance, true) as MethodGroupResolveResult;
+ Assert.AreEqual(2, rr.MethodsGroupedByDeclaringType.Count());
+
+ var baseGroup = rr.MethodsGroupedByDeclaringType.ElementAt(0);
+ Assert.AreEqual("Base`1[[System.Int32]]", baseGroup.DeclaringType.ReflectionName);
+ Assert.AreEqual(1, baseGroup.Count);
+ Assert.AreEqual("Derived.Method", baseGroup[0].FullName);
+ Assert.AreEqual("System.Int32", baseGroup[0].Parameters[0].Type.Resolve(context).ReflectionName);
+
+ var derivedGroup = rr.MethodsGroupedByDeclaringType.ElementAt(1);
+ Assert.AreEqual("Derived", derivedGroup.DeclaringType.ReflectionName);
+ Assert.AreEqual(1, derivedGroup.Count);
+ Assert.AreEqual("Derived.Method", derivedGroup[0].FullName);
+ Assert.AreEqual("System.String", derivedGroup[0].Parameters[0].Type.Resolve(context).ReflectionName);
+ }
+
+ [Test]
+ public void GenericMethod()
+ {
+ string program = @"
+class Base {
+ public virtual void Method(T a) {}
+}
+class Derived : Base {
+ public override void Method(S a) {}
+}";
+ ITypeDefinition derived = Parse(program).TopLevelTypeDefinitions[1];
+ var rr = lookup.Lookup(new ResolveResult(derived), "Method", EmptyList.Instance, true) as MethodGroupResolveResult;
+ Assert.AreEqual(1, rr.MethodsGroupedByDeclaringType.Count());
+
+ var baseGroup = rr.MethodsGroupedByDeclaringType.ElementAt(0);
+ Assert.AreEqual("Base", baseGroup.DeclaringType.ReflectionName);
+ Assert.AreEqual(1, baseGroup.Count);
+ Assert.AreEqual("Derived.Method", baseGroup[0].FullName);
+ Assert.AreEqual("``0", baseGroup[0].Parameters[0].Type.Resolve(context).ReflectionName);
+ }
+ }
+}
diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs
index 2585e70ba1..c15aa4ae23 100644
--- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs
+++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs
@@ -197,7 +197,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
SetUp();
CSharpParsedFile parsedFile = new CSharpParsedFile("test.cs", resolver.CurrentUsingScope);
- TypeSystemConvertVisitor convertVisitor = new TypeSystemConvertVisitor(parsedFile, resolver.CurrentUsingScope, null);
+ TypeSystemConvertVisitor convertVisitor = new TypeSystemConvertVisitor(parsedFile);
cu.AcceptVisitor(convertVisitor, null);
project.UpdateProjectContent(null, convertVisitor.ParsedFile);
diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
index ad8a9fb826..5394410868 100644
--- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
+++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
@@ -154,6 +154,7 @@
+
diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs
index 8b74b7267d..98bec8dcea 100644
--- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs
+++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs
@@ -431,8 +431,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.IsFalse(type.TypeParameters[0].HasReferenceTypeConstraint);
Assert.IsTrue(type.TypeParameters[1].HasReferenceTypeConstraint);
- Assert.IsTrue(type.TypeParameters[0].IsReferenceType(ctx) == true);
- Assert.IsTrue(type.TypeParameters[1].IsReferenceType(ctx) == true);
+ Assert.IsNull(type.TypeParameters[0].IsReferenceType(ctx));
+ Assert.AreEqual(true, type.TypeParameters[1].IsReferenceType(ctx));
}
[Test]
diff --git a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs
index f1ed305f7a..2a98f1ccc9 100644
--- a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs
+++ b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs
@@ -37,7 +37,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// Note that this method does not return all supertypes - doing so is impossible due to contravariance
/// (and undesirable for covariance as the list could become very large).
///
- /// The output is ordered so that base types occur in before derived types.
+ /// The output is ordered so that base types occur before derived types.
///
public static IEnumerable GetAllBaseTypes(this IType type, ITypeResolveContext context)
{
@@ -47,12 +47,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
///
- /// Gets the non-interface base types.
+ /// Gets all non-interface base types.
///
///
- /// When is an interface, this method will also return base interfaces.
+ /// When is an interface, this method will also return base interfaces (return same output as GetAllBaseTypes()).
///
- /// The output is ordered so that base types occur in before derived types.
+ /// The output is ordered so that base types occur before derived types.
///
public static IEnumerable GetNonInterfaceBaseTypes(this IType type, ITypeResolveContext context)
{
diff --git a/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs b/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs
index 2a55c1e1fd..bf0698f339 100644
--- a/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs
+++ b/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs
@@ -18,11 +18,51 @@
using System;
using System.Collections.Generic;
+using System.Threading;
+using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.TypeSystem
{
public static class ParameterListComparer
{
+ // We want to consider the parameter lists "Method(T a)" and "Method(S b)" as equal.
+ // However, the parameter types are not considered equal, as T is a different type parameter than S.
+ // In order to compare the method signatures, we will normalize all method type parameters.
+ sealed class NormalizeMethodTypeParameters : TypeVisitor
+ {
+ public static readonly NormalizeMethodTypeParameters Instance = new NormalizeMethodTypeParameters();
+
+ ITypeParameter[] normalTypeParameters = { new DefaultTypeParameter(EntityType.Method, 0, string.Empty) };
+
+ public override IType VisitTypeParameter(ITypeParameter type)
+ {
+ if (type.OwnerType == EntityType.Method) {
+ ITypeParameter[] tps = this.normalTypeParameters;
+ while (type.Index >= tps.Length) {
+ // We don't have a normal type parameter for this index, so we need to extend our array.
+ // Because the array can be used concurrently from multiple threads, we have to use
+ // Interlocked.CompareExchange.
+ ITypeParameter[] newTps = new ITypeParameter[type.Index + 1];
+ tps.CopyTo(newTps, 0);
+ for (int i = tps.Length; i < newTps.Length; i++) {
+ newTps[i] = new DefaultTypeParameter(EntityType.Method, i, string.Empty);
+ }
+ ITypeParameter[] oldTps = Interlocked.CompareExchange(ref normalTypeParameters, newTps, tps);
+ if (oldTps == tps) {
+ // exchange successful
+ tps = newTps;
+ } else {
+ // exchange not successful
+ tps = oldTps;
+ }
+ }
+ return tps[type.Index];
+ } else {
+ return base.VisitTypeParameter(type);
+ }
+ }
+ }
+
public static bool Compare(ITypeResolveContext context, IParameterizedMember x, IParameterizedMember y)
{
var px = x.Parameters;
@@ -36,7 +76,13 @@ namespace ICSharpCode.NRefactory.TypeSystem
continue;
if (a == null || b == null)
return false;
- if (!a.Type.Resolve(context).Equals(b.Type.Resolve(context)))
+ IType aType = a.Type.Resolve(context);
+ IType bType = b.Type.Resolve(context);
+
+ aType = aType.AcceptVisitor(NormalizeMethodTypeParameters.Instance);
+ bType = bType.AcceptVisitor(NormalizeMethodTypeParameters.Instance);
+
+ if (!aType.Equals(bType))
return false;
}
return true;
@@ -48,7 +94,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
unchecked {
foreach (IParameter p in obj.Parameters) {
hashCode *= 27;
- hashCode += p.Type.Resolve(context).GetHashCode();
+ IType type = p.Type.Resolve(context);
+ type = type.AcceptVisitor(NormalizeMethodTypeParameters.Instance);
+ hashCode += type.GetHashCode();
}
}
return hashCode;