fix inheritance of abstract and default methods when multiple defaults are present

This commit is contained in:
Anna.Kozlova
2016-08-09 14:32:14 +02:00
parent dad8ec13e5
commit 0e3d077bfe
4 changed files with 93 additions and 15 deletions

View File

@@ -446,7 +446,7 @@ public class GenericsHighlightUtil {
for (Set<PsiMethod> overrideEquivalentMethods : overrideEquivalent.values()) {
if (overrideEquivalentMethods.size() <= 1) continue;
List<PsiMethod> defaults = null;
List<PsiMethod> astracts = null;
List<PsiMethod> abstracts = null;
boolean hasConcrete = false;
for (PsiMethod method : overrideEquivalentMethods) {
final boolean isDefault = method.hasModifierProperty(PsiModifier.DEFAULT);
@@ -456,8 +456,8 @@ public class GenericsHighlightUtil {
defaults.add(method);
}
if (isAbstract) {
if (astracts == null) astracts = new ArrayList<>(2);
astracts.add(method);
if (abstracts == null) abstracts = new ArrayList<>(2);
abstracts.add(method);
}
hasConcrete |= !isDefault && !isAbstract;
}
@@ -466,37 +466,38 @@ public class GenericsHighlightUtil {
final PsiMethod defaultMethod = defaults.get(0);
final PsiClass defaultMethodContainingClass = defaultMethod.getContainingClass();
if (defaultMethodContainingClass == null) continue;
final PsiMethod unrelatedMethod = astracts != null ? astracts.get(0) : defaults.get(1);
final PsiMethod unrelatedMethod = abstracts != null ? abstracts.get(0) : defaults.get(1);
final PsiClass unrelatedMethodContainingClass = unrelatedMethod.getContainingClass();
if (unrelatedMethodContainingClass == null) continue;
if (!aClass.hasModifierProperty(PsiModifier.ABSTRACT) && !(aClass instanceof PsiTypeParameter)
&& astracts != null && unrelatedMethodContainingClass.isInterface()) {
&& abstracts != null && unrelatedMethodContainingClass.isInterface()) {
if (defaultMethodContainingClass.isInheritor(unrelatedMethodContainingClass, true) &&
MethodSignatureUtil.isSubsignature(unrelatedMethod.getSignature(TypeConversionUtil.getSuperClassSubstitutor(unrelatedMethodContainingClass, defaultMethodContainingClass, PsiSubstitutor.EMPTY)),
defaultMethod.getSignature(PsiSubstitutor.EMPTY))) {
continue;
}
final String key = aClass instanceof PsiEnumConstantInitializer ? "enum.constant.should.implement.method" : "class.must.be.abstract";
final String message = JavaErrorMessages.message(key, HighlightUtil.formatClass(aClass, false), JavaHighlightUtil.formatMethod(astracts.get(0)),
final String message = JavaErrorMessages.message(key, HighlightUtil.formatClass(aClass, false), JavaHighlightUtil.formatMethod(abstracts.get(0)),
HighlightUtil.formatClass(unrelatedMethodContainingClass, false));
final HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(classIdentifier).descriptionAndTooltip(message).create();
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createImplementMethodsFix(aClass));
return info;
}
if (isInterface || astracts == null || unrelatedMethodContainingClass.isInterface()) {
if (defaultMethodContainingClass.isInheritor(unrelatedMethodContainingClass, true) ||
unrelatedMethodContainingClass.isInheritor(defaultMethodContainingClass, true)) {
if (isInterface || abstracts == null || unrelatedMethodContainingClass.isInterface()) {
final List<PsiClass> defaultContainingClasses = ContainerUtil.mapNotNull(defaults, PsiMethod::getContainingClass);
final String unrelatedDefaults = hasUnrelatedDefaults(defaultContainingClasses);
if (unrelatedDefaults == null &&
(abstracts == null || !hasNotOverriddenAbstract(defaultContainingClasses, unrelatedMethodContainingClass))) {
continue;
}
final String message = astracts != null ? " inherits abstract and default for " : " inherits unrelated defaults for ";
final String message = unrelatedDefaults != null ? " inherits unrelated defaults for " : " inherits abstract and default for ";
final HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(classIdentifier).descriptionAndTooltip(
HighlightUtil.formatClass(aClass) +
message +
JavaHighlightUtil.formatMethod(defaultMethod) +
" from types " +
HighlightUtil.formatClass(defaultMethodContainingClass) +
" and " +
HighlightUtil.formatClass(unrelatedMethodContainingClass))
JavaHighlightUtil.formatMethod(defaultMethod) + " from types " +
(unrelatedDefaults != null ? unrelatedDefaults
: HighlightUtil.formatClass(defaultMethodContainingClass) + " and " + HighlightUtil.formatClass(unrelatedMethodContainingClass)))
.create();
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createImplementMethodsFix(aClass));
return info;
@@ -506,6 +507,31 @@ public class GenericsHighlightUtil {
return null;
}
private static boolean belongToOneHierarchy(@NotNull PsiClass defaultMethodContainingClass, @NotNull PsiClass unrelatedMethodContainingClass) {
return defaultMethodContainingClass.isInheritor(unrelatedMethodContainingClass, true) ||
unrelatedMethodContainingClass.isInheritor(defaultMethodContainingClass, true);
}
private static boolean hasNotOverriddenAbstract(List<PsiClass> defaultContainingClasses, @NotNull PsiClass abstractMethodContainingClass) {
return !defaultContainingClasses.stream().anyMatch(containingClass -> belongToOneHierarchy(containingClass, abstractMethodContainingClass));
}
private static String hasUnrelatedDefaults(List<PsiClass> defaults) {
if (defaults.size() > 1) {
for (int i = 0; i < defaults.size(); i++) {
final PsiClass aClass1 = defaults.get(i);
for (int j = i + 1; j < defaults.size(); j++) {
final PsiClass aClass2 = defaults.get(j);
if (aClass2 != null && !belongToOneHierarchy(aClass1, aClass2)) {
return HighlightUtil.formatClass(aClass1) + " and " + HighlightUtil.formatClass(aClass2);
}
}
}
}
return null;
}
static HighlightInfo checkUnrelatedConcrete(@NotNull PsiClass psiClass,
@NotNull PsiIdentifier classIdentifier) {
final PsiClass superClass = psiClass.getSuperClass();

View File

@@ -0,0 +1,22 @@
interface I {
void foo();
}
interface I1 extends I {
//void foo();
}
interface J {
default void foo() {}
}
interface A extends J, I {
@Override
default void foo() {}
}
interface O extends J {
}
interface R extends O, A, I1{}

View File

@@ -0,0 +1,22 @@
interface I {
void foo();
}
interface I1 extends I {
}
interface J {
default void foo() {}
}
interface A extends J, I {
@Override
default void foo() {}
}
interface O extends J {
default void foo() {}
}
interface <error descr="R inherits unrelated defaults for foo() from types O and A">R</error> extends O, A, I1{}

View File

@@ -107,6 +107,14 @@ public class Interface8MethodsHighlightingTest extends LightCodeInsightFixtureTe
doTest();
}
public void testUnrelatedDefaultsWhenAbstractIsOverridden() throws Exception {
doTest();
}
public void testAbstractOverriddenBySecondDefault() throws Exception {
doTest();
}
private void doTest() {
doTest(false, false);
}