Browse Source

Implemented grouping overloads by declared type.

Return overrides in most-derived class (necessary for determining the correct parameter names/IsOptional value), but keep them in the group where the virtual/abstract base method was.
newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
ea3e312438
  1. 196
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs
  2. 10
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs
  3. 8
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs
  4. 5
      ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs
  5. 2
      ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs
  6. 4
      ICSharpCode.NRefactory/CSharp/Refactoring/ContextAction/CheckIfParameterIsNull.cs
  7. 17
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  8. 496
      ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs
  9. 83
      ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs
  10. 79
      ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs
  11. 4
      ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs
  12. 37
      ICSharpCode.NRefactory/TypeSystem/Implementation/BaseTypeCollector.cs
  13. 37
      ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs

196
ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs

@ -27,8 +27,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -27,8 +27,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
[TestFixture]
public class InvocationTests : ResolverTestBase
{
// TODO: do we want to return the MemberResolveResult for the InvocationExpression, or only for it's target?
[Test]
public void MethodCallTest()
{
@ -66,7 +64,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -66,7 +64,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Assert.AreSame(SharedTypes.UnknownType, result.Type);
}
[Test, Ignore("Resolver returns the member from the base class, which is correct according to C# spec, but not what we want to show in tooltips")]
[Test]
public void OverriddenMethodCall()
{
string program = @"class A {
@ -87,7 +85,7 @@ class B : A { @@ -87,7 +85,7 @@ class B : A {
Assert.AreEqual("B.GetRandomNumber", result.Member.FullName);
}
[Test, Ignore("Resolver returns the member from the base class, which is correct according to C# spec, but not what we want to show in tooltips")]
[Test]
public void OverriddenMethodCall2()
{
string program = @"class A {
@ -99,8 +97,7 @@ class B : A { @@ -99,8 +97,7 @@ class B : A {
}
class B : A {
public override int GetRandomNumber(string b, A a) {
return 4; // chosen by fair dice roll.
// guaranteed to be random
return 4;
}
}
";
@ -226,7 +223,7 @@ class Program { @@ -226,7 +223,7 @@ class Program {
Assert.IsTrue(mrr.Member.Parameters[0].IsRef);
}
[Test, Ignore("Grouping by declaring type not yet implemented")]
[Test]
public void AddedOverload()
{
string program = @"class BaseClass {
@ -242,6 +239,21 @@ class DerivedClass : BaseClass { @@ -242,6 +239,21 @@ class DerivedClass : BaseClass {
Assert.AreEqual("DerivedClass.Test", mrr.Member.FullName);
}
[Test]
public void AddedOverloadOnInterface()
{
string program = @"
interface IBase { void Method(int a); }
interface IDerived { void Method(object a); }
class Test {
static void Main(IDerived d) {
$d.Method(3)$;
}
}";
InvocationResolveResult mrr = Resolve<InvocationResolveResult>(program);
Assert.AreEqual("IDerived.Method", mrr.Member.FullName);
}
[Test]
public void AddedNonApplicableOverload()
{
@ -261,7 +273,7 @@ class DerivedClass : BaseClass { @@ -261,7 +273,7 @@ class DerivedClass : BaseClass {
Assert.AreEqual("DerivedClass.Test", mrr.Member.FullName);
}
[Test, Ignore("Grouping by declaring type not yet implemented")]
[Test]
public void OverrideShadowed()
{
string program = @"using System;
@ -294,5 +306,173 @@ class DerivedClass : MiddleClass { @@ -294,5 +306,173 @@ class DerivedClass : MiddleClass {
Assert.AreEqual("T", m.Parameters[0].Type.Resolve(context).Name);
Assert.AreEqual("X", m.Parameters[1].Type.Resolve(context).Name);
}
[Test]
public void MemberHiddenOnOneAccessPath()
{
// If a member is hidden in any access path, it is hidden in all access paths
string program = @"
interface IBase { int F { get; } }
interface ILeft: IBase { new int F { get; } }
interface IRight: IBase { void G(); }
interface IDerived: ILeft, IRight {}
class A {
void Test(IDerived d) { var a = $d.F$; }
}";
var rr = Resolve<MemberResolveResult>(program);
Assert.AreEqual("ILeft.F", rr.Member.FullName);
}
[Test]
public void PropertyClashesWithMethod()
{
string program = @"
interface IList { int Count { get; set; } }
interface ICounter { void Count(int i); }
interface IListCounter: IList, ICounter {}
class A {
void Test(IListCounter x) { var a = $x.Count$; }
}";
var rr = Resolve<MethodGroupResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("ICounter.Count", rr.Methods.Single().FullName);
}
[Test]
public void OverloadAmbiguousWithMethodInTwoInterfaces()
{
string program = @"
interface ILeft { void Method(); }
interface IRight { void Method(); }
interface IBoth : ILeft, IRight {}
class A {
void Test(IBoth x) { $x.Method()$; }
}";
var rr = Resolve<InvocationResolveResult>(program);
Assert.IsTrue(rr.IsError);
Assert.AreEqual(OverloadResolutionErrors.AmbiguousMatch, rr.OverloadResolutionErrors);
}
[Test]
public void AddedOverloadInOneInterfaceAndBetterOverloadInOtherInterface1()
{
string program = @"
interface IBase { void Method(int x); }
interface ILeft : IBase { void Method(object x); }
interface IRight { void Method(int x); }
interface IBoth : ILeft, IRight {}
class A {
void Test(IBoth x) { $x.Method(1)$; }
}";
// IBase.Method is "hidden" because ILeft.Method is also applicable,
// so IRight.Method is unambiguously the chosen overload.
var rr = Resolve<InvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("IRight.Method", rr.Member.FullName);
}
[Test]
public void AddedOverloadInOneInterfaceAndBetterOverloadInOtherInterface2()
{
// repeat the above test with Left/Right swapped to make sure we aren't order-sensitive
string program = @"
interface IBase { void Method(int x); }
interface ILeft : IBase { void Method(object x); }
interface IRight { void Method(int x); }
interface IBoth : IRight, ILeft {}
class A {
void Test(IBoth x) { $x.Method(1)$; }
}";
var rr = Resolve<InvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("IRight.Method", rr.Member.FullName);
}
[Test]
public void AddedOverloadHidesCommonBaseMethod_Generic1()
{
string program = @"
interface IBase<T> {
void Method(int x);
}
interface ILeft : IBase<int> { void Method(object x); }
interface IRight : IBase<int> { }
interface IBoth : ILeft, IRight {}
class A {
void Test(IBoth x) { $x.Method(1)$; }
}";
var rr = Resolve<InvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("ILeft.Method", rr.Member.FullName);
}
[Test]
public void AddedOverloadHidesCommonBaseMethod_Generic2()
{
string program = @"
interface IBase<T> {
void Method(int x);
}
interface ILeft : IBase<int> { void Method(object x); }
interface IRight : IBase<int> { }
interface IBoth : ILeft, IRight {}
class A {
void Test(IBoth x) { $x.Method(1)$; }
}";
var rr = Resolve<InvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("ILeft.Method", rr.Member.FullName);
}
[Test]
public void AddedOverloadDoesNotHideCommonBaseMethodWithDifferentTypeArgument1()
{
string program = @"
interface IBase<T> {
void Method(int x);
}
interface ILeft : IBase<int> { void Method(object x); }
interface IRight : IBase<long> { }
interface IBoth : IRight, ILeft {}
class A {
void Test(IBoth x) { $x.Method(1)$; }
}";
var rr = Resolve<InvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("IBase`1[[System.Int64]]", rr.Member.DeclaringType.ReflectionName);
}
[Test]
public void AddedOverloadDoesNotHideCommonBaseMethodWithDifferentTypeArgument2()
{
string program = @"
interface IBase<T> {
void Method(int x);
}
interface ILeft : IBase<int> { void Method(object x); }
interface IRight : IBase<long> { }
interface IBoth : IRight, ILeft {}
class A {
void Test(IBoth x) { $x.Method(1)$; }
}";
var rr = Resolve<InvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("IBase`1[[System.Int64]]", rr.Member.DeclaringType.ReflectionName);
}
[Test]
public void AmbiguityBetweenMemberAndMethodIsNotAnError()
{
string program = @"
interface ILeft { void Method(object x); }
interface IRight { Action<object> Method { get; } }
interface IBoth : ILeft, IRight {}
class A {
void Test(IBoth x) { $x.Method(null)$; }
}";
var rr = Resolve<InvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("ILeft.Method", rr.Member.FullName);
}
}
}

10
ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs

@ -296,7 +296,7 @@ class Color { @@ -296,7 +296,7 @@ class Color {
Assert.AreEqual("value", result.Variable.Name);
}
[Test, Ignore("Anonymous methods not supported in resolver")]
[Test]
public void AnonymousMethodParameters()
{
string program = @"using System;
@ -523,7 +523,7 @@ class TestClass { @@ -523,7 +523,7 @@ class TestClass {
Assert.AreEqual("C.D.Inner", trr.Type.FullName);
}
[Test, Ignore("parser is broken and produces IdentifierExpression instead of PrimitiveType")]
[Test]
public void ShortMaxValueTest()
{
string program = @"using System;
@ -767,7 +767,7 @@ namespace A { @@ -767,7 +767,7 @@ namespace A {
Assert.AreEqual("DerivedClass.Test", mrr.Member.FullName);
}
[Test, Ignore("Resolver Bug")]
[Test]
public void SD_1487()
{
string program = @"using System;
@ -804,7 +804,7 @@ class Test { @@ -804,7 +804,7 @@ class Test {
Assert.AreEqual("System.Int32", rr.Member.ReturnType.Resolve(context).FullName);
}
[Test, Ignore("Resolver bug")]
[Test]
public void MethodHidesEvent()
{
// see SD-1542
@ -825,7 +825,7 @@ class Form { @@ -825,7 +825,7 @@ class Form {
Assert.AreEqual("Test.KeyDown", mgrr.Methods.Single().FullName);
}
[Test, Ignore("partial classes not yet supported")]
[Test]
public void ProtectedMemberVisibleWhenBaseTypeReferenceIsInOtherPart()
{
string program = @"using System;

8
ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs

@ -100,8 +100,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -100,8 +100,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
DefaultTypeParameter B = new DefaultTypeParameter(EntityType.Method, 1, "B");
ITypeDefinition declType = ctx.GetTypeDefinition(typeof(int));
var methods = declType.Methods.Where(m => m.Name == "Parse").ToList();
var argument = new MethodGroupResolveResult(new TypeResolveResult(declType), "Parse", methods, new IType[0]);
var methods = new MethodListWithDeclaringType(declType, declType.Methods.Where(m => m.Name == "Parse"));
var argument = new MethodGroupResolveResult(new TypeResolveResult(declType), "Parse", new[] { methods }, new IType[0]);
bool success;
ti.InferTypeArguments(new [] { A, B }, new [] { argument },
@ -119,8 +119,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -119,8 +119,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
DefaultTypeParameter T = new DefaultTypeParameter(EntityType.Method, 0, "T");
ITypeDefinition declType = ctx.GetTypeDefinition(typeof(Console));
var methods = declType.Methods.Where(m => m.Name == "ReadKey").ToList();
var argument = new MethodGroupResolveResult(new TypeResolveResult(declType), "ReadKey", methods, new IType[0]);
var methods = new MethodListWithDeclaringType(declType, declType.Methods.Where(m => m.Name == "ReadKey"));
var argument = new MethodGroupResolveResult(new TypeResolveResult(declType), "ReadKey", new[] { methods }, new IType[0]);
bool success;
Assert.AreEqual(

5
ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs

@ -65,8 +65,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -65,8 +65,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
string[] superTypes = c.GetAllBaseTypes(ctx).Select(t => t.ToString()).ToArray();
Assert.AreEqual(new string[] {
"System.SystemException", "System.Exception", "System.Object",
"System.Runtime.Serialization.ISerializable", "System.Runtime.InteropServices._Exception"
"System.Object",
"System.Runtime.Serialization.ISerializable", "System.Runtime.InteropServices._Exception",
"System.Exception", "System.SystemException"
}, superTypes);
}

2
ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs

@ -94,7 +94,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -94,7 +94,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
DefaultTypeDefinition c2 = new DefaultTypeDefinition(mscorlib, string.Empty, "C2");
c1.BaseTypes.Add(c2);
c2.BaseTypes.Add(c1);
Assert.AreEqual(new [] { c1, c2 }, c1.GetAllBaseTypes(context).ToArray());
Assert.AreEqual(new [] { c2, c1 }, c1.GetAllBaseTypes(context).ToArray());
}
[Test]

4
ICSharpCode.NRefactory/CSharp/Refactoring/ContextAction/CheckIfParameterIsNull.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
//
//
// CheckIfParameterIsNull.cs
//
// Author:
@ -94,7 +94,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -94,7 +94,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
this.parameter = parameter;
}
public object VisitIfElseStatement (IfElseStatement ifElseStatement, object data)
public override object VisitIfElseStatement (IfElseStatement ifElseStatement, object data)
{
if (ifElseStatement.Condition is BinaryOperatorExpression) {
var binOp = ifElseStatement.Condition as BinaryOperatorExpression;

17
ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs

@ -2019,7 +2019,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -2019,7 +2019,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (result is UnknownMemberResolveResult) {
var extensionMethods = GetExtensionMethods(target.Type, identifier, typeArguments);
if (extensionMethods.Count > 0) {
return new MethodGroupResolveResult(target, identifier, EmptyList<IMethod>.Instance, typeArguments) {
return new MethodGroupResolveResult(target, identifier, EmptyList<MethodListWithDeclaringType>.Instance, typeArguments) {
extensionMethods = extensionMethods
};
}
@ -2273,8 +2273,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -2273,8 +2273,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return umrr.MemberName;
MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult;
if (mgrr != null && mgrr.Methods.Count > 0)
return mgrr.Methods[0].Name;
if (mgrr != null)
return mgrr.MethodName;
LocalResolveResult vrr = rr as LocalResolveResult;
if (vrr != null)
@ -2328,16 +2328,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -2328,16 +2328,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return new ArrayAccessResolveResult(((TypeWithElementType)target.Type).ElementType, target, arguments);
}
// §7.6.6.2 Array access
// §7.6.6.2 Indexer access
OverloadResolution or = new OverloadResolution(context, arguments, argumentNames, new IType[0]);
MemberLookup lookup = CreateMemberLookup();
bool allowProtectedAccess = lookup.IsProtectedAccessAllowed(target.Type);
var indexers = target.Type.GetProperties(context, p => p.IsIndexer && lookup.IsAccessible(p, allowProtectedAccess));
// TODO: filter indexers hiding other indexers?
foreach (IProperty p in indexers) {
// TODO: grouping by class definition?
or.AddCandidate(p);
}
var indexers = lookup.LookupIndexers(target.Type);
or.AddMethodLists(indexers);
if (or.BestCandidate != null) {
return new InvocationResolveResult(target, or, context);
} else {

496
ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
@ -43,7 +44,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -43,7 +44,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
IType returnType = member.ReturnType.Resolve(context);
if (returnType == SharedTypes.Dynamic)
return true;
return returnType.IsDelegate();
return returnType.Kind == TypeKind.Delegate;
}
#endregion
@ -122,166 +123,419 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -122,166 +123,419 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
#endregion
#region class LookupGroup
sealed class LookupGroup
{
public readonly IType DeclaringType;
// When a nested type is hidden, it is simply removed from the list.
public List<IType> NestedTypes;
// When members are hidden, they are merely marked as hidden.
// We still need to store the hidden methods so that the 'override' processing can
// find them, so that it won't introduce the override as a new method.
public readonly List<IParameterizedMember> Methods;
public bool MethodsAreHidden;
public IMember NonMethod;
public bool NonMethodIsHidden;
public LookupGroup(IType declaringType, List<IType> nestedTypes, List<IParameterizedMember> methods, IMember nonMethod)
{
this.DeclaringType = declaringType;
this.NestedTypes = nestedTypes;
this.Methods = methods;
this.NonMethod = nonMethod;
this.MethodsAreHidden = (methods == null || methods.Count == 0);
this.NonMethodIsHidden = (nonMethod == null);
}
public bool AllHidden {
get {
if (NestedTypes != null && NestedTypes.Count > 0)
return false;
return NonMethodIsHidden && MethodsAreHidden;
}
}
}
#endregion
#region LookupType
public ResolveResult LookupType(IType declaringType, string name, IList<IType> typeArguments, bool parameterizeResultType = true)
{
int typeArgumentCount = typeArguments.Count;
Predicate<ITypeDefinition> typeFilter = delegate (ITypeDefinition d) {
// inner types contain the type parameters of outer types. therefore this count has to been adjusted.
int correctedCount = d.TypeParameterCount - (d.DeclaringType != null ? d.DeclaringType.TypeParameterCount : 0);
return correctedCount == typeArgumentCount && d.Name == name && IsAccessible(d, true);
Predicate<ITypeDefinition> filter = delegate (ITypeDefinition d) {
return InnerTypeParameterCount(d) == typeArgumentCount && d.Name == name && IsAccessible(d, true);
};
List<IType> types;
if (parameterizeResultType)
types = declaringType.GetNestedTypes(typeArguments, context, typeFilter).ToList();
else
types = declaringType.GetNestedTypes(context, typeFilter).ToList();
RemoveTypesHiddenByOtherTypes(types);
if (types.Count == 1)
return new TypeResolveResult(types[0]);
else if (types.Count > 1)
return new AmbiguousTypeResolveResult(types[0]);
else
List<LookupGroup> lookupGroups = new List<LookupGroup>();
if (declaringType.Kind != TypeKind.TypeParameter) {
foreach (IType type in declaringType.GetNonInterfaceBaseTypes(context)) {
List<IType> newNestedTypes = null;
IEnumerable<IType> typeBaseTypes = null;
IEnumerable<IType> nestedTypes;
if (parameterizeResultType) {
nestedTypes = type.GetNestedTypes(typeArguments, context, filter, GetMemberOptions.IgnoreInheritedMembers);
} else {
nestedTypes = type.GetNestedTypes(context, filter, GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions);
}
AddNestedTypes(type, nestedTypes, typeArgumentCount, lookupGroups, ref typeBaseTypes, ref newNestedTypes);
if (newNestedTypes != null)
lookupGroups.Add(new LookupGroup(type, newNestedTypes, null, null));
}
}
lookupGroups.RemoveAll(g => g.AllHidden);
Debug.Assert(lookupGroups.All(g => g.NestedTypes != null && g.NestedTypes.Count > 0));
if (lookupGroups.Count == 0) {
return new UnknownMemberResolveResult(declaringType, name, typeArguments);
}
LookupGroup resultGroup = lookupGroups[lookupGroups.Count - 1];
if (resultGroup.NestedTypes.Count > 1 || lookupGroups.Count > 1)
return new AmbiguousTypeResolveResult(resultGroup.NestedTypes[0]);
else
return new TypeResolveResult(resultGroup.NestedTypes[0]);
}
void RemoveTypesHiddenByOtherTypes(List<IType> types)
static int InnerTypeParameterCount(IType type)
{
for (int i = types.Count - 1; i >= 0; i--) {
ITypeDefinition d = GetDeclaringTypeDef(types[i]);
if (d == null)
continue;
// nested loop depends on the fact that the members of more derived classes appear later in the list
for (int j = i + 1; j < types.Count; j++) {
if (types[i].TypeParameterCount != types[j].TypeParameterCount)
continue;
ITypeDefinition s = GetDeclaringTypeDef(types[j]);
if (s != null && s != d && s.IsDerivedFrom(d, context)) {
// types[j] hides types[i]
types.RemoveAt(i);
break;
}
}
}
// inner types contain the type parameters of outer types. therefore this count has to been adjusted.
return type.TypeParameterCount - (type.DeclaringType != null ? type.DeclaringType.TypeParameterCount : 0);
}
#endregion
#region Lookup
/// <summary>
/// Performs a member lookup.
/// </summary>
public ResolveResult Lookup(ResolveResult targetResolveResult, string name, IList<IType> typeArguments, bool isInvocation)
{
IType type = targetResolveResult.Type;
int typeArgumentCount = typeArguments.Count;
bool targetIsTypeParameter = targetResolveResult.Type.Kind == TypeKind.TypeParameter;
List<IType> types = new List<IType>();
List<IMember> members = new List<IMember>();
if (!isInvocation) {
// Consider nested types only if it's not an invocation.
// type.GetNestedTypes() is checking the type parameter count for an exact match.
Predicate<ITypeDefinition> typeFilter = delegate (ITypeDefinition d) {
return d.Name == name && IsAccessible(d, true);
};
types.AddRange(type.GetNestedTypes(typeArguments, context, typeFilter));
bool allowProtectedAccess = IsProtectedAccessAllowed(targetResolveResult.Type);
Predicate<IEntity> filter = delegate(IEntity entity) {
return entity.Name == name && IsAccessible(entity, allowProtectedAccess);
};
List<LookupGroup> lookupGroups = new List<LookupGroup>();
// This loop will handle base types before derived types.
// The loop performs three jobs:
// 1) It marks entries in lookup groups from base classes as removed when those members
// are hidden by a derived class.
// 2) It adds a new lookup group with the members from a declaring type.
// 3) It replaces virtual members with the overridden version, placing the override in the
// lookup group belonging to the base class.
foreach (IType type in targetResolveResult.Type.GetNonInterfaceBaseTypes(context)) {
List<IType> newNestedTypes = null;
List<IParameterizedMember> newMethods = null;
IMember newNonMethod = null;
IEnumerable<IType> typeBaseTypes = null;
if (!isInvocation && !targetIsTypeParameter) {
// Consider nested types only if it's not an invocation.
// type.GetNestedTypes() is checking the type parameter count for an exact match,
// so we don't need to do that in our filter.
var nestedTypes = type.GetNestedTypes(typeArguments, context, filter, GetMemberOptions.IgnoreInheritedMembers);
AddNestedTypes(type, nestedTypes, typeArguments.Count, lookupGroups, ref typeBaseTypes, ref newNestedTypes);
}
IEnumerable<IMember> members;
if (typeArguments.Count == 0) {
// Note: IsInvocable-checking cannot be done as part of the filter;
// because it must be done after type substitution.
members = type.GetMembers(context, filter, GetMemberOptions.IgnoreInheritedMembers);
if (isInvocation)
members = members.Where(m => IsInvocable(m, context));
} else {
// No need to check for isInvocation/isInvocable here:
// we only fetch methods
members = type.GetMethods(typeArguments, context, filter, GetMemberOptions.IgnoreInheritedMembers);
}
AddMembers(type, members, lookupGroups, false, ref typeBaseTypes, ref newMethods, ref newNonMethod);
if (newNestedTypes != null || newMethods != null || newNonMethod != null)
lookupGroups.Add(new LookupGroup(type, newNestedTypes, newMethods, newNonMethod));
}
bool allowProtectedAccess = IsProtectedAccessAllowed(type);
// Remove interface members hidden by class members.
if (targetIsTypeParameter) {
// This can happen only with type parameters.
RemoveInterfaceMembersHiddenByClassMembers(lookupGroups);
}
Predicate<IMember> memberFilter = delegate(IMember member) {
return !member.IsOverride && member.Name == name && IsAccessible(member, allowProtectedAccess);
return CreateResult(targetResolveResult, lookupGroups, name, typeArguments);
}
#endregion
#region Lookup Indexer
/// <summary>
/// Looks up the indexers on the target type.
/// </summary>
public IList<MethodListWithDeclaringType> LookupIndexers(IType targetType)
{
bool allowProtectedAccess = IsProtectedAccessAllowed(targetType);
Predicate<IProperty> filter = delegate(IProperty property) {
return property.IsIndexer && IsAccessible(property, allowProtectedAccess);
};
if (typeArgumentCount == 0) {
members.AddRange(type.GetMembers(context, memberFilter));
// Note: IsInvocable-checking cannot be done as part of the memberFilter;
// because it must be done after type substitution.
if (isInvocation)
members.RemoveAll(m => !IsInvocable(m, context));
} else {
// No need to check for isInvocation/isInvocable here:
// we filter out all non-methods
members.AddRange(type.GetMethods(typeArguments, context, memberFilter));
}
// TODO: can't members also hide types?
List<LookupGroup> lookupGroups = new List<LookupGroup>();
foreach (IType type in targetType.GetNonInterfaceBaseTypes(context)) {
List<IParameterizedMember> newMethods = null;
IMember newNonMethod = null;
IEnumerable<IType> typeBaseTypes = null;
var members = type.GetProperties(context, filter, GetMemberOptions.IgnoreInheritedMembers);
AddMembers(type, members, lookupGroups, true, ref typeBaseTypes, ref newMethods, ref newNonMethod);
if (newMethods != null || newNonMethod != null)
lookupGroups.Add(new LookupGroup(type, null, newMethods, newNonMethod));
}
RemoveTypesHiddenByOtherTypes(types);
// remove members hidden by types
for (int i = 0; i < types.Count; i++) {
ITypeDefinition d = GetDeclaringTypeDef(types[i]);
if (d != null)
members.RemoveAll(m => d.IsDerivedFrom(m.DeclaringTypeDefinition, context));
// Remove interface members hidden by class members.
if (targetType.Kind == TypeKind.TypeParameter) {
// This can happen only with type parameters.
RemoveInterfaceMembersHiddenByClassMembers(lookupGroups);
}
ParameterListComparer parameterListComparer = new ParameterListComparer(context);
// Remove all hidden groups
lookupGroups.RemoveAll(g => g.MethodsAreHidden || g.Methods.Count == 0);
// remove members hidden by other members
for (int i = members.Count - 1; i >= 0; i--) {
ITypeDefinition d = members[i].DeclaringTypeDefinition;
IMethod mi = members[i] as IMethod;
// nested loop depends on the fact that the members of more derived classes appear later in the list
for (int j = i + 1; j < members.Count; j++) {
if (mi != null) {
IMethod mj = members[j] as IMethod;
if (mj != null && !parameterListComparer.Equals(mi, mj))
continue;
}
ITypeDefinition s = members[j].DeclaringTypeDefinition;
if (s != null && s != d && s.IsDerivedFrom(d, context)) {
// members[j] hides members[i]
members.RemoveAt(i);
break;
MethodListWithDeclaringType[] methodLists = new MethodListWithDeclaringType[lookupGroups.Count];
for (int i = 0; i < methodLists.Length; i++) {
methodLists[i] = new MethodListWithDeclaringType(lookupGroups[i].DeclaringType, lookupGroups[i].Methods);
}
return methodLists;
}
#endregion
#region AddNestedTypes
/// <summary>
/// Adds the nested types to 'newNestedTypes' and removes any hidden members from the existing lookup groups.
/// </summary>
/// <param name="type">Declaring type of the nested types</param>
/// <param name="nestedTypes">List of nested types to add.</param>
/// <param name="typeArgumentCount">The number of type arguments - used for hiding types from the base class</param>
/// <param name="lookupGroups">List of existing lookup groups</param>
/// <param name="typeBaseTypes">The base types of 'type' (initialized on demand)</param>
/// <param name="newNestedTypes">The target list (created on demand).</param>
void AddNestedTypes(IType type, IEnumerable<IType> nestedTypes, int typeArgumentCount,
List<LookupGroup> lookupGroups,
ref IEnumerable<IType> typeBaseTypes,
ref List<IType> newNestedTypes)
{
foreach (IType nestedType in nestedTypes) {
// Remove all non-types declared in a base type of 'type',
// and all types with same number of type parameters declared in a base type of 'type'.
foreach (var lookupGroup in lookupGroups) {
if (lookupGroup.AllHidden)
continue; // everything is already hidden
if (typeBaseTypes == null)
typeBaseTypes = type.GetNonInterfaceBaseTypes(context);
if (typeBaseTypes.Contains(lookupGroup.DeclaringType)) {
lookupGroup.MethodsAreHidden = true;
lookupGroup.NonMethodIsHidden = true;
if (lookupGroup.NestedTypes != null)
lookupGroup.NestedTypes.RemoveAll(t => InnerTypeParameterCount(t) == typeArgumentCount);
}
}
// Add the new nested type.
if (newNestedTypes == null)
newNestedTypes = new List<IType>();
newNestedTypes.Add(nestedType);
}
// remove interface members hidden by class members
if (type is ITypeParameter) {
// this can happen only with type parameters
for (int i = members.Count - 1; i >= 0; i--) {
if (members[i].DeclaringTypeDefinition.Kind != TypeKind.Interface)
continue;
IMethod mi = members[i] as IMethod;
for (int j = 0; j < members.Count; j++) {
if (mi != null) {
IMethod mj = members[j] as IMethod;
if (mj != null && !parameterListComparer.Equals(mi, mj))
continue;
}
#endregion
#region AddMembers
/// <summary>
/// Adds members to 'newMethods'/'newNonMethod'.
/// Removes any members in the existing lookup groups that were hidden by added members.
/// Substitutes 'virtual' members in the existing lookup groups for added 'override' members.
/// </summary>
/// <param name="type">Declaring type of the members</param>
/// <param name="members">List of members to add.</param>
/// <param name="lookupGroups">List of existing lookup groups</param>
/// <param name="treatAllParameterizedMembersAsMethods">Whether to treat properties as methods</param>
/// <param name="typeBaseTypes">The base types of 'type' (initialized on demand)</param>
/// <param name="newMethods">The target list for methods (created on demand).</param>
/// <param name="newNonMethod">The target variable for non-method members.</param>
void AddMembers(IType type, IEnumerable<IMember> members, List<LookupGroup> lookupGroups,
bool treatAllParameterizedMembersAsMethods,
ref IEnumerable<IType> typeBaseTypes, ref List<IParameterizedMember> newMethods, ref IMember newNonMethod)
{
foreach (IMember member in members) {
IParameterizedMember method;
if (treatAllParameterizedMembersAsMethods)
method = member as IParameterizedMember;
else
method = member as IMethod;
bool replacedVirtualMemberWithOverride = false;
if (member.IsOverride) {
// Replacing virtual member with override:
// Go backwards so that we find the corresponding virtual member
// in the most-derived type
for (int i = lookupGroups.Count - 1; i >= 0 && !replacedVirtualMemberWithOverride; i--) {
if (typeBaseTypes == null)
typeBaseTypes = type.GetNonInterfaceBaseTypes(context);
var lookupGroup = lookupGroups[i];
if (typeBaseTypes.Contains(lookupGroup.DeclaringType)) {
if (method != null) {
// Find the matching method, and replace it with the override
for (int j = 0; j < lookupGroup.Methods.Count; j++) {
if (ParameterListComparer.Compare(context, method, lookupGroup.Methods[j])) {
lookupGroup.Methods[j] = method;
replacedVirtualMemberWithOverride = true;
break;
}
}
} else {
// If the member type matches, replace it with the override
if (lookupGroup.NonMethod != null && lookupGroup.NonMethod.EntityType == member.EntityType) {
lookupGroup.NonMethod = member;
replacedVirtualMemberWithOverride = true;
break;
}
}
}
ITypeDefinition s = members[j].DeclaringTypeDefinition;
if (s != null && IsNonInterfaceType(s)) {
// members[j] hides members[i]
members.RemoveAt(i);
break;
}
}
// If the member wasn't an override, or if we didn't find any matching virtual method,
// proceed to add the member.
if (!replacedVirtualMemberWithOverride) {
// Make the member hide other members:
foreach (var lookupGroup in lookupGroups) {
if (lookupGroup.AllHidden)
continue; // everything is already hidden
if (typeBaseTypes == null)
typeBaseTypes = type.GetNonInterfaceBaseTypes(context);
if (typeBaseTypes.Contains(lookupGroup.DeclaringType)) {
// Methods hide all non-methods; Non-methods hide everything
lookupGroup.NestedTypes = null;
lookupGroup.NonMethodIsHidden = true;
if (method == null) { // !(member is IMethod)
lookupGroup.MethodsAreHidden = true;
}
}
}
// Add the new member
if (method != null) {
if (newMethods == null)
newMethods = new List<IParameterizedMember>();
newMethods.Add(method);
} else {
newNonMethod = member;
}
}
}
if (types.Count > 0) {
bool isAmbiguous = !(types.Count == 1 && members.Count == 0);
if (isAmbiguous)
return new AmbiguousTypeResolveResult(types[0]);
else
return new TypeResolveResult(types[0]);
}
#endregion
#region RemoveInterfaceMembersHiddenByClassMembers
void RemoveInterfaceMembersHiddenByClassMembers(List<LookupGroup> lookupGroups)
{
foreach (var classLookupGroup in lookupGroups) {
if (IsInterfaceOrSystemObject(classLookupGroup.DeclaringType))
continue;
// The current lookup groups contains class members that might hide interface members
bool hasNestedTypes = classLookupGroup.NestedTypes != null && classLookupGroup.NestedTypes.Count > 0;
if (hasNestedTypes || !classLookupGroup.NonMethodIsHidden) {
// Hide all members from interface types
foreach (var interfaceLookupGroup in lookupGroups) {
if (IsInterfaceOrSystemObject(interfaceLookupGroup.DeclaringType)) {
interfaceLookupGroup.NestedTypes = null;
interfaceLookupGroup.NonMethodIsHidden = true;
interfaceLookupGroup.MethodsAreHidden = true;
}
}
} else if (!classLookupGroup.MethodsAreHidden) {
foreach (IMethod classMethod in classLookupGroup.Methods) {
// Hide all non-methods from interface types, and all methods with the same signature
// as a method in this class type.
foreach (var interfaceLookupGroup in lookupGroups) {
if (IsInterfaceOrSystemObject(interfaceLookupGroup.DeclaringType)) {
interfaceLookupGroup.NestedTypes = null;
interfaceLookupGroup.NonMethodIsHidden = true;
if (interfaceLookupGroup.Methods != null && !interfaceLookupGroup.MethodsAreHidden) {
// The mapping of virtual to overridden methods is already done,
// so we can simply remove the methods from the collection
interfaceLookupGroup.Methods.RemoveAll(
m => ParameterListComparer.Compare(context, classMethod, m));
}
}
}
}
}
}
if (members.Count == 0)
return new UnknownMemberResolveResult(type, name, typeArguments);
IMember firstNonMethod = members.FirstOrDefault(m => !(m is IMethod));
if (members.Count == 1 && firstNonMethod != null)
return new MemberResolveResult(targetResolveResult, firstNonMethod, context);
if (firstNonMethod == null)
return new MethodGroupResolveResult(targetResolveResult, name, members.ConvertAll(m => (IMethod)m), typeArguments);
return new AmbiguousMemberResolveResult(targetResolveResult, firstNonMethod, firstNonMethod.ReturnType.Resolve(context));
}
static bool IsNonInterfaceType(ITypeDefinition def)
static bool IsInterfaceOrSystemObject(IType type)
{
// return type if def is neither an interface nor System.Object
return def.Kind != TypeKind.Interface && !(def.Name == "Object" && def.Namespace == "System" && def.TypeParameterCount == 0);
// return if type is an interface or System.Object
return type.Kind == TypeKind.Interface
|| (type.Name == "Object" && type.Namespace == "System" && type.TypeParameterCount == 0);
}
#endregion
static ITypeDefinition GetDeclaringTypeDef(IType type)
#region CreateResult
ResolveResult CreateResult(ResolveResult targetResolveResult, List<LookupGroup> lookupGroups, string name, IList<IType> typeArguments)
{
IType declType = type.DeclaringType;
return declType != null ? declType.GetDefinition() : null;
// Remove all hidden groups
lookupGroups.RemoveAll(g => g.AllHidden);
if (lookupGroups.Count == 0) {
// No members found
return new UnknownMemberResolveResult(targetResolveResult.Type, name, typeArguments);
}
if (lookupGroups.Any(g => !g.MethodsAreHidden && g.Methods.Count > 0)) {
// If there are methods, make a MethodGroupResolveResult.
// Note that a conflict between a member and a method (possible with multiple interface inheritance)
// is only a warning, not an error, and the C# compiler will prefer the method group.
List<MethodListWithDeclaringType> methodLists = new List<MethodListWithDeclaringType>();
foreach (var lookupGroup in lookupGroups) {
if (!lookupGroup.MethodsAreHidden && lookupGroup.Methods.Count > 0) {
var methodListWithDeclType = new MethodListWithDeclaringType(lookupGroup.DeclaringType);
foreach (var method in lookupGroup.Methods) {
methodListWithDeclType.Add((IMethod)method);
}
methodLists.Add(methodListWithDeclType);
}
}
return new MethodGroupResolveResult(targetResolveResult, name, methodLists, typeArguments);
}
// If there are ambiguities, report the most-derived result (last group)
LookupGroup resultGroup = lookupGroups[lookupGroups.Count - 1];
if (resultGroup.NestedTypes != null && resultGroup.NestedTypes.Count > 0) {
if (resultGroup.NestedTypes.Count > 1 || !resultGroup.NonMethodIsHidden || lookupGroups.Count > 1)
return new AmbiguousTypeResolveResult(resultGroup.NestedTypes[0]);
else
return new TypeResolveResult(resultGroup.NestedTypes[0]);
}
if (lookupGroups.Count > 1) {
return new AmbiguousMemberResolveResult(targetResolveResult, resultGroup.NonMethod,
resultGroup.NonMethod.ReturnType.Resolve(context));
} else {
return new MemberResolveResult(targetResolveResult, resultGroup.NonMethod, context);
}
}
#endregion
}
}

83
ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs

@ -26,17 +26,37 @@ using ICSharpCode.NRefactory.TypeSystem; @@ -26,17 +26,37 @@ using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
public class MethodListWithDeclaringType : List<IParameterizedMember>
{
readonly IType declaringType;
public IType DeclaringType {
get { return declaringType; }
}
public MethodListWithDeclaringType(IType declaringType)
{
this.declaringType = declaringType;
}
public MethodListWithDeclaringType(IType declaringType, IEnumerable<IParameterizedMember> methods)
: base(methods)
{
this.declaringType = declaringType;
}
}
/// <summary>
/// Represents a group of methods.
/// </summary>
public class MethodGroupResolveResult : ResolveResult
{
readonly IList<IMethod> methods;
readonly IList<MethodListWithDeclaringType> methodLists;
readonly IList<IType> typeArguments;
readonly ResolveResult targetResult;
readonly string methodName;
public MethodGroupResolveResult(ResolveResult targetResult, string methodName, IList<IMethod> methods, IList<IType> typeArguments) : base(SharedTypes.UnknownType)
public MethodGroupResolveResult(ResolveResult targetResult, string methodName, IList<MethodListWithDeclaringType> methods, IList<IType> typeArguments) : base(SharedTypes.UnknownType)
{
if (targetResult == null)
throw new ArgumentNullException("targetResult");
@ -44,7 +64,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -44,7 +64,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
throw new ArgumentNullException("methods");
this.targetResult = targetResult;
this.methodName = methodName;
this.methods = methods;
this.methodLists = methods;
this.typeArguments = typeArguments ?? EmptyList<IType>.Instance;
}
@ -73,8 +93,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -73,8 +93,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// Gets the methods that were found.
/// This list does not include extension methods.
/// </summary>
public IList<IMethod> Methods {
get { return methods; }
public IEnumerable<IMethod> Methods {
get { return methodLists.SelectMany(m => m.Cast<IMethod>()); }
}
/// <summary>
/// Gets the methods that were found.
/// This list does not include extension methods.
/// </summary>
public IEnumerable<MethodListWithDeclaringType> MethodsGroupedByDeclaringType {
get { return methodLists; }
}
/// <summary>
@ -97,6 +125,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -97,6 +125,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary>
/// Gets all candidate extension methods.
/// </summary>
/// <remarks>
/// 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 }
/// }
/// </remarks>
public List<List<IMethod>> GetExtensionMethods()
{
if (resolver != null) {
@ -116,7 +153,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -116,7 +153,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public override string ToString()
{
return string.Format("[{0} with {1} method(s)]", GetType().Name, methods.Count);
return string.Format("[{0} with {1} method(s)]", GetType().Name, methodLists.Count);
}
public OverloadResolution PerformOverloadResolution(ITypeResolveContext context, ResolveResult[] arguments, string[] argumentNames = null, bool allowExtensionMethods = true, bool allowExpandingParams = true)
@ -127,14 +164,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -127,14 +164,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
var typeArgumentArray = this.TypeArguments.ToArray();
OverloadResolution or = new OverloadResolution(context, arguments, argumentNames, typeArgumentArray);
or.AllowExpandingParams = allowExpandingParams;
foreach (IMethod method in this.Methods) {
// TODO: grouping by class definition?
Log.Indent();
OverloadResolutionErrors errors = or.AddCandidate(method);
Log.Unindent();
LogOverloadResolution(" Candidate", method, errors, or);
}
or.AddMethodLists(methodLists);
if (allowExtensionMethods && !or.FoundApplicableCandidate) {
// No applicable match found, so let's try extension methods.
@ -159,7 +191,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -159,7 +191,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Log.Indent();
OverloadResolutionErrors errors = extOr.AddCandidate(method);
Log.Unindent();
LogOverloadResolution(" Extension", method, errors, or);
or.LogCandidateAddingResult(" Extension", method, errors);
}
if (extOr.FoundApplicableCandidate)
break;
@ -177,27 +209,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -177,27 +209,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return or;
}
[Conditional("DEBUG")]
void LogOverloadResolution(string text, IMethod method, OverloadResolutionErrors errors, OverloadResolution or)
{
#if DEBUG
StringBuilder b = new StringBuilder(text);
b.Append(' ');
b.Append(method);
b.Append(" = ");
if (errors == OverloadResolutionErrors.None)
b.Append("Success");
else
b.Append(errors);
if (or.BestCandidate == method) {
b.Append(" (best candidate so far)");
} else if (or.BestCandidateAmbiguousWith == method) {
b.Append(" (ambiguous)");
}
Log.WriteLine(b.ToString());
#endif
}
public override IEnumerable<ResolveResult> GetChildResults()
{
if (targetResult != null)

79
ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs

@ -18,7 +18,9 @@ @@ -18,7 +18,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
@ -188,6 +190,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -188,6 +190,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return c.Errors;
}
public static bool IsApplicable(OverloadResolutionErrors errors)
{
return (errors & ~OverloadResolutionErrors.AmbiguousMatch) == OverloadResolutionErrors.None;
}
/// <summary>
/// Calculates applicability etc. for the candidate.
/// </summary>
@ -220,6 +227,75 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -220,6 +227,75 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
#endregion
#region AddMethodLists
/// <summary>
/// Adds all candidates from the method lists.
///
/// This method implements the logic that causes applicable methods in derived types to hide
/// all methods in base types.
/// </summary>
public void AddMethodLists(IList<MethodListWithDeclaringType> methodLists)
{
if (methodLists == null)
throw new ArgumentNullException("methodLists");
// Base types come first, so go through the list backwards (derived types first)
bool[] isHiddenByDerivedType;
if (methodLists.Count > 1)
isHiddenByDerivedType = new bool[methodLists.Count];
else
isHiddenByDerivedType = null;
for (int i = methodLists.Count - 1; i >= 0; i--) {
if (isHiddenByDerivedType != null && isHiddenByDerivedType[i]) {
Log.WriteLine(" Skipping methods in {0} because they are hidden by an applicable method in a derived type", methodLists[i].DeclaringType);
continue;
}
MethodListWithDeclaringType methodList = methodLists[i];
bool foundApplicableCandidateInCurrentList = false;
for (int j = 0; j < methodList.Count; j++) {
IParameterizedMember method = methodList[j];
Log.Indent();
OverloadResolutionErrors errors = AddCandidate(method);
Log.Unindent();
LogCandidateAddingResult(" Candidate", method, errors);
foundApplicableCandidateInCurrentList |= IsApplicable(errors);
}
if (foundApplicableCandidateInCurrentList && i > 0) {
foreach (IType baseType in methodList.DeclaringType.GetAllBaseTypes(context)) {
for (int j = 0; j < i; j++) {
if (!isHiddenByDerivedType[j] && baseType.Equals(methodLists[j].DeclaringType))
isHiddenByDerivedType[j] = true;
}
}
}
}
}
[Conditional("DEBUG")]
internal void LogCandidateAddingResult(string text, IParameterizedMember method, OverloadResolutionErrors errors)
{
#if DEBUG
StringBuilder b = new StringBuilder(text);
b.Append(' ');
b.Append(method);
b.Append(" = ");
if (errors == OverloadResolutionErrors.None)
b.Append("Success");
else
b.Append(errors);
if (this.BestCandidate == method) {
b.Append(" (best candidate so far)");
} else if (this.BestCandidateAmbiguousWith == method) {
b.Append(" (ambiguous)");
}
Log.WriteLine(b.ToString());
#endif
}
#endregion
#region MapCorrespondingParameters
void MapCorrespondingParameters(Candidate candidate)
{
@ -345,7 +421,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -345,7 +421,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ConstraintsValid = false;
ConstraintsValid &= typeArg.GetConstructors(
context,
m => m.Parameters.Count == 0 && m.Accessibility == Accessibility.Public
m => m.Parameters.Count == 0 && m.Accessibility == Accessibility.Public,
GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions
).Any();
}
foreach (IType constraintType in tp.Constraints) {

4
ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs

@ -38,6 +38,8 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -38,6 +38,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// <remarks>This is the reflexive and transitive closure of <see cref="IType.GetBaseTypes"/>.
/// 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.
/// </remarks>
public static IEnumerable<IType> GetAllBaseTypes(this IType type, ITypeResolveContext context)
{
@ -51,6 +53,8 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -51,6 +53,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
/// <remarks>
/// When <paramref name="type"/> is an interface, this method will also return base interfaces.
///
/// The output is ordered so that base types occur in before derived types.
/// </remarks>
public static IEnumerable<IType> GetNonInterfaceBaseTypes(this IType type, ITypeResolveContext context)
{

37
ICSharpCode.NRefactory/TypeSystem/Implementation/BaseTypeCollector.cs

@ -27,7 +27,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -27,7 +27,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
sealed class BaseTypeCollector : List<IType>
{
readonly ITypeResolveContext context;
readonly Stack<ITypeDefinition> activeTypeDefinitions = new Stack<ITypeDefinition>();
readonly Stack<IType> activeTypes = new Stack<IType>();
/// <summary>
/// If this option is enabled, the list will not contain interfaces when retrieving the base types
@ -42,21 +42,24 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -42,21 +42,24 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
public void CollectBaseTypes(IType type)
{
ITypeDefinition def = type.GetDefinition();
if (def != null) {
// Maintain a stack of currently active type definitions, and avoid having one definition
// multiple times on that stack.
// This is necessary to ensure the output is finite in the presence of cyclic inheritance:
// class C<X> : C<C<X>> {} would not be caught by the 'no duplicate output' check, yet would
// produce infinite output.
if (activeTypeDefinitions.Contains(def))
return;
activeTypeDefinitions.Push(def);
}
IType def = type.GetDefinition() ?? type;
// Maintain a stack of currently active type definitions, and avoid having one definition
// multiple times on that stack.
// This is necessary to ensure the output is finite in the presence of cyclic inheritance:
// class C<X> : C<C<X>> {} would not be caught by the 'no duplicate output' check, yet would
// produce infinite output.
if (activeTypes.Contains(def))
return;
activeTypes.Push(def);
// Note that we also need to push non-type definitions, e.g. for protecting against
// cyclic inheritance in type parameters (where T : S where S : T).
// The output check doesn't help there because we call Add(type) only at the end.
// We can't simply call this.Add(type); at the start because that would return in an incorrect order.
// Avoid outputting a type more than once - necessary for "diamond" multiple inheritance
// (e.g. C implements I1 and I2, and both interfaces derive from Object)
if (!this.Contains(type)) {
this.Add(type);
foreach (IType baseType in type.GetBaseTypes(context)) {
if (SkipImplementedInterfaces && def != null && def.Kind != TypeKind.Interface) {
if (baseType.Kind == TypeKind.Interface) {
@ -66,9 +69,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -66,9 +69,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
}
CollectBaseTypes(baseType);
}
// Add(type) at the end - we want a type to be output only after all its base types were added.
this.Add(type);
// Note that this is not the same as putting the this.Add() call in front and then reversing the list.
// For the diamond inheritance, Add() at the start produces "C, I1, Object, I2",
// while Add() at the end produces "Object, I1, I2, C".
}
if (def != null)
activeTypeDefinitions.Pop();
activeTypes.Pop();
}
}
}

37
ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs

@ -21,26 +21,9 @@ using System.Collections.Generic; @@ -21,26 +21,9 @@ using System.Collections.Generic;
namespace ICSharpCode.NRefactory.TypeSystem
{
public sealed class ParameterListComparer : IEqualityComparer<IParameterizedMember>
public static class ParameterListComparer
{
ITypeResolveContext context;
/// <summary>
/// Creates a new ParameterListComparer that compares type <b>references</b>.
/// </summary>
public ParameterListComparer()
{
}
/// <summary>
/// Creates a new ParameterListComparer that uses the specified context to resolve types.
/// </summary>
public ParameterListComparer(ITypeResolveContext context)
{
this.context = context;
}
public bool Equals(IParameterizedMember x, IParameterizedMember y)
public static bool Compare(ITypeResolveContext context, IParameterizedMember x, IParameterizedMember y)
{
var px = x.Parameters;
var py = y.Parameters;
@ -53,27 +36,19 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -53,27 +36,19 @@ namespace ICSharpCode.NRefactory.TypeSystem
continue;
if (a == null || b == null)
return false;
if (context != null) {
if (!a.Type.Resolve(context).Equals(b.Type.Resolve(context)))
return false;
} else {
if (!a.Type.Equals(b.Type))
return false;
}
if (!a.Type.Resolve(context).Equals(b.Type.Resolve(context)))
return false;
}
return true;
}
public int GetHashCode(IParameterizedMember obj)
public static int GetHashCode(ITypeResolveContext context, IParameterizedMember obj)
{
int hashCode = obj.Parameters.Count;
unchecked {
foreach (IParameter p in obj.Parameters) {
hashCode *= 27;
if (context != null)
hashCode += p.Type.Resolve(context).GetHashCode();
else
hashCode += p.Type.GetHashCode();
hashCode += p.Type.Resolve(context).GetHashCode();
}
}
return hashCode;

Loading…
Cancel
Save