diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveAtLocation.cs b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveAtLocation.cs index aba11c97ec..e95d1d107f 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveAtLocation.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveAtLocation.cs @@ -33,9 +33,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public static ResolveResult Resolve (ICompilation compilation, CSharpParsedFile parsedFile, CompilationUnit cu, TextLocation location, CancellationToken cancellationToken = default(CancellationToken)) { - return Resolve (() => compilation, parsedFile, cu, location, cancellationToken); + return Resolve (new Lazy(() => compilation), parsedFile, cu, location, cancellationToken); } - public static ResolveResult Resolve(Func compilation, CSharpParsedFile parsedFile, CompilationUnit cu, TextLocation location, + public static ResolveResult Resolve(Lazy compilation, CSharpParsedFile parsedFile, CompilationUnit cu, TextLocation location, CancellationToken cancellationToken = default(CancellationToken)) { AstNode node; @@ -45,9 +45,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public static ResolveResult Resolve (ICompilation compilation, CSharpParsedFile parsedFile, CompilationUnit cu, TextLocation location, out AstNode node, CancellationToken cancellationToken = default(CancellationToken)) { - return Resolve (() => compilation, parsedFile, cu, location, out node, cancellationToken); + return Resolve (new Lazy(() => compilation), parsedFile, cu, location, out node, cancellationToken); } - public static ResolveResult Resolve(Func compilation, CSharpParsedFile parsedFile, CompilationUnit cu, TextLocation location, out AstNode node, + public static ResolveResult Resolve(Lazy compilation, CSharpParsedFile parsedFile, CompilationUnit cu, TextLocation location, out AstNode node, CancellationToken cancellationToken = default(CancellationToken)) { node = cu.GetNodeAt(location); @@ -94,8 +94,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver parentInvocation = node.Parent as InvocationExpression; } - CSharpAstResolver resolver = new CSharpAstResolver(compilation(), cu, parsedFile); - resolver.ApplyNavigator(new NodeListResolveVisitorNavigator(node), cancellationToken); + // TODO: I think we should provide an overload so that an existing CSharpAstResolver can be reused + CSharpAstResolver resolver = new CSharpAstResolver(compilation.Value, cu, parsedFile); ResolveResult rr = resolver.Resolve(node, cancellationToken); if (rr is MethodGroupResolveResult && parentInvocation != null) return resolver.Resolve(parentInvocation); diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs index 4213d5729b..86300a0ab7 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs @@ -653,16 +653,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver CSharpResolver oldResolver = resolver; for (AstNode node = fieldOrEventDeclaration.FirstChild; node != null; node = node.NextSibling) { if (node.Role == Roles.Variable) { - IMember member = null; + IMember member; if (parsedFile != null) { member = GetMemberFromLocation(node.StartLocation); - } else if (resolver.CurrentTypeDefinition != null) { + } else { string name = ((VariableInitializer)node).Name; - if (entityType == EntityType.Event) { - member = resolver.CurrentTypeDefinition.GetEvents(e => e.Name == name && !e.IsExplicitInterfaceImplementation, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); - } else { - member = resolver.CurrentTypeDefinition.GetFields(e => e.Name == name, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); - } + member = AbstractUnresolvedMember.Resolve(resolver.CurrentTypeResolveContext, entityType, name); } resolver = resolver.WithCurrentMember(member); @@ -775,46 +771,30 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { CSharpResolver oldResolver = resolver; try { - IMember member = null; + IMember member; if (parsedFile != null) { member = GetMemberFromLocation(memberDeclaration.StartLocation); - } else if (resolver.CurrentTypeDefinition != null) { + } else { // Re-discover the method: EntityType entityType = memberDeclaration.EntityType; - var typeParameters = memberDeclaration.GetChildrenByRole(Roles.TypeParameter).ToArray(); var parameterTypes = TypeSystemConvertVisitor.GetParameterTypes(memberDeclaration.GetChildrenByRole(Roles.Parameter)); if (entityType == EntityType.Constructor) { - bool isStatic = memberDeclaration.HasModifier(Modifiers.Static); - member = resolver.CurrentTypeDefinition.Methods.FirstOrDefault( - m => m.EntityType == entityType && m.IsStatic == isStatic - && m.Parameters.Count == parameterTypes.Count - && IsMatchingMethod(m, typeParameters, parameterTypes)); + string name = memberDeclaration.HasModifier(Modifiers.Static) ? ".cctor" : ".ctor"; + member = AbstractUnresolvedMember.Resolve( + resolver.CurrentTypeResolveContext, entityType, name, + parameterTypeReferences: parameterTypes); + } else if (entityType == EntityType.Destructor) { + member = AbstractUnresolvedMember.Resolve(resolver.CurrentTypeResolveContext, entityType, "Finalize"); } else { - string name = (entityType == EntityType.Destructor ? "Finalize" : memberDeclaration.Name); + string[] typeParameterNames = memberDeclaration.GetChildrenByRole(Roles.TypeParameter).Select(tp => tp.Name).ToArray(); AstType explicitInterfaceAstType = memberDeclaration.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole); - bool isExplicitInterfaceImplementation = false; - IType explicitInterfaceType = null; + ITypeReference explicitInterfaceType = null; if (!explicitInterfaceAstType.IsNull) { - isExplicitInterfaceImplementation = true; - explicitInterfaceType = explicitInterfaceAstType.ToTypeReference().Resolve(resolver.CurrentTypeResolveContext); - } - foreach (var method in resolver.CurrentTypeDefinition.GetMethods( - m => m.EntityType == entityType && m.Name == name - && m.TypeParameters.Count == typeParameters.Length && m.Parameters.Count == parameterTypes.Count - && m.IsExplicitInterfaceImplementation == isExplicitInterfaceImplementation, - GetMemberOptions.IgnoreInheritedMembers - )) { - if (isExplicitInterfaceImplementation) { - if (method.ImplementedInterfaceMembers.Count != 1) - continue; - if (!explicitInterfaceType.Equals(method.ImplementedInterfaceMembers[0].DeclaringType)) - continue; - } - if (IsMatchingMethod(method, typeParameters, parameterTypes)) { - member = method; - break; - } + explicitInterfaceType = explicitInterfaceAstType.ToTypeReference(); } + member = AbstractUnresolvedMember.Resolve( + resolver.CurrentTypeResolveContext, entityType, memberDeclaration.Name, + explicitInterfaceType, typeParameterNames, parameterTypes); } } resolver = resolver.WithCurrentMember(member); @@ -829,20 +809,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - bool IsMatchingMethod(IMethod method, TypeParameterDeclaration[] typeParameters, IList parameterTypes) - { - for (int i = 0; i < typeParameters.Length; i++) { - if (method.TypeParameters[i].Name != typeParameters[i].Name) - return false; - } - var resolvedParameterTypes = parameterTypes.Resolve(resolver.CurrentTypeResolveContext.WithCurrentMember(method)); - for (int i = 0; i < parameterTypes.Count; i++) { - if (!method.Parameters[i].Type.Equals(resolvedParameterTypes[i])) - return false; - } - return true; - } - ResolveResult IAstVisitor.VisitMethodDeclaration(MethodDeclaration methodDeclaration) { return VisitMethodMember(methodDeclaration); @@ -868,36 +834,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { CSharpResolver oldResolver = resolver; try { - IMember member = null; + IMember member; if (parsedFile != null) { member = GetMemberFromLocation(propertyOrIndexerDeclaration.StartLocation); - } else if (resolver.CurrentTypeDefinition != null) { + } else { // Re-discover the property: string name = propertyOrIndexerDeclaration.Name; var parameterTypeReferences = TypeSystemConvertVisitor.GetParameterTypes(propertyOrIndexerDeclaration.GetChildrenByRole(Roles.Parameter)); - var parameterTypes = parameterTypeReferences.Resolve(resolver.CurrentTypeResolveContext); AstType explicitInterfaceAstType = propertyOrIndexerDeclaration.GetChildByRole(EntityDeclaration.PrivateImplementationTypeRole); - bool isExplicitInterfaceImplementation = false; - IType explicitInterfaceType = null; + ITypeReference explicitInterfaceType = null; if (!explicitInterfaceAstType.IsNull) { - isExplicitInterfaceImplementation = true; - explicitInterfaceType = explicitInterfaceAstType.ToTypeReference().Resolve(resolver.CurrentTypeResolveContext); - } - foreach (IProperty property in resolver.CurrentTypeDefinition.GetProperties( - p => p.Name == name && p.Parameters.Count == parameterTypes.Count && p.IsExplicitInterfaceImplementation == isExplicitInterfaceImplementation, - GetMemberOptions.IgnoreInheritedMembers - )) { - if (isExplicitInterfaceImplementation) { - if (property.ImplementedInterfaceMembers.Count != 1) - continue; - if (!explicitInterfaceType.Equals(property.ImplementedInterfaceMembers[0].DeclaringType)) - continue; - } - if (Enumerable.SequenceEqual(parameterTypes, property.Parameters.Select(p => p.Type))) { - member = property; - break; - } + explicitInterfaceType = explicitInterfaceAstType.ToTypeReference(); } + member = AbstractUnresolvedMember.Resolve( + resolver.CurrentTypeResolveContext, propertyOrIndexerDeclaration.EntityType, name, + explicitInterfaceType, parameterTypeReferences: parameterTypeReferences); } resolver = resolver.WithCurrentMember(member); @@ -934,24 +885,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { CSharpResolver oldResolver = resolver; try { - IMember member = null; + IMember member; if (parsedFile != null) { member = GetMemberFromLocation(eventDeclaration.StartLocation); - } else if (resolver.CurrentTypeDefinition != null) { + } else { string name = eventDeclaration.Name; AstType explicitInterfaceAstType = eventDeclaration.PrivateImplementationType; if (explicitInterfaceAstType.IsNull) { - member = resolver.CurrentTypeDefinition.GetEvents( - e => e.Name == name && !e.IsExplicitInterfaceImplementation, - GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); + member = AbstractUnresolvedMember.Resolve(resolver.CurrentTypeResolveContext, EntityType.Event, name); } else { - IType explicitInterfaceType = explicitInterfaceAstType.ToTypeReference().Resolve(resolver.CurrentTypeResolveContext); - member = resolver.CurrentTypeDefinition.GetEvents( - e => e.Name == name && e.IsExplicitInterfaceImplementation, - GetMemberOptions.IgnoreInheritedMembers - ).FirstOrDefault( - e => e.ImplementedInterfaceMembers.Count == 1 && explicitInterfaceType.Equals(e.ImplementedInterfaceMembers[0].DeclaringType) - ); + member = AbstractUnresolvedMember.Resolve(resolver.CurrentTypeResolveContext, EntityType.Event, name, + explicitInterfaceAstType.ToTypeReference()); } } resolver = resolver.WithCurrentMember(member); diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpParsedFile.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpParsedFile.cs index 889fde118a..186961031f 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpParsedFile.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpParsedFile.cs @@ -213,6 +213,9 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem var unresolvedTypeDef = entity as IUnresolvedTypeDefinition ?? entity.DeclaringTypeDefinition; var resolvedTypeDef = resolvedEntity as ITypeDefinition ?? resolvedEntity.DeclaringTypeDefinition; if (unresolvedTypeDef != null && resolvedTypeDef != null) { + // Strictly speaking, we would have to pass the parent context into CreateResolveContext, + // then transform the result using WithTypeDefinition(). + // However, we can simplify this here because we know this is a C# type definition. var context = unresolvedTypeDef.CreateResolveContext(new SimpleTypeResolveContext(resolvedTypeDef)); if (resolvedEntity is IMember) context = context.WithCurrentMember((IMember)resolvedEntity); diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpTypeResolveContext.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpTypeResolveContext.cs index 33f3639b53..aadc413047 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpTypeResolveContext.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpTypeResolveContext.cs @@ -27,6 +27,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem readonly ResolvedUsingScope currentUsingScope; readonly ITypeDefinition currentTypeDefinition; readonly IMember currentMember; + readonly string[] methodTypeParameterNames; public CSharpTypeResolveContext(IAssembly assembly, ResolvedUsingScope usingScope = null, ITypeDefinition typeDefinition = null, IMember member = null) { @@ -38,6 +39,15 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem this.currentMember = member; } + private CSharpTypeResolveContext(IAssembly assembly, ResolvedUsingScope usingScope, ITypeDefinition typeDefinition, IMember member, string[] methodTypeParameterNames) + { + this.assembly = assembly; + this.currentUsingScope = usingScope; + this.currentTypeDefinition = typeDefinition; + this.currentMember = member; + this.methodTypeParameterNames = methodTypeParameterNames; + } + public ResolvedUsingScope CurrentUsingScope { get { return currentUsingScope; } } @@ -60,7 +70,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem public CSharpTypeResolveContext WithCurrentTypeDefinition(ITypeDefinition typeDefinition) { - return new CSharpTypeResolveContext(assembly, currentUsingScope, typeDefinition, currentMember); + return new CSharpTypeResolveContext(assembly, currentUsingScope, typeDefinition, currentMember, methodTypeParameterNames); } ITypeResolveContext ITypeResolveContext.WithCurrentTypeDefinition(ITypeDefinition typeDefinition) @@ -70,7 +80,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem public CSharpTypeResolveContext WithCurrentMember(IMember member) { - return new CSharpTypeResolveContext(assembly, currentUsingScope, currentTypeDefinition, member); + return new CSharpTypeResolveContext(assembly, currentUsingScope, currentTypeDefinition, member, methodTypeParameterNames); } ITypeResolveContext ITypeResolveContext.WithCurrentMember(IMember member) @@ -80,7 +90,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem public CSharpTypeResolveContext WithUsingScope(ResolvedUsingScope usingScope) { - return new CSharpTypeResolveContext(assembly, usingScope, currentTypeDefinition, currentMember); + return new CSharpTypeResolveContext(assembly, usingScope, currentTypeDefinition, currentMember, methodTypeParameterNames); } } } diff --git a/ICSharpCode.NRefactory.ConsistencyCheck/ICSharpCode.NRefactory.ConsistencyCheck.csproj b/ICSharpCode.NRefactory.ConsistencyCheck/ICSharpCode.NRefactory.ConsistencyCheck.csproj index 58c0780a5f..0abf5100f1 100644 --- a/ICSharpCode.NRefactory.ConsistencyCheck/ICSharpCode.NRefactory.ConsistencyCheck.csproj +++ b/ICSharpCode.NRefactory.ConsistencyCheck/ICSharpCode.NRefactory.ConsistencyCheck.csproj @@ -59,7 +59,7 @@ - + diff --git a/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs b/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs index b93c60f960..b562f03a9e 100644 --- a/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs +++ b/ICSharpCode.NRefactory.ConsistencyCheck/Program.cs @@ -57,7 +57,8 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck solution.AllFiles.Count(), solution.Projects.Count); - //using (new Timer("ID String test... ")) IDStringConsistencyCheck.Run(solution); + //using (new Timer("ID String test... ")) TypeSystemTests.IDStringConsistencyCheck(solution); + using (new Timer("Resolve unresolved members... ")) TypeSystemTests.ResolvedUnresolvedMembers(solution); //RunTestOnAllFiles("Roundtripping test", RoundtripTest.RunTest); RunTestOnAllFiles("Resolver test", ResolverTest.RunTest); RunTestOnAllFiles("Resolver test (no parsed file)", ResolverTest.RunTestWithoutParsedFile); diff --git a/ICSharpCode.NRefactory.ConsistencyCheck/IDStringConsistencyCheck.cs b/ICSharpCode.NRefactory.ConsistencyCheck/TypeSystemTests.cs similarity index 67% rename from ICSharpCode.NRefactory.ConsistencyCheck/IDStringConsistencyCheck.cs rename to ICSharpCode.NRefactory.ConsistencyCheck/TypeSystemTests.cs index cb6de55897..5c8b6a3aa4 100644 --- a/ICSharpCode.NRefactory.ConsistencyCheck/IDStringConsistencyCheck.cs +++ b/ICSharpCode.NRefactory.ConsistencyCheck/TypeSystemTests.cs @@ -25,9 +25,9 @@ using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.ConsistencyCheck { - public class IDStringConsistencyCheck + public class TypeSystemTests { - public static void Run(Solution solution) + public static void IDStringConsistencyCheck(Solution solution) { foreach (var project in solution.Projects) { var compilation = project.Compilation; @@ -51,5 +51,30 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck if (resolvedEntity != entity) throw new InvalidOperationException(id); } + + public static void ResolvedUnresolvedMembers(Solution solution) + { + foreach (var project in solution.Projects) { + var compilation = project.Compilation; + var context = new SimpleTypeResolveContext(compilation.MainAssembly); + foreach (var typeDef in compilation.MainAssembly.GetAllTypeDefinitions()) { + foreach (var part in typeDef.Parts) { + if (!typeDef.Equals(part.Resolve(context))) + throw new InvalidOperationException(); + } + foreach (var member in typeDef.Members) { + var resolvedMember = member.UnresolvedMember.Resolve(context); + if (!member.Equals(resolvedMember)) + throw new InvalidOperationException(); + } + // Include (potentially specialized) inherited members when testing ToMemberReference() + foreach (var member in typeDef.GetMembers()) { + var resolvedMember = member.ToMemberReference().Resolve(context); + if (!member.Equals(resolvedMember)) + throw new InvalidOperationException(); + } + } + } + } } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs index 2c06277934..4586652ff8 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs @@ -189,6 +189,7 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.AreEqual(1, constraint.TypeParameterCount); Assert.AreEqual(1, constraint.TypeArguments.Count); Assert.AreSame(m.TypeParameters[0], constraint.TypeArguments[0]); + Assert.AreSame(m.TypeParameters[0], m.Parameters[0].Type); } [Test] @@ -210,6 +211,7 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.AreEqual(1, constraint.TypeParameterCount); Assert.AreEqual(1, constraint.TypeArguments.Count); Assert.AreSame(m.TypeParameters[0], constraint.TypeArguments[0]); + Assert.AreSame(m.TypeParameters[0], m.Parameters[0].Type); } [Test] @@ -238,6 +240,38 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.AreEqual(m12, m2); } + [Test] + public void Specialized_GetIndex_ToTypeReference() + { + var method = compilation.FindType(typeof(GenericClass)).GetMethods(m => m.Name == "GetIndex").Single(); + Assert.AreSame(method.TypeParameters[0], method.Parameters[0].Type); + Assert.AreSame(method, method.TypeParameters[0].Owner); + Assert.IsInstanceOf(method); + Assert.AreEqual(0, ((SpecializedMethod)method).TypeArguments.Count); // the method itself is not specialized + var methodReference = method.ToMemberReference(); + var resolvedMethod = methodReference.Resolve(compilation.TypeResolveContext); + Assert.AreEqual(method, resolvedMethod); + } + + [Test] + public void Specialized_GetIndex_SpecializeWithIdentityHasNoEffect() + { + var genericClass = compilation.FindType(typeof(GenericClass)); + IType[] methodTypeArguments = { DummyTypeParameter.GetMethodTypeParameter(0) }; + var method = (SpecializedMethod)genericClass.GetMethods(methodTypeArguments, m => m.Name == "GetIndex").Single(); + // GenericClass.GetIndex() + Assert.AreSame(method, method.TypeParameters[0].Owner); + Assert.AreNotEqual(method.TypeParameters[0], method.TypeArguments[0]); + Assert.IsNull(((ITypeParameter)method.TypeArguments[0]).Owner); + // Now apply identity substitution: + var method2 = new SpecializedMethod(method, TypeParameterSubstitution.Identity); + Assert.AreSame(method2, method2.TypeParameters[0].Owner); + Assert.AreNotEqual(method2.TypeParameters[0], method2.TypeArguments[0]); + Assert.IsNull(((ITypeParameter)method2.TypeArguments[0]).Owner); + + Assert.AreEqual(method, method2); + } + [Test] public void GenericEnum() { @@ -726,7 +760,7 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.AreEqual(1.0, arg.ConstantValue); } - [Test, Ignore("Getting implicit interface implementations is not yet implemented.")] + [Test] public void ImplicitImplementationOfUnifiedMethods() { ITypeDefinition type = GetTypeDefinition(typeof(ImplementationOfUnifiedMethods)); diff --git a/ICSharpCode.NRefactory/Documentation/IdStringMemberReference.cs b/ICSharpCode.NRefactory/Documentation/IdStringMemberReference.cs index 25cd054e05..e7dadd8d73 100644 --- a/ICSharpCode.NRefactory/Documentation/IdStringMemberReference.cs +++ b/ICSharpCode.NRefactory/Documentation/IdStringMemberReference.cs @@ -55,6 +55,10 @@ namespace ICSharpCode.NRefactory.Documentation } } + public ITypeReference DeclaringTypeReference { + get { return declaringTypeReference; } + } + public IMember Resolve(ITypeResolveContext context) { IType declaringType = declaringTypeReference.Resolve(context); diff --git a/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs b/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs index 12b9616cd3..c8d5c76742 100644 --- a/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs +++ b/ICSharpCode.NRefactory/Documentation/IdStringProvider.cs @@ -184,6 +184,11 @@ namespace ICSharpCode.NRefactory.Documentation /// The ID string representing the member (with "M:", "F:", "P:" or "E:" prefix). /// A member reference that represents the ID string. /// The syntax of the ID string is invalid + /// + /// The member reference will look in first, + /// and if the member is not found there, + /// it will look in all other assemblies of the compilation. + /// public static IMemberReference ParseMemberIdString(string memberIdString) { if (memberIdString == null) diff --git a/ICSharpCode.NRefactory/TypeSystem/IEvent.cs b/ICSharpCode.NRefactory/TypeSystem/IEvent.cs index cc1726fac2..c8b6b9bf6c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IEvent.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IEvent.cs @@ -31,6 +31,18 @@ namespace ICSharpCode.NRefactory.TypeSystem IUnresolvedMethod AddAccessor { get; } IUnresolvedMethod RemoveAccessor { get; } IUnresolvedMethod InvokeAccessor { get; } + + /// + /// Resolves the member. + /// + /// + /// Context for looking up the member. The context must specify the current assembly. + /// A that specifies the current assembly is sufficient. + /// + /// + /// Returns the resolved member, or null if the member could not be found. + /// + new IEvent Resolve(ITypeResolveContext context); } public interface IEvent : IMember diff --git a/ICSharpCode.NRefactory/TypeSystem/IField.cs b/ICSharpCode.NRefactory/TypeSystem/IField.cs index 7be280e96c..85c59812ec 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IField.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IField.cs @@ -42,6 +42,18 @@ namespace ICSharpCode.NRefactory.TypeSystem bool IsConst { get; } IConstantValue ConstantValue { get; } + + /// + /// Resolves the member. + /// + /// + /// Context for looking up the member. The context must specify the current assembly. + /// A that specifies the current assembly is sufficient. + /// + /// + /// Returns the resolved member, or null if the member could not be found. + /// + new IField Resolve(ITypeResolveContext context); } /// diff --git a/ICSharpCode.NRefactory/TypeSystem/IMember.cs b/ICSharpCode.NRefactory/TypeSystem/IMember.cs index 8b5cb1e575..2cd4ef2b19 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IMember.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IMember.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// Method/field/property/event. /// - public interface IUnresolvedMember : IUnresolvedEntity + public interface IUnresolvedMember : IUnresolvedEntity, IMemberReference { /// /// Gets the return type of this member. @@ -62,14 +62,47 @@ namespace ICSharpCode.NRefactory.TypeSystem /// bool IsOverridable { get; } + /// + /// Resolves the member. + /// + /// + /// Context for looking up the member. The context must specify the current assembly. + /// A that specifies the current assembly is sufficient. + /// + /// + /// Returns the resolved member, or null if the member could not be found. + /// + new IMember Resolve(ITypeResolveContext context); + + /// + /// Creates the resolved member. + /// + /// + /// The language-specific context that includes the parent type definition. + /// + /// IMember CreateResolved(ITypeResolveContext context); } public interface IMemberReference { + /// + /// Gets the declaring type reference for the member. + /// + ITypeReference DeclaringTypeReference { get; } + /// /// Resolves the member. /// + /// + /// Context to use for resolving this member reference. + /// Which kind of context is required depends on the which kind of member reference this is; + /// please consult the documentation of the method that was used to create this member reference, + /// or that of the class implementing this method. + /// + /// + /// Returns the resolved member, or null if the member could not be found. + /// IMember Resolve(ITypeResolveContext context); } @@ -132,6 +165,10 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// Creates a member reference that can be used to rediscover this member in another compilation. /// + /// + /// If this member is specialized using open generic types, the resulting member reference will need to be looked up in an appropriate generic context. + /// Otherwise, the main resolve context of a compilation is sufficient. + /// IMemberReference ToMemberReference(); } } diff --git a/ICSharpCode.NRefactory/TypeSystem/IMethod.cs b/ICSharpCode.NRefactory/TypeSystem/IMethod.cs index 745aacfa67..c3fd1b7720 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IMethod.cs @@ -38,6 +38,18 @@ namespace ICSharpCode.NRefactory.TypeSystem bool IsPartialMethodDeclaration { get; } bool IsPartialMethodImplementation { get; } + + /// + /// Resolves the member. + /// + /// + /// Context for looking up the member. The context must specify the current assembly. + /// A that specifies the current assembly is sufficient. + /// + /// + /// Returns the resolved member, or null if the member could not be found. + /// + new IMethod Resolve(ITypeResolveContext context); } /// diff --git a/ICSharpCode.NRefactory/TypeSystem/IProperty.cs b/ICSharpCode.NRefactory/TypeSystem/IProperty.cs index e815638a3e..24e4bcbabd 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IProperty.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IProperty.cs @@ -32,6 +32,18 @@ namespace ICSharpCode.NRefactory.TypeSystem IUnresolvedMethod Setter { get; } bool IsIndexer { get; } + + /// + /// Resolves the member. + /// + /// + /// Context for looking up the member. The context must specify the current assembly. + /// A that specifies the current assembly is sufficient. + /// + /// + /// Returns the resolved member, or null if the member could not be found. + /// + new IProperty Resolve(ITypeResolveContext context); } /// diff --git a/ICSharpCode.NRefactory/TypeSystem/IType.cs b/ICSharpCode.NRefactory/TypeSystem/IType.cs index 4bd140dda0..2dd1981a11 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IType.cs @@ -107,8 +107,8 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Creates a type reference that can be used to look up a type equivalent to this type in another compilation. /// /// - /// If this type is open, the resulting type reference will need to be looked up in an appropriate generic context. - /// If this type is closed, the resulting type reference can be looked up in the main resolve context of another compilation. + /// If this type contains open generics, the resulting type reference will need to be looked up in an appropriate generic context. + /// Otherwise, the main resolve context of a compilation is sufficient. /// ITypeReference ToTypeReference(); diff --git a/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs index cbf7b650d7..116f4ee701 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs @@ -49,12 +49,34 @@ namespace ICSharpCode.NRefactory.TypeSystem bool? HasExtensionMethods { get; } /// - /// Creates a type resolve context for this part of the type definition. + /// Looks up the resolved type definition from the corresponding to this unresolved + /// type definition. + /// + /// + /// Context for looking up the type. The context must specify the current assembly. + /// A that specifies the current assembly is sufficient. + /// + /// + /// Returns the resolved type definition. + /// In case of an error, returns an instance. + /// Never returns null. + /// + new IType Resolve(ITypeResolveContext context); + + /// /// This method is used to add language-specific elements like the C# UsingScope /// to the type resolve context. /// /// The parent context (e.g. the parent assembly), /// including the parent type definition for inner classes. + /// + /// The parent context, modified to include language-specific elements (e.g. using scope) + /// associated with this type definition. + /// + /// + /// Use unresolvedTypeDef.CreateResolveContext(parentContext).WithTypeDefinition(typeDef) to + /// create the context for use within the type definition. + /// ITypeResolveContext CreateResolveContext(ITypeResolveContext parentContext); } diff --git a/ICSharpCode.NRefactory/TypeSystem/ITypeReference.cs b/ICSharpCode.NRefactory/TypeSystem/ITypeReference.cs index 4d58020b98..efad8e983c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ITypeReference.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ITypeReference.cs @@ -34,12 +34,18 @@ namespace ICSharpCode.NRefactory.TypeSystem // Keep this interface simple: I decided against having GetMethods/GetEvents etc. here, // so that the Resolve step is never hidden from the consumer. - // I decided against implementing IFreezable here: ITypeDefinition can be used as ITypeReference, + // I decided against implementing IFreezable here: IUnresolvedTypeDefinition can be used as ITypeReference, // but when freezing the reference, one wouldn't expect the definition to freeze. /// /// Resolves this type reference. /// + /// + /// Context to use for resolving this type reference. + /// Which kind of context is required depends on the which kind of type reference this is; + /// please consult the documentation of the method that was used to create this type reference, + /// or that of the class implementing this method. + /// /// /// Returns the resolved type. /// In case of an error, returns . diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs index 9d3248a572..7dd6af61de 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs @@ -319,7 +319,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public override string ToString() { - return this.ReflectionName; + return this.ReflectionName + " (owner=" + owner + ")"; } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedMember.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedMember.cs index 7caba61b28..c41290cef9 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedMember.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractUnresolvedMember.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq; using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -83,7 +84,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } /* - public IList InterfaceImplementations { + public IList ExplicitInterfaceImplementations { get { RareFields rareFields = (RareFields)this.rareFields; if (rareFields == null || rareFields.interfaceImplementations == null) { @@ -125,6 +126,131 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } + ITypeReference IMemberReference.DeclaringTypeReference { + get { return this.DeclaringTypeDefinition; } + } + + #region Resolve public abstract IMember CreateResolved(ITypeResolveContext context); + + public virtual IMember Resolve(ITypeResolveContext context) + { + ITypeReference interfaceTypeReference = null; + if (this.IsExplicitInterfaceImplementation && this.ExplicitInterfaceImplementations.Count == 1) + interfaceTypeReference = this.ExplicitInterfaceImplementations[0].DeclaringTypeReference; + return Resolve(ExtendContextForType(context, this.DeclaringTypeDefinition), this.EntityType, this.Name, interfaceTypeReference); + } + + protected static ITypeResolveContext ExtendContextForType(ITypeResolveContext assemblyContext, IUnresolvedTypeDefinition typeDef) + { + if (typeDef == null) + return assemblyContext; + ITypeResolveContext parentContext; + if (typeDef.DeclaringTypeDefinition != null) + parentContext = ExtendContextForType(assemblyContext, typeDef.DeclaringTypeDefinition); + else + parentContext = assemblyContext; + ITypeDefinition resolvedTypeDef = typeDef.Resolve(assemblyContext).GetDefinition(); + return typeDef.CreateResolveContext(parentContext).WithCurrentTypeDefinition(resolvedTypeDef); + } + + public static IMember Resolve(ITypeResolveContext context, + EntityType entityType, + string name, + ITypeReference explicitInterfaceTypeReference = null, + IList typeParameterNames = null, + IList parameterTypeReferences = null) + { + if (context.CurrentTypeDefinition == null) + return null; + if (parameterTypeReferences == null) + parameterTypeReferences = EmptyList.Instance; + if (typeParameterNames == null || typeParameterNames.Count == 0) { + // non-generic member + // In this case, we can simply resolve the parameter types in the given context + var parameterTypes = parameterTypeReferences.Resolve(context); + if (explicitInterfaceTypeReference == null) { + foreach (IMember member in context.CurrentTypeDefinition.Members) { + if (member.IsExplicitInterfaceImplementation) + continue; + if (IsNonGenericMatch(member, entityType, name, parameterTypes)) + return member; + } + } else { + IType explicitInterfaceType = explicitInterfaceTypeReference.Resolve(context); + foreach (IMember member in context.CurrentTypeDefinition.Members) { + if (!member.IsExplicitInterfaceImplementation) + continue; + if (member.ImplementedInterfaceMembers.Count != 1) + continue; + if (IsNonGenericMatch(member, entityType, name, parameterTypes)) { + if (explicitInterfaceType.Equals(member.ImplementedInterfaceMembers[0].DeclaringType)) + return member; + } + } + } + } else { + // generic member + // In this case, we must specify the correct context for resolving the parameter types + foreach (IMethod method in context.CurrentTypeDefinition.Methods) { + if (method.EntityType != entityType) + continue; + if (method.Name != name) + continue; + if (method.Parameters.Count != parameterTypeReferences.Count) + continue; + // Compare type parameter count and names: + if (!typeParameterNames.SequenceEqual(method.TypeParameters.Select(tp => tp.Name))) + continue; + // Once we know the type parameter names are fitting, we can resolve the + // type references in the context of the method: + var contextForMethod = context.WithCurrentMember(method); + var parameterTypes = parameterTypeReferences.Resolve(contextForMethod); + if (!IsParameterTypeMatch(method, parameterTypes)) + continue; + if (explicitInterfaceTypeReference == null) { + if (!method.IsExplicitInterfaceImplementation) + return method; + } else if (method.IsExplicitInterfaceImplementation && method.ImplementedInterfaceMembers.Count == 1) { + IType explicitInterfaceType = explicitInterfaceTypeReference.Resolve(contextForMethod); + if (explicitInterfaceTypeReference.Equals(method.ImplementedInterfaceMembers[0].DeclaringType)) + return method; + } + } + } + return null; + } + + static bool IsNonGenericMatch(IMember member, EntityType entityType, string name, IList parameterTypes) + { + if (member.EntityType != entityType) + return false; + if (member.Name != name) + return false; + IMethod method = member as IMethod; + if (method != null && method.TypeParameters.Count > 0) + return false; + return IsParameterTypeMatch(member, parameterTypes); + } + + static bool IsParameterTypeMatch(IMember member, IList parameterTypes) + { + IParameterizedMember parameterizedMember = member as IParameterizedMember; + if (parameterizedMember == null) { + return parameterTypes.Count == 0; + } else if (parameterTypes.Count == parameterizedMember.Parameters.Count) { + for (int i = 0; i < parameterTypes.Count; i++) { + IType type1 = parameterTypes[i]; + IType type2 = parameterizedMember.Parameters[i].Type; + if (!type1.Equals(type2)) { + return false; + } + } + return true; + } else { + return false; + } + } + #endregion } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMemberReference.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMemberReference.cs index 522f716425..6e26291fa6 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMemberReference.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMemberReference.cs @@ -26,6 +26,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// References an entity by its type and name. /// This class can be used to refer to fields, events, and parameterless properties. /// + /// + /// Resolving a DefaultMemberReference requires a context that provides enough information for resolving the declaring type reference + /// and the parameter types references. + /// [Serializable] public sealed class DefaultMemberReference : IMemberReference, ISupportsInterning { @@ -50,6 +54,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation this.parameterTypes = parameterTypes ?? EmptyList.Instance; } + public ITypeReference DeclaringTypeReference { + get { return typeReference; } + } + public IMember Resolve(ITypeResolveContext context) { IType type = typeReference.Resolve(context); @@ -66,14 +74,14 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation var resolvedParameterTypes = parameterTypes.Resolve(context); foreach (IMember member in members) { IParameterizedMember parameterizedMember = member as IParameterizedMember; - if (parameterTypes.Count == 0) { - if (parameterizedMember == null || parameterizedMember.Parameters.Count == 0) + if (parameterizedMember == null) { + if (parameterTypes.Count == 0) return member; } else if (parameterTypes.Count == parameterizedMember.Parameters.Count) { bool signatureMatches = true; for (int i = 0; i < parameterTypes.Count; i++) { - IType type1 = ParameterListComparer.Instance.NormalizeMethodTypeParameters(resolvedParameterTypes[i]); - IType type2 = ParameterListComparer.Instance.NormalizeMethodTypeParameters(parameterizedMember.Parameters[i].Type); + IType type1 = DummyTypeParameter.NormalizeAllTypeParameters(resolvedParameterTypes[i]); + IType type2 = DummyTypeParameter.NormalizeAllTypeParameters(parameterizedMember.Parameters[i].Type); if (!type1.Equals(type2)) { signatureMatches = false; break; diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs index 5c03765229..c12eb7c4df 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultParameter.cs @@ -118,7 +118,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation b.Append("params "); b.Append(parameter.Name); b.Append(':'); - b.Append(parameter.Type.ToString()); + b.Append(parameter.Type.ReflectionName); if (parameter.IsOptional) { b.Append(" = "); if (parameter.ConstantValue != null) diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs index a15e83d26a..e53cb1422e 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs @@ -260,8 +260,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation this.unresolvedTypeDict = unresolved.GetTypeDictionary(compilation.NameComparer); this.rootNamespace = new NS(this, unresolved.GetUnresolvedRootNamespace(compilation.NameComparer), null); this.context = new SimpleTypeResolveContext(this); - this.AssemblyAttributes = unresolved.AssemblyAttributes.ToList().CreateResolvedAttributes(context); - this.ModuleAttributes = unresolved.ModuleAttributes.ToList().CreateResolvedAttributes(context); + this.AssemblyAttributes = unresolved.AssemblyAttributes.CreateResolvedAttributes(context); + this.ModuleAttributes = unresolved.ModuleAttributes.CreateResolvedAttributes(context); } public IUnresolvedAssembly UnresolvedAssembly { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedEvent.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedEvent.cs index 2e502aa39c..64d940f9fe 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedEvent.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedEvent.cs @@ -98,5 +98,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation { return new DefaultResolvedEvent(this, context); } + + IEvent IUnresolvedEvent.Resolve(ITypeResolveContext context) + { + return (IEvent)Resolve(context); + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedField.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedField.cs index 0b5a989c8a..850ffe3937 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedField.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedField.cs @@ -86,5 +86,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation { return new DefaultResolvedField(this, context); } + + IField IUnresolvedField.Resolve(ITypeResolveContext context) + { + return (IField)Resolve(context); + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedMethod.cs index a5a2728d74..f06815297a 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedMethod.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -147,6 +148,22 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return new DefaultResolvedMethod(this, context); } + public override IMember Resolve(ITypeResolveContext context) + { + ITypeReference interfaceTypeReference = null; + if (this.IsExplicitInterfaceImplementation && this.ExplicitInterfaceImplementations.Count == 1) + interfaceTypeReference = this.ExplicitInterfaceImplementations[0].DeclaringTypeReference; + return Resolve(ExtendContextForType(context, this.DeclaringTypeDefinition), + this.EntityType, this.Name, interfaceTypeReference, + this.TypeParameters.Select(tp => tp.Name).ToList(), + this.Parameters.Select(p => p.Type).ToList()); + } + + IMethod IUnresolvedMethod.Resolve(ITypeResolveContext context) + { + return (IMethod)Resolve(context); + } + public static DefaultUnresolvedMethod CreateDefaultConstructor(IUnresolvedTypeDefinition typeDefinition) { if (typeDefinition == null) diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedProperty.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedProperty.cs index 3f7765b923..aa86d31145 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedProperty.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedProperty.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { @@ -100,5 +101,20 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation { return new DefaultResolvedProperty(this, context); } + + public override IMember Resolve(ITypeResolveContext context) + { + ITypeReference interfaceTypeReference = null; + if (this.IsExplicitInterfaceImplementation && this.ExplicitInterfaceImplementations.Count == 1) + interfaceTypeReference = this.ExplicitInterfaceImplementations[0].DeclaringTypeReference; + return Resolve(ExtendContextForType(context, this.DeclaringTypeDefinition), + this.EntityType, this.Name, interfaceTypeReference, + parameterTypeReferences: this.Parameters.Select(p => p.Type).ToList()); + } + + IProperty IUnresolvedProperty.Resolve(ITypeResolveContext context) + { + return (IProperty)Resolve(context); + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DummyTypeParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DummyTypeParameter.cs index c05a9c902d..d6658dfe39 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DummyTypeParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DummyTypeParameter.cs @@ -61,6 +61,62 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return tps[index]; } + sealed class NormalizeMethodTypeParametersVisitor : TypeVisitor + { + public override IType VisitTypeParameter(ITypeParameter type) + { + if (type.OwnerType == EntityType.Method) { + return DummyTypeParameter.GetMethodTypeParameter(type.Index); + } else { + return base.VisitTypeParameter(type); + } + } + } + sealed class NormalizeClassTypeParametersVisitor : TypeVisitor + { + public override IType VisitTypeParameter(ITypeParameter type) + { + if (type.OwnerType == EntityType.TypeDefinition) { + return DummyTypeParameter.GetClassTypeParameter(type.Index); + } else { + return base.VisitTypeParameter(type); + } + } + } + + static readonly NormalizeMethodTypeParametersVisitor normalizeMethodTypeParameters = new NormalizeMethodTypeParametersVisitor(); + static readonly NormalizeClassTypeParametersVisitor normalizeClassTypeParameters = new NormalizeClassTypeParametersVisitor(); + + /// + /// Replaces all occurrences of method type parameters in the given type + /// by normalized type parameters. This allows comparing parameter types from different + /// generic methods. + /// + public static IType NormalizeMethodTypeParameters(IType type) + { + return type.AcceptVisitor(normalizeMethodTypeParameters); + } + + /// + /// Replaces all occurrences of class type parameters in the given type + /// by normalized type parameters. This allows comparing parameter types from different + /// generic methods. + /// + public static IType NormalizeClassTypeParameters(IType type) + { + return type.AcceptVisitor(normalizeClassTypeParameters); + } + + /// + /// Replaces all occurrences of class and method type parameters in the given type + /// by normalized type parameters. This allows comparing parameter types from different + /// generic methods. + /// + public static IType NormalizeAllTypeParameters(IType type) + { + return type.AcceptVisitor(normalizeClassTypeParameters).AcceptVisitor(normalizeMethodTypeParameters); + } + readonly EntityType ownerType; readonly int index; @@ -95,6 +151,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return new TypeParameterReference(ownerType, index); } + public override IType AcceptVisitor(TypeVisitor visitor) + { + return visitor.VisitTypeParameter(this); + } + public int Index { get { return index; } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/ExplicitInterfaceImplementationMemberReference.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/ExplicitInterfaceImplementationMemberReference.cs index 09d47b8b6a..2da8ffe870 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/ExplicitInterfaceImplementationMemberReference.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/ExplicitInterfaceImplementationMemberReference.cs @@ -24,6 +24,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// /// References a member that is an explicit interface implementation. /// + /// + /// Resolving an ExplicitInterfaceImplementationMemberReference requires a context + /// that provides enough information for resolving the declaring type reference + /// and the interface member reference. + /// [Serializable] public sealed class ExplicitInterfaceImplementationMemberReference : IMemberReference { @@ -40,6 +45,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation this.interfaceMemberReference = interfaceMemberReference; } + public ITypeReference DeclaringTypeReference { + get { return typeReference; } + } + public IMember Resolve(ITypeResolveContext context) { IMember interfaceMember = interfaceMemberReference.Resolve(context); diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs index bab18349c8..ed879624d0 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation : base(eventDefinition) { AddSubstitution(substitution); - this.eventDefinition = (IEvent)base.MemberDefinition; + this.eventDefinition = (IEvent)base.baseMember; } public bool CanAdd { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs index d79d1b3ab2..58f52cd625 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation : base(fieldDefinition) { AddSubstitution(substitution); - this.fieldDefinition = (IField)base.MemberDefinition; + this.fieldDefinition = (IField)base.baseMember; } public bool IsReadOnly { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs index b616783ff4..53fd6d350c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs @@ -32,7 +32,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// public abstract class SpecializedMember : IMember { - readonly IMember memberDefinition; + protected readonly IMember baseMember; TypeParameterSubstitution substitution; IType declaringType; @@ -45,10 +45,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation SpecializedMember sm = memberDefinition as SpecializedMember; if (sm != null) { - this.memberDefinition = sm.memberDefinition; + this.baseMember = sm.baseMember; this.substitution = sm.substitution; } else { - this.memberDefinition = memberDefinition; + this.baseMember = memberDefinition; this.substitution = TypeParameterSubstitution.Identity; } } @@ -80,15 +80,15 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } - public IMemberReference ToMemberReference() + public virtual IMemberReference ToMemberReference() { return new SpecializingMemberReference( - memberDefinition.ToMemberReference(), + baseMember.ToMemberReference(), ToTypeReference(substitution.ClassTypeArguments), - ToTypeReference(substitution.MethodTypeArguments)); + null); } - static IList ToTypeReference(IList typeArguments) + internal static IList ToTypeReference(IList typeArguments) { if (typeArguments == null) return null; @@ -119,7 +119,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation var result = LazyInit.VolatileRead(ref this.declaringType); if (result != null) return result; - IType definitionDeclaringType = memberDefinition.DeclaringType; + IType definitionDeclaringType = baseMember.DeclaringType; ITypeDefinition definitionDeclaringTypeDef = definitionDeclaringType as ITypeDefinition; if (definitionDeclaringTypeDef != null && definitionDeclaringType.TypeParameterCount > 0) { if (substitution.ClassTypeArguments != null && substitution.ClassTypeArguments.Count == definitionDeclaringType.TypeParameterCount) { @@ -144,11 +144,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } public IMember MemberDefinition { - get { return memberDefinition.MemberDefinition; } + get { return baseMember.MemberDefinition; } } public IUnresolvedMember UnresolvedMember { - get { return memberDefinition.UnresolvedMember; } + get { return baseMember.UnresolvedMember; } } public IType ReturnType { @@ -157,7 +157,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation if (result != null) return result; else - return LazyInit.GetOrSet(ref this.returnType, memberDefinition.ReturnType.AcceptVisitor(substitution)); + return LazyInit.GetOrSet(ref this.returnType, baseMember.ReturnType.AcceptVisitor(substitution)); } protected set { // This setter is used for LiftedUserDefinedOperator, a special case of specialized member @@ -170,35 +170,35 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } public bool IsVirtual { - get { return memberDefinition.IsVirtual; } + get { return baseMember.IsVirtual; } } public bool IsOverride { - get { return memberDefinition.IsOverride; } + get { return baseMember.IsOverride; } } public bool IsOverridable { - get { return memberDefinition.IsOverridable; } + get { return baseMember.IsOverridable; } } public EntityType EntityType { - get { return memberDefinition.EntityType; } + get { return baseMember.EntityType; } } public DomRegion Region { - get { return memberDefinition.Region; } + get { return baseMember.Region; } } public DomRegion BodyRegion { - get { return memberDefinition.BodyRegion; } + get { return baseMember.BodyRegion; } } public ITypeDefinition DeclaringTypeDefinition { - get { return memberDefinition.DeclaringTypeDefinition; } + get { return baseMember.DeclaringTypeDefinition; } } public IList Attributes { - get { return memberDefinition.Attributes; } + get { return baseMember.Attributes; } } IList implementedInterfaceMembers; @@ -211,7 +211,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation IList FindImplementedInterfaceMembers() { - var definitionImplementations = memberDefinition.ImplementedInterfaceMembers; + var definitionImplementations = baseMember.ImplementedInterfaceMembers; IMember[] result = new IMember[definitionImplementations.Count]; for (int i = 0; i < result.Length; i++) { result[i] = SpecializedMember.Create(definitionImplementations[i], substitution); @@ -220,83 +220,83 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } public bool IsExplicitInterfaceImplementation { - get { return memberDefinition.IsExplicitInterfaceImplementation; } + get { return baseMember.IsExplicitInterfaceImplementation; } } public DocumentationComment Documentation { - get { return memberDefinition.Documentation; } + get { return baseMember.Documentation; } } public Accessibility Accessibility { - get { return memberDefinition.Accessibility; } + get { return baseMember.Accessibility; } } public bool IsStatic { - get { return memberDefinition.IsStatic; } + get { return baseMember.IsStatic; } } public bool IsAbstract { - get { return memberDefinition.IsAbstract; } + get { return baseMember.IsAbstract; } } public bool IsSealed { - get { return memberDefinition.IsSealed; } + get { return baseMember.IsSealed; } } public bool IsShadowing { - get { return memberDefinition.IsShadowing; } + get { return baseMember.IsShadowing; } } public bool IsSynthetic { - get { return memberDefinition.IsSynthetic; } + get { return baseMember.IsSynthetic; } } public bool IsPrivate { - get { return memberDefinition.IsPrivate; } + get { return baseMember.IsPrivate; } } public bool IsPublic { - get { return memberDefinition.IsPublic; } + get { return baseMember.IsPublic; } } public bool IsProtected { - get { return memberDefinition.IsProtected; } + get { return baseMember.IsProtected; } } public bool IsInternal { - get { return memberDefinition.IsInternal; } + get { return baseMember.IsInternal; } } public bool IsProtectedOrInternal { - get { return memberDefinition.IsProtectedOrInternal; } + get { return baseMember.IsProtectedOrInternal; } } public bool IsProtectedAndInternal { - get { return memberDefinition.IsProtectedAndInternal; } + get { return baseMember.IsProtectedAndInternal; } } public string FullName { - get { return memberDefinition.FullName; } + get { return baseMember.FullName; } } public string Name { - get { return memberDefinition.Name; } + get { return baseMember.Name; } } public string Namespace { - get { return memberDefinition.Namespace; } + get { return baseMember.Namespace; } } public string ReflectionName { - get { return memberDefinition.ReflectionName; } + get { return baseMember.ReflectionName; } } public ICompilation Compilation { - get { return memberDefinition.Compilation; } + get { return baseMember.Compilation; } } public IAssembly ParentAssembly { - get { return memberDefinition.ParentAssembly; } + get { return baseMember.ParentAssembly; } } public override bool Equals(object obj) @@ -304,13 +304,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation SpecializedMember other = obj as SpecializedMember; if (other == null) return false; - return this.memberDefinition.Equals(other.memberDefinition) && this.substitution.Equals(other.substitution); + return this.baseMember.Equals(other.baseMember) && this.substitution.Equals(other.substitution); } public override int GetHashCode() { unchecked { - return 1000000007 * memberDefinition.GetHashCode() + 1000000009 * substitution.GetHashCode(); + return 1000000007 * baseMember.GetHashCode() + 1000000009 * substitution.GetHashCode(); } } @@ -358,7 +358,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation protected IList CreateParameters(TypeVisitor substitution) { - var paramDefs = ((IParameterizedMember)this.MemberDefinition).Parameters; + var paramDefs = ((IParameterizedMember)this.baseMember).Parameters; if (paramDefs.Count == 0) { return EmptyList.Instance; } else { @@ -380,7 +380,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation StringBuilder b = new StringBuilder("["); b.Append(GetType().Name); b.Append(' '); - b.Append(this.DeclaringType.ToString()); + b.Append(this.DeclaringType.ReflectionName); b.Append('.'); b.Append(this.Name); b.Append('('); @@ -389,7 +389,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation b.Append(this.Parameters[i].ToString()); } b.Append("):"); - b.Append(this.ReturnType.ToString()); + b.Append(this.ReturnType.ReflectionName); b.Append(']'); return b.ToString(); } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs index 3e8a0e920d..ac3fdb3d8c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs @@ -33,28 +33,46 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation { readonly IMethod methodDefinition; readonly ITypeParameter[] specializedTypeParameters; + readonly bool genericMethodIsSpecialized; + readonly TypeParameterSubstitution substitutionWithoutSpecializedTypeParameters; public SpecializedMethod(IMethod methodDefinition, TypeParameterSubstitution substitution) : base(methodDefinition) { + SpecializedMethod specializedMethodDefinition = methodDefinition as SpecializedMethod; + if (specializedMethodDefinition != null) + this.genericMethodIsSpecialized = specializedMethodDefinition.genericMethodIsSpecialized; + // The base ctor might have unpacked a SpecializedMember // (in case we are specializing an already-specialized method) - methodDefinition = (IMethod)base.MemberDefinition; + methodDefinition = (IMethod)base.baseMember; this.methodDefinition = methodDefinition; - if (methodDefinition.TypeParameters.Any(ConstraintNeedsSpecialization)) { - // The method is generic, and we need to specialize the type parameters + if (methodDefinition.TypeParameters.Count > 0) { + // The method is generic, so we need to specialize the type parameters + // (for specializing the constraints, and also to set the correct Owner) specializedTypeParameters = new ITypeParameter[methodDefinition.TypeParameters.Count]; for (int i = 0; i < specializedTypeParameters.Length; i++) { - ITypeParameter tp = methodDefinition.TypeParameters[i]; - if (ConstraintNeedsSpecialization(tp)) - tp = new SpecializedTypeParameter(tp, this); - specializedTypeParameters[i] = tp; + specializedTypeParameters[i] = new SpecializedTypeParameter(methodDefinition.TypeParameters[i], this); + } + if (!genericMethodIsSpecialized) { + // Add substitution that replaces the base method's type parameters with our specialized version + // but do this only if the type parameters on the baseMember have not already been substituted + substitutionWithoutSpecializedTypeParameters = this.Substitution; + AddSubstitution(new TypeParameterSubstitution(null, specializedTypeParameters)); } - // add substitution that replaces the base method's type parameters with our specialized version - AddSubstitution(new TypeParameterSubstitution(null, specializedTypeParameters)); } // Add the main substitution after the method type parameter specialization. AddSubstitution(substitution); + if (substitutionWithoutSpecializedTypeParameters != null) { + // If we already have a substitution without specialized type parameters, update that: + substitutionWithoutSpecializedTypeParameters = TypeParameterSubstitution.Compose(substitution, substitutionWithoutSpecializedTypeParameters); + } else { + // Otherwise just use the whole substitution, as that doesn't contain specialized type parameters + // in this case. + substitutionWithoutSpecializedTypeParameters = this.Substitution; + } + if (substitution != null && substitution.MethodTypeArguments != null && methodDefinition.TypeParameters.Count > 0) + this.genericMethodIsSpecialized = true; if (specializedTypeParameters != null) { // Set the substitution on the type parameters to the final composed substitution foreach (var tp in specializedTypeParameters.OfType()) { @@ -64,30 +82,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } - static bool ConstraintNeedsSpecialization(ITypeParameter tp) - { - // TODO: can we avoid specialization if a type parameter doesn't have any constraints? - return true; - } - - internal static TypeVisitor GetSubstitution(IType declaringType, IList typeArguments) - { - ParameterizedType pt = declaringType as ParameterizedType; - if (pt != null) - return pt.GetSubstitution(typeArguments); - else if (typeArguments != null) - return new TypeParameterSubstitution(null, typeArguments); - else - return null; - } - /// /// Gets the type arguments passed to this method. /// If only the type parameters for the class were specified and the generic method /// itself is not specialized yet, this property will return an empty list. /// public IList TypeArguments { - get { return this.Substitution.MethodTypeArguments ?? EmptyList.Instance; } + get { return genericMethodIsSpecialized ? this.Substitution.MethodTypeArguments : EmptyList.Instance; } } public IList Parts { @@ -120,19 +121,57 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation get { return methodDefinition.IsOperator; } } + public override IMemberReference ToMemberReference() + { + // Pass the MethodTypeArguments to the SpecializingMemberReference only if + // the generic method itself is specialized, not if the generic method is only + // specialized with class type arguments. + + // This is necessary due to this part of the ToMemberReference() contract: + // If this member is specialized using open generic types, the resulting member reference will need to be looked up in an appropriate generic context. + // Otherwise, the main resolve context of a compilation is sufficient. + // -> + // This means that if the method itself isn't specialized, + // we must not include TypeParameterReferences for the specialized type parameters + // in the resulting member reference. + if (genericMethodIsSpecialized) { + return new SpecializingMemberReference( + baseMember.ToMemberReference(), + ToTypeReference(base.Substitution.ClassTypeArguments), + ToTypeReference(base.Substitution.MethodTypeArguments)); + } else { + return base.ToMemberReference(); + } + } + + public override bool Equals(object obj) + { + SpecializedMethod other = obj as SpecializedMethod; + if (other == null) + return false; + return this.baseMember.Equals(other.baseMember) && this.substitutionWithoutSpecializedTypeParameters.Equals(other.substitutionWithoutSpecializedTypeParameters); + } + + public override int GetHashCode() + { + unchecked { + return 1000000013 * baseMember.GetHashCode() + 1000000009 * substitutionWithoutSpecializedTypeParameters.GetHashCode(); + } + } + public override string ToString() { StringBuilder b = new StringBuilder("["); b.Append(GetType().Name); b.Append(' '); - b.Append(this.DeclaringType.ToString()); + b.Append(this.DeclaringType.ReflectionName); b.Append('.'); b.Append(this.Name); if (this.TypeArguments.Count > 0) { b.Append('['); for (int i = 0; i < this.TypeArguments.Count; i++) { if (i > 0) b.Append(", "); - b.Append(this.TypeArguments[i].ToString()); + b.Append(this.TypeArguments[i].ReflectionName); } b.Append(']'); } @@ -142,7 +181,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation b.Append(this.Parameters[i].ToString()); } b.Append("):"); - b.Append(this.ReturnType.ToString()); + b.Append(this.ReturnType.ReflectionName); b.Append(']'); return b.ToString(); } @@ -169,6 +208,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public override bool Equals(IType other) { + // Compare the owner, not the substitution, because the substitution may contain this specialized type parameter recursively SpecializedTypeParameter o = other as SpecializedTypeParameter; return o != null && baseTp.Equals(o.baseTp) && this.Owner.Equals(o.Owner); } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs index c35c9e1a68..d0e23b5a27 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation : base(propertyDefinition) { AddSubstitution(substitution); - this.propertyDefinition = (IProperty)base.MemberDefinition; + this.propertyDefinition = (IProperty)base.baseMember; } public bool CanGet { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializingMemberReference.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializingMemberReference.cs index d560fa4f85..df93ab08c8 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializingMemberReference.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializingMemberReference.cs @@ -48,5 +48,14 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation ) ); } + + public ITypeReference DeclaringTypeReference { + get { + if (classTypeArgumentReferences != null) + return new ParameterizedTypeReference(memberDefinitionReference.DeclaringTypeReference, classTypeArgumentReferences); + else + return memberDefinitionReference.DeclaringTypeReference; + } + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs index 6571c6f3d6..92dab013a7 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs @@ -54,12 +54,16 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// /// Gets the list of class type arguments. - /// Returns null if this substitution keeps class type parameter unmodified. + /// Returns null if this substitution keeps class type parameters unmodified. /// public IList ClassTypeArguments { get { return classTypeArguments; } } + /// + /// Gets the list of method type arguments. + /// Returns null if this substitution keeps method type parameters unmodified. + /// public IList MethodTypeArguments { get { return methodTypeArguments; } } @@ -170,7 +174,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation b.Append('`'); b.Append(i); b.Append(" -> "); - b.Append(classTypeArguments[i]); + b.Append(classTypeArguments[i].ReflectionName); } } if (methodTypeArguments != null) { @@ -179,7 +183,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation b.Append("``"); b.Append(i); b.Append(" -> "); - b.Append(methodTypeArguments[i]); + b.Append(methodTypeArguments[i].ReflectionName); } } b.Append(']'); diff --git a/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs b/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs index 2af27927fd..5f1708623e 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ParameterListComparer.cs @@ -29,28 +29,12 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// 'ref int' and 'out int' are considered to be equal. /// "Method{T}(T a)" and "Method{S}(S b)" are also considered equal. + /// However, "Method(T a)" and "Method(S b)" are not considered equal when the type parameters T and S belong to classes. /// public sealed class ParameterListComparer : IEqualityComparer> { public static readonly ParameterListComparer Instance = new 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 NormalizeMethodTypeParametersVisitor : TypeVisitor - { - public override IType VisitTypeParameter(ITypeParameter type) - { - if (type.OwnerType == EntityType.Method) { - return DummyTypeParameter.GetMethodTypeParameter(type.Index); - } else { - return base.VisitTypeParameter(type); - } - } - } - - readonly NormalizeMethodTypeParametersVisitor normalization = new NormalizeMethodTypeParametersVisitor(); - /// /// Replaces all occurrences of method type parameters in the given type /// by normalized type parameters. This allows comparing parameter types from different @@ -58,7 +42,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// public IType NormalizeMethodTypeParameters(IType type) { - return type.AcceptVisitor(normalization); + return DummyTypeParameter.NormalizeMethodTypeParameters(type); } public bool Equals(IList x, IList y) @@ -74,8 +58,12 @@ namespace ICSharpCode.NRefactory.TypeSystem continue; if (a == null || b == null) return false; - IType aType = a.Type.AcceptVisitor(normalization); - IType bType = b.Type.AcceptVisitor(normalization); + + // 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. + IType aType = DummyTypeParameter.NormalizeMethodTypeParameters(a.Type); + IType bType = DummyTypeParameter.NormalizeMethodTypeParameters(b.Type); if (!aType.Equals(bType)) return false; @@ -89,7 +77,7 @@ namespace ICSharpCode.NRefactory.TypeSystem unchecked { foreach (IParameter p in obj) { hashCode *= 27; - IType type = p.Type.AcceptVisitor(normalization); + IType type = DummyTypeParameter.NormalizeMethodTypeParameters(p.Type); hashCode += type.GetHashCode(); } }