push down: warn if result in unrelated defaults/non-implemented method(IDEA-155595)

This commit is contained in:
Anna.Kozlova
2017-12-08 19:01:51 +01:00
parent 4b91a73011
commit 4b2daa611c
6 changed files with 99 additions and 3 deletions
@@ -16,6 +16,7 @@
package com.intellij.refactoring.memberPushDown;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.InheritanceUtil;
@@ -29,6 +30,7 @@ import com.intellij.refactoring.util.classMembers.MemberInfo;
import com.intellij.util.containers.MultiMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class PushDownConflicts {
@@ -76,6 +78,26 @@ public class PushDownConflicts {
if (annotation != null && myMovedMembers.contains(LambdaUtil.getFunctionalInterfaceMethod(myClass))) {
myConflicts.putValue(annotation, RefactoringBundle.message("functional.interface.broken"));
}
boolean isAbstract = myClass.hasModifierProperty(PsiModifier.ABSTRACT);
for (PsiMember member : myMovedMembers) {
if (!member.hasModifierProperty(PsiModifier.STATIC) && member instanceof PsiMethod && !myAbstractMembers.contains(member)) {
Set<PsiClass> unrelatedDefaults = new LinkedHashSet<>();
for (PsiMethod superMethod : ((PsiMethod)member).findSuperMethods()) {
if (!isAbstract && superMethod.hasModifierProperty(PsiModifier.ABSTRACT)) {
myConflicts.putValue(member, "Non abstract " + RefactoringUIUtil.getDescription(myClass, false) + " will miss implementation of " + RefactoringUIUtil.getDescription(superMethod, false));
break;
}
if (superMethod.hasModifierProperty(PsiModifier.DEFAULT)) {
unrelatedDefaults.add(superMethod.getContainingClass());
if (unrelatedDefaults.size() > 1) {
myConflicts.putValue(member, CommonRefactoringUtil.capitalize(RefactoringUIUtil.getDescription(myClass, false) + " will inherit unrelated defaults from " +
StringUtil.join(unrelatedDefaults, aClass -> RefactoringUIUtil.getDescription(aClass, false)," and ")));
break;
}
}
}
}
}
}
public void checkTargetClassConflicts(final PsiElement targetElement, final PsiElement context) {
@@ -0,0 +1,16 @@
interface A {
default void foo() {}
}
interface I {
default void foo() { }
}
class B implements I, A {
@Override
public void fo<caret>o() { }
}
class C extends B {
}
@@ -0,0 +1,16 @@
interface A {
default void foo() {}
}
interface I {
default void foo() { }
}
class B implements I, A {
}
class C extends B {
@Override
public void foo() { }
}
@@ -0,0 +1,11 @@
abstract class A {
abstract void foo();
}
class B extends A {
@Override
public void fo<caret>o() { }
}
class C extends B {
}
@@ -0,0 +1,11 @@
abstract class A {
abstract void foo();
}
class B extends A {
}
class C extends B {
@Override
public void foo() { }
}
@@ -27,7 +27,9 @@ import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
/**
* @author anna
@@ -107,11 +109,31 @@ public class PushDownTest extends LightRefactoringTestCase {
doTest();
}
public void testClassShouldBeAbstractConflict() {
doTest(conflicts -> {
assertSameElements(conflicts.values(), Collections.singletonList("Non abstract class <b><code>B</code></b> will miss implementation of method <b><code>foo()</code></b>"));
});
}
public void testClassInheritsUnrelatedDefaultsConflict() {
doTest(conflicts -> {
assertSameElements(conflicts.values(), Collections.singletonList("Class <b><code>B</code></b> will inherit unrelated defaults from interface <b><code>I</code></b> and interface <b><code>A</code></b>"));
});
}
private void doTest() {
doTest(false);
}
private void doTest(final boolean failure) {
doTest(conflicts -> {
if (failure == conflicts.isEmpty()) {
fail(failure ? "Conflict was not detected" : "False conflict was detected");
}
});
}
private void doTest(final Consumer<MultiMap<PsiElement, String>> checkConflicts) {
configureByFile(BASE_PATH + getTestName(false) + ".java");
final PsiElement targetElement = TargetElementUtil.findTargetElement(getEditor(), TargetElementUtil.ELEMENT_NAME_ACCEPTED);
@@ -147,9 +169,7 @@ public class PushDownTest extends LightRefactoringTestCase {
new DocCommentPolicy(DocCommentPolicy.ASIS)) {
@Override
protected boolean showConflicts(@NotNull MultiMap<PsiElement, String> conflicts, UsageInfo[] usages) {
if (failure == conflicts.isEmpty()) {
fail(failure ? "Conflict was not detected" : "False conflict was detected");
}
checkConflicts.accept(conflicts);
return true;
}
}.run();