diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeSystemConvertVisitorTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeSystemConvertVisitorTests.cs index 7f5922f1ae..a95b19bbcd 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeSystemConvertVisitorTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeSystemConvertVisitorTests.cs @@ -61,63 +61,6 @@ namespace ICSharpCode.NRefactory.CSharp.Parser Assert.AreEqual(compilation.FindType(KnownTypeCode.Array), typeRef.Resolve(compilation.TypeResolveContext)); } - [Test] - public void ExplicitDisposableImplementation() - { - ITypeDefinition disposable = GetTypeDefinition(typeof(NRefactory.TypeSystem.TestCase.ExplicitDisposableImplementation)); - IMethod method = disposable.Methods.Single(m => m.Name == "Dispose"); - Assert.IsTrue(method.IsExplicitInterfaceImplementation); - Assert.AreEqual("System.IDisposable.Dispose", method.ImplementedInterfaceMembers.Single().FullName); - } - - [Test] - public void ExplicitGenericInterfaceImplementation() - { - ITypeDefinition impl = GetTypeDefinition(typeof(NRefactory.TypeSystem.TestCase.ExplicitGenericInterfaceImplementation)); - IType genericInterfaceOfString = compilation.FindType(typeof(IGenericInterface)); - IMethod implMethod1 = impl.Methods.Single(m => m.Name == "Test" && !m.Parameters[1].IsRef); - IMethod implMethod2 = impl.Methods.Single(m => m.Name == "Test" && m.Parameters[1].IsRef); - Assert.IsTrue(implMethod1.IsExplicitInterfaceImplementation); - Assert.IsTrue(implMethod2.IsExplicitInterfaceImplementation); - - IMethod interfaceMethod1 = (IMethod)implMethod1.ImplementedInterfaceMembers.Single(); - Assert.AreEqual(genericInterfaceOfString, interfaceMethod1.DeclaringType); - Assert.IsTrue(!interfaceMethod1.Parameters[1].IsRef); - - IMethod interfaceMethod2 = (IMethod)implMethod2.ImplementedInterfaceMembers.Single(); - Assert.AreEqual(genericInterfaceOfString, interfaceMethod2.DeclaringType); - Assert.IsTrue(interfaceMethod2.Parameters[1].IsRef); - } - - [Test] - public void ExplicitImplementationOfUnifiedMethods() - { - IType type = compilation.FindType(typeof(ExplicitGenericInterfaceImplementationWithUnifiableMethods)); - Assert.AreEqual(2, type.GetMethods(m => m.IsExplicitInterfaceImplementation).Count()); - foreach (IMethod method in type.GetMethods(m => m.IsExplicitInterfaceImplementation)) { - Assert.AreEqual(1, method.ImplementedInterfaceMembers.Count, method.ToString()); - Assert.AreEqual("System.Int32", method.Parameters.Single().Type.ReflectionName); - IMethod interfaceMethod = (IMethod)method.ImplementedInterfaceMembers.Single(); - Assert.AreEqual("System.Int32", interfaceMethod.Parameters.Single().Type.ReflectionName); - var genericParamType = ((IMethod)method.MemberDefinition).Parameters.Single().Type; - var interfaceGenericParamType = ((IMethod)interfaceMethod.MemberDefinition).Parameters.Single().Type; - Assert.AreEqual(TypeKind.TypeParameter, genericParamType.Kind); - Assert.AreEqual(TypeKind.TypeParameter, interfaceGenericParamType.Kind); - Assert.AreEqual(genericParamType.ReflectionName, interfaceGenericParamType.ReflectionName); - } - } - - [Test] - public void ExplicitImplementationOfUnifiedMethods_ToMemberReference() - { - IType type = compilation.FindType(typeof(ExplicitGenericInterfaceImplementationWithUnifiableMethods)); - Assert.AreEqual(2, type.GetMethods(m => m.IsExplicitInterfaceImplementation).Count()); - foreach (IMethod method in type.GetMethods(m => m.IsExplicitInterfaceImplementation)) { - IMethod resolvedMethod = (IMethod)method.ToMemberReference().Resolve(compilation.TypeResolveContext); - Assert.AreEqual(method, resolvedMethod); - } - } - [Test] public void PartialMethodWithImplementation() { diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs index f15071a7f4..e5c9b3f502 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs @@ -249,6 +249,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase public int Prop { get; set; } } + public class ClassThatImplementsPropertyExplicitly : IInterfaceWithProperty { + int IInterfaceWithProperty.Prop { get; set; } + } + public interface IInterfaceWithIndexers { int this[int x] { get; set; } int this[string x] { get; set; } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs index 44b63ca41e..b8589447b4 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs @@ -894,5 +894,72 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.That(type.Properties.All(p => p.Getter.ImplementedInterfaceMembers.Count == 1)); Assert.That(type.Properties.All(p => p.Setter.ImplementedInterfaceMembers.Count == 1)); } + + [Test] + public void ExplicitDisposableImplementation() + { + ITypeDefinition disposable = GetTypeDefinition(typeof(NRefactory.TypeSystem.TestCase.ExplicitDisposableImplementation)); + IMethod method = disposable.Methods.Single(m => !m.IsConstructor); + Assert.IsTrue(method.IsExplicitInterfaceImplementation); + Assert.AreEqual("System.IDisposable.Dispose", method.ImplementedInterfaceMembers.Single().FullName); + } + + [Test] + public void ExplicitImplementationOfUnifiedMethods() + { + IType type = compilation.FindType(typeof(ExplicitGenericInterfaceImplementationWithUnifiableMethods)); + Assert.AreEqual(2, type.GetMethods(m => m.IsExplicitInterfaceImplementation).Count()); + foreach (IMethod method in type.GetMethods(m => m.IsExplicitInterfaceImplementation)) { + Assert.AreEqual(1, method.ImplementedInterfaceMembers.Count, method.ToString()); + Assert.AreEqual("System.Int32", method.Parameters.Single().Type.ReflectionName); + IMethod interfaceMethod = (IMethod)method.ImplementedInterfaceMembers.Single(); + Assert.AreEqual("System.Int32", interfaceMethod.Parameters.Single().Type.ReflectionName); + var genericParamType = ((IMethod)method.MemberDefinition).Parameters.Single().Type; + var interfaceGenericParamType = ((IMethod)interfaceMethod.MemberDefinition).Parameters.Single().Type; + Assert.AreEqual(TypeKind.TypeParameter, genericParamType.Kind); + Assert.AreEqual(TypeKind.TypeParameter, interfaceGenericParamType.Kind); + Assert.AreEqual(genericParamType.ReflectionName, interfaceGenericParamType.ReflectionName); + } + } + + [Test] + public void ExplicitImplementationOfUnifiedMethods_ToMemberReference() + { + IType type = compilation.FindType(typeof(ExplicitGenericInterfaceImplementationWithUnifiableMethods)); + Assert.AreEqual(2, type.GetMethods(m => m.IsExplicitInterfaceImplementation).Count()); + foreach (IMethod method in type.GetMethods(m => m.IsExplicitInterfaceImplementation)) { + IMethod resolvedMethod = (IMethod)method.ToMemberReference().Resolve(compilation.TypeResolveContext); + Assert.AreEqual(method, resolvedMethod); + } + } + + [Test] + public void ExplicitGenericInterfaceImplementation() + { + ITypeDefinition impl = GetTypeDefinition(typeof(ExplicitGenericInterfaceImplementation)); + IType genericInterfaceOfString = compilation.FindType(typeof(IGenericInterface)); + IMethod implMethod1 = impl.Methods.Single(m => !m.IsConstructor && !m.Parameters[1].IsRef); + IMethod implMethod2 = impl.Methods.Single(m => !m.IsConstructor && m.Parameters[1].IsRef); + Assert.IsTrue(implMethod1.IsExplicitInterfaceImplementation); + Assert.IsTrue(implMethod2.IsExplicitInterfaceImplementation); + + IMethod interfaceMethod1 = (IMethod)implMethod1.ImplementedInterfaceMembers.Single(); + Assert.AreEqual(genericInterfaceOfString, interfaceMethod1.DeclaringType); + Assert.IsTrue(!interfaceMethod1.Parameters[1].IsRef); + + IMethod interfaceMethod2 = (IMethod)implMethod2.ImplementedInterfaceMembers.Single(); + Assert.AreEqual(genericInterfaceOfString, interfaceMethod2.DeclaringType); + Assert.IsTrue(interfaceMethod2.Parameters[1].IsRef); + } + + [Test] + public void ExplicitlyImplementedPropertiesShouldBeReportedAsBeingImplemented() { + ITypeDefinition type = GetTypeDefinition(typeof(ClassThatImplementsPropertyExplicitly)); + var prop = type.Properties.Single(); + Assert.That(prop.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.NRefactory.TypeSystem.TestCase.IInterfaceWithProperty.Prop" })); + Assert.That(prop.Getter.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.NRefactory.TypeSystem.TestCase.IInterfaceWithProperty.get_Prop" })); + Assert.That(prop.Setter.ImplementedInterfaceMembers.Select(p => p.ReflectionName).ToList(), Is.EqualTo(new[] { "ICSharpCode.NRefactory.TypeSystem.TestCase.IInterfaceWithProperty.set_Prop" })); + } + } } diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index 15530707a5..7251ebce7f 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -88,6 +88,15 @@ namespace ICSharpCode.NRefactory.TypeSystem // Enable interning by default. this.InterningProvider = new SimpleInterningProvider(); } + + void ReadExplicitInterfaceImplementation(DefaultUnresolvedMethod m, MethodDefinition method) { + if (method.Name.Contains(".") && method.HasOverrides) { + m.IsExplicitInterfaceImplementation = true; + foreach (var or in method.Overrides) { + m.ExplicitInterfaceImplementations.Add(new DefaultMemberReference(EntityType.Method, ReadTypeReference(or.DeclaringType), or.Name, or.GenericParameters.Count, m.Parameters.Select(p => p.Type).ToList(), false)); + } + } + } #region Load From AssemblyDefinition /// @@ -1650,7 +1659,7 @@ namespace ICSharpCode.NRefactory.TypeSystem return ReadMethod(method, parentType, null, methodType); } - IUnresolvedMethod ReadMethod(MethodDefinition method, IUnresolvedTypeDefinition parentType, IUnresolvedMember accessorOwner, EntityType methodType = EntityType.Method) + IUnresolvedMethod ReadMethod(MethodDefinition method, IUnresolvedTypeDefinition parentType, IUnresolvedMember accessorOwner, EntityType methodType = EntityType.Method, bool readExplicitInterfaceImplementation = true) { if (method == null) return null; @@ -1685,6 +1694,9 @@ namespace ICSharpCode.NRefactory.TypeSystem if (method.IsStatic && HasExtensionAttribute(method)) { m.IsExtensionMethod = true; } + + if (readExplicitInterfaceImplementation) + ReadExplicitInterfaceImplementation(m, method); FinishReadMember(m, method); return m; @@ -1870,6 +1882,7 @@ namespace ICSharpCode.NRefactory.TypeSystem #endregion #region Read Property + [CLSCompliant(false)] public IUnresolvedProperty ReadProperty(PropertyDefinition property, IUnresolvedTypeDefinition parentType, EntityType propertyType = EntityType.Property) { @@ -1891,6 +1904,33 @@ namespace ICSharpCode.NRefactory.TypeSystem } } AddAttributes(property, p); + + int lastDot = property.Name.LastIndexOf('.'); + if (lastDot >= 0) { + string name = property.Name.Substring(lastDot + 1); + if (p.Getter != null && p.Getter.IsExplicitInterfaceImplementation) { + p.IsExplicitInterfaceImplementation = true; + foreach (var x in p.Getter.ExplicitInterfaceImplementations) { + var r = new DefaultMemberReference(p.Parameters.Count == 0 ? EntityType.Property : EntityType.Indexer, x.DeclaringTypeReference, name, 0, p.Parameters.Select(y => y.Type).ToList()); + p.ExplicitInterfaceImplementations.Add(r); + } + } + else if (p.Setter != null && p.Setter.IsExplicitInterfaceImplementation) { + p.IsExplicitInterfaceImplementation = true; + foreach (var x in p.Setter.ExplicitInterfaceImplementations) { + var r = new DefaultMemberReference(p.Parameters.Count == 0 ? EntityType.Property : EntityType.Indexer, x.DeclaringTypeReference, name, 0, p.Parameters.Select(y => y.Type).ToList()); + p.ExplicitInterfaceImplementations.Add(r); + } + } + } + + // This is very hacky. Due to which code parts are taken later in the execution, we need to pretend that the accessors are not explicit interface implementations at this stage. + if (p.Getter != null && p.Getter.IsExplicitInterfaceImplementation) { + p.Getter = ReadMethod(property.GetMethod, parentType, p, readExplicitInterfaceImplementation: false); + } + if (p.Setter != null && p.Setter.IsExplicitInterfaceImplementation) { + p.Setter = ReadMethod(property.GetMethod, parentType, p, readExplicitInterfaceImplementation: false); + } FinishReadMember(p, property); return p; diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMemberReference.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMemberReference.cs index 632207e621..ed67e6eba4 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMemberReference.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultMemberReference.cs @@ -38,8 +38,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation string name; int typeParameterCount; IList parameterTypes; + bool isExplicitInterfaceImplementation; - public DefaultMemberReference(EntityType entityType, ITypeReference typeReference, string name, int typeParameterCount = 0, IList parameterTypes = null) + public DefaultMemberReference(EntityType entityType, ITypeReference typeReference, string name, int typeParameterCount = 0, IList parameterTypes = null, bool isExplicitInterfaceImplementation = false) { if (typeReference == null) throw new ArgumentNullException("typeReference"); @@ -52,6 +53,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation this.name = name; this.typeParameterCount = typeParameterCount; this.parameterTypes = parameterTypes ?? EmptyList.Instance; + this.isExplicitInterfaceImplementation = isExplicitInterfaceImplementation; } public ITypeReference DeclaringTypeReference { @@ -65,11 +67,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation if (entityType == EntityType.Method) { members = type.GetMethods( m => m.Name == name && m.EntityType == EntityType.Method - && m.TypeParameters.Count == typeParameterCount && !m.IsExplicitInterfaceImplementation, + && m.TypeParameters.Count == typeParameterCount && m.IsExplicitInterfaceImplementation == isExplicitInterfaceImplementation, GetMemberOptions.IgnoreInheritedMembers); } else { members = type.GetMembers( - m => m.Name == name && m.EntityType == entityType && !m.IsExplicitInterfaceImplementation, + m => m.Name == name && m.EntityType == entityType && m.IsExplicitInterfaceImplementation == isExplicitInterfaceImplementation, GetMemberOptions.IgnoreInheritedMembers); } var resolvedParameterTypes = parameterTypes.Resolve(context);