diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedIssue.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedIssue.cs index b547be062d..867078a31e 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedIssue.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedIssue.cs @@ -68,8 +68,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring continue; var currentType = localResolveResult.Type; var candidateTypes = localResolveResult.Type.GetAllBaseTypes().ToList(); - candidateTypes.Add(localResolveResult.Type); - var possibleTypes = GetLeastDerivedImplementors(candidateTypes, invocations); + var possibleTypes = GetPossibleTypes(candidateTypes, invocations); var suggestedTypes = possibleTypes.Where(t => t != currentType); if (suggestedTypes.Any()) AddIssue(parameter, context.TranslateString("Parameter can be demoted to base class"), @@ -89,45 +88,45 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } } - static IEnumerable GetDeclaredMembers(IMember member) - { - var implementedInterfaceMembers = member.ImplementedInterfaceMembers; - if (implementedInterfaceMembers.Count == 0) { - yield return member; - } - else { - foreach (var interfaceMember in implementedInterfaceMembers) { - yield return interfaceMember; - } - } - } - HashSet GetNeededMembers(IEnumerable invocations) { var members = new HashSet(); foreach (var invocation in invocations) { - foreach (var member in GetDeclaredMembers(invocation.Member)) { - members.Add(member); - } + members.Add(invocation.Member); } return members; } - bool SatisfiesMemberRequests(IType type, HashSet members) + static bool HasCommonMemberDeclaration(IEnumerable acceptableMembers, IMember member) + { + var implementedInterfaceMembers = member.MemberDefinition.ImplementedInterfaceMembers; + if (implementedInterfaceMembers.Any()) { + return acceptableMembers.ContainsAny(implementedInterfaceMembers); + } + else { + return acceptableMembers.Contains(member.MemberDefinition); + } + } + + bool SatisfiesMemberRequests(IType candidateType, HashSet wantedMembers) { - var unsatisfiedMembers = new HashSet(members); - foreach (var member in type.GetMembers()) { - var interfaceMembers = member.ImplementedInterfaceMembers; - if (interfaceMembers.Count > 0) { - unsatisfiedMembers.RemoveWhere(m => interfaceMembers.Contains(m)); + var allMembers = candidateType.GetMembers(); + foreach (var wantedMember in wantedMembers) { + IEnumerable acceptableMembers; + if (wantedMember.ImplementedInterfaceMembers.Any()) { + acceptableMembers = wantedMember.ImplementedInterfaceMembers.ToList(); } else { - unsatisfiedMembers.RemoveWhere(m => m == member); + acceptableMembers = new List() { wantedMember.MemberDefinition }; } + + var hasMember = allMembers.Any(m => HasCommonMemberDeclaration(acceptableMembers, m)); + if (!hasMember) + return false; } - return unsatisfiedMembers.Count == 0; + return true; } - IEnumerable GetLeastDerivedImplementors(IEnumerable types, IEnumerable invocations) + IEnumerable GetPossibleTypes(IEnumerable types, IEnumerable invocations) { var members = GetNeededMembers(invocations); return from type in types @@ -176,5 +175,17 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } } } + + static class IEnumerableExtensions + { + public static bool ContainsAny(this IEnumerable collection, IEnumerable items) + { + foreach (var item in items) { + if (collection.Contains(item)) + return true; + } + return false; + } + } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedTests.cs index bf976e82aa..8f8e2894ff 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedTests.cs @@ -27,18 +27,17 @@ using System; using NUnit.Framework; using ICSharpCode.NRefactory.CSharp.CodeActions; using ICSharpCode.NRefactory.CSharp.Refactoring; -using System.Linq; namespace ICSharpCode.NRefactory.CSharp.CodeIssues { - [TestFixture] - public class ParameterCanBeDemotedTests : InspectionActionTestBase - { + [TestFixture] + public class ParameterCanBeDemotedTests : InspectionActionTestBase + { - [Test] - public void BasicTest() - { - var input = @" + [Test] + public void BasicTest() + { + var input = @" class A { public virtual void Foo() {} @@ -54,11 +53,13 @@ class C b.Foo(); } }"; - TestRefactoringContext context; - var issues = GetIssues(new ParameterCanBeDemotedIssue(), input, out context); - Assert.AreEqual(1, issues.Count); + TestRefactoringContext context; + var issues = GetIssues(new ParameterCanBeDemotedIssue(), input, out context); + Assert.AreEqual(1, issues.Count); + var issue = issues [0]; + Assert.AreEqual(1, issue.Actions.Count); - CheckFix(context, issues [0], @" + CheckFix(context, issues [0], @" class A { public virtual void Foo() {} @@ -74,13 +75,13 @@ class C b.Foo(); } }" - ); - } + ); + } - [Test] - public void InterfaceTest() - { - var input = @" + [Test] + public void InterfaceTest() + { + var input = @" interface IA { void Foo(); @@ -97,11 +98,13 @@ class C b.Foo(); } }"; - TestRefactoringContext context; - var issues = GetIssues(new ParameterCanBeDemotedIssue(), input, out context); - Assert.AreEqual(1, issues.Count); + TestRefactoringContext context; + var issues = GetIssues(new ParameterCanBeDemotedIssue(), input, out context); + Assert.AreEqual(1, issues.Count); + var issue = issues [0]; + Assert.AreEqual(1, issue.Actions.Count); - CheckFix(context, issues [0], @" + CheckFix(context, issues [0], @" interface IA { void Foo(); @@ -118,8 +121,66 @@ class C b.Foo(); } }" - ); - } + ); + } + + [Test] + public void MultipleInterfaceTest() + { + var input = @" +interface IA1 +{ + void Foo(); +} +interface IA2 +{ + void Bar(); +} +class B : IA1, IA2 +{ + public virtual void Foo() {} + public virtual void Bar() {} +} +class C : B {} +class Test +{ + void F(C c) + { + c.Foo(); + c.Bar(); + } +}"; + TestRefactoringContext context; + var issues = GetIssues(new ParameterCanBeDemotedIssue(), input, out context); + Assert.AreEqual(1, issues.Count); + var issue = issues [0]; + Assert.AreEqual(1, issue.Actions.Count); + + CheckFix(context, issues [0], @" +interface IA1 +{ + void Foo(); +} +interface IA2 +{ + void Bar(); +} +class B : IA1, IA2 +{ + public virtual void Foo() {} + public virtual void Bar() {} +} +class C : B {} +class Test +{ + void F(B c) + { + c.Foo(); + c.Bar(); + } +}" + ); + } string baseInput = @" interface IA @@ -160,7 +221,9 @@ class Test TestRefactoringContext context; var issues = GetIssues(new ParameterCanBeDemotedIssue(), input, out context); Assert.AreEqual(1, issues.Count); - var fixes = issues [0].Actions; + var issue = issues [0]; + Assert.AreEqual(4, issue.Actions.Count); + CheckFix(context, issues [0], baseInput + @" class Test { @@ -168,7 +231,8 @@ class Test { d.Foo(); } -}"); +}" + ); } } }