mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
inherit abstract/default when inheritor provides substitutor which makes 2 different methods in the super hierarchy override equivalent (IDEA-140490; IDEA-146056)
This commit is contained in:
@@ -439,52 +439,91 @@ public class GenericsHighlightUtil {
|
||||
}
|
||||
|
||||
static HighlightInfo checkUnrelatedDefaultMethods(@NotNull PsiClass aClass,
|
||||
@NotNull Collection<HierarchicalMethodSignature> signaturesWithSupers,
|
||||
@NotNull PsiIdentifier classIdentifier) {
|
||||
for (HierarchicalMethodSignature methodSignature : signaturesWithSupers) {
|
||||
final PsiMethod method = methodSignature.getMethod();
|
||||
final boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
if (method.hasModifierProperty(PsiModifier.DEFAULT) || isAbstract) {
|
||||
@NotNull Collection<HierarchicalMethodSignature> signaturesWithSupers,
|
||||
@NotNull PsiIdentifier classIdentifier) {
|
||||
final Map<MethodSignature, Set<PsiMethod>> overrideEquivalent =
|
||||
new THashMap<MethodSignature, Set<PsiMethod>>(MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY);
|
||||
PsiClass[] supers = aClass.getSupers();
|
||||
for (int i = 0; i < supers.length; i++) {
|
||||
PsiClass superClass = supers[i];
|
||||
boolean subType = false;
|
||||
for (int j = 0; j < supers.length; j++) {
|
||||
if (j == i) continue;
|
||||
subType |= supers[j].isInheritor(supers[i], true);
|
||||
}
|
||||
if (subType) continue;
|
||||
for (HierarchicalMethodSignature hms : superClass.getVisibleSignatures()) {
|
||||
final PsiMethod method = hms.getMethod();
|
||||
if (aClass.findMethodsBySignature(method, false).length > 0) continue;
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
List<HierarchicalMethodSignature> superSignatures = methodSignature.getSuperSignatures();
|
||||
if (!superSignatures.isEmpty()) {
|
||||
for (HierarchicalMethodSignature signature : superSignatures) {
|
||||
final PsiMethod superMethod = signature.getMethod();
|
||||
final PsiClass superContainingClass = superMethod.getContainingClass();
|
||||
if (containingClass != null && superContainingClass != null && !InheritanceUtil.isInheritorOrSelf(containingClass, superContainingClass, true)) {
|
||||
final boolean isDefault = superMethod.hasModifierProperty(PsiModifier.DEFAULT);
|
||||
if (!aClass.hasModifierProperty(PsiModifier.ABSTRACT) && !isDefault && !isAbstract) {
|
||||
final String message = JavaErrorMessages.message(
|
||||
aClass instanceof PsiEnumConstantInitializer ? "enum.constant.should.implement.method" : "class.must.be.abstract",
|
||||
HighlightUtil.formatClass(superContainingClass),
|
||||
JavaHighlightUtil.formatMethod(superMethod),
|
||||
HighlightUtil.formatClass(superContainingClass, false));
|
||||
final HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
|
||||
.range(classIdentifier).descriptionAndTooltip(message)
|
||||
.create();
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createImplementMethodsFix(aClass));
|
||||
return info;
|
||||
}
|
||||
if (containingClass == null) continue;
|
||||
final PsiSubstitutor containingClassSubstitutor =
|
||||
TypeConversionUtil.getSuperClassSubstitutor(containingClass, aClass, PsiSubstitutor.EMPTY);
|
||||
final PsiSubstitutor finalSubstitutor =
|
||||
PsiSuperMethodImplUtil.obtainFinalSubstitutor(containingClass, containingClassSubstitutor, hms.getSubstitutor(), false);
|
||||
final MethodSignatureBackedByPsiMethod signature = MethodSignatureBackedByPsiMethod.create(method, finalSubstitutor, false);
|
||||
Set<PsiMethod> methods = overrideEquivalent.get(signature);
|
||||
if (methods == null) {
|
||||
methods = new LinkedHashSet<PsiMethod>();
|
||||
overrideEquivalent.put(signature, methods);
|
||||
}
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefault || !isAbstract && superMethod.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
final String message = isDefault && !isAbstract
|
||||
? " inherits unrelated defaults for "
|
||||
: " inherits abstract and default for ";
|
||||
final String inheritUnrelatedDefaultsMessage = HighlightUtil.formatClass(aClass) +
|
||||
message +
|
||||
JavaHighlightUtil.formatMethod(method) +
|
||||
" from types " +
|
||||
HighlightUtil.formatClass(containingClass) +
|
||||
" and " +
|
||||
HighlightUtil.formatClass(superContainingClass);
|
||||
final HighlightInfo info = HighlightInfo
|
||||
.newHighlightInfo(HighlightInfoType.ERROR).range(classIdentifier).descriptionAndTooltip(inheritUnrelatedDefaultsMessage)
|
||||
.create();
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createImplementMethodsFix(aClass));
|
||||
return info;
|
||||
}
|
||||
}
|
||||
final boolean isInterface = aClass.isInterface();
|
||||
for (Set<PsiMethod> overrideEquivalentMethods : overrideEquivalent.values()) {
|
||||
if (overrideEquivalentMethods.size() <= 1) continue;
|
||||
List<PsiMethod> defaults = null;
|
||||
List<PsiMethod> astracts = null;
|
||||
boolean hasConcrete = false;
|
||||
for (PsiMethod method : overrideEquivalentMethods) {
|
||||
final boolean isDefault = method.hasModifierProperty(PsiModifier.DEFAULT);
|
||||
final boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
if (isDefault) {
|
||||
if (defaults == null) defaults = new ArrayList<PsiMethod>(2);
|
||||
defaults.add(method);
|
||||
}
|
||||
if (isAbstract) {
|
||||
if (astracts == null) astracts = new ArrayList<PsiMethod>(2);
|
||||
astracts.add(method);
|
||||
}
|
||||
hasConcrete |= !isDefault && !isAbstract;
|
||||
}
|
||||
|
||||
if (!hasConcrete && defaults != null) {
|
||||
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 PsiClass unrelatedMethodContainingClass = unrelatedMethod.getContainingClass();
|
||||
if (unrelatedMethodContainingClass == null) continue;
|
||||
if (!aClass.hasModifierProperty(PsiModifier.ABSTRACT) && astracts != null && unrelatedMethodContainingClass.isInterface()) {
|
||||
if (defaultMethodContainingClass.isInheritor(unrelatedMethodContainingClass, true)) 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)),
|
||||
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)) {
|
||||
continue;
|
||||
}
|
||||
final String message = astracts != null ? " inherits abstract and default for " : " inherits unrelated defaults 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))
|
||||
.create();
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createImplementMethodsFix(aClass));
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,4 +10,4 @@ interface A5 {
|
||||
Iterator iterator();
|
||||
}
|
||||
|
||||
abstract class <error descr="B inherits abstract and default for iterator() from types A5 and A4">B</error> implements A5, A4 {}
|
||||
abstract class <error descr="B inherits abstract and default for iterator() from types A4 and A5">B</error> implements A5, A4 {}
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
class Test1 {
|
||||
interface A<T> {
|
||||
void foo(T x);
|
||||
default void foo(String x) { }
|
||||
}
|
||||
|
||||
class <error descr="Class 'C' must either be declared abstract or implement abstract method 'foo(T)' in 'A'">C</error> implements A<String> { }
|
||||
abstract class <error descr="Test1.D inherits abstract and default for foo(String) from types Test1.A and Test1.A">D</error> implements A<String> {}
|
||||
interface <error descr="Test1.E inherits abstract and default for foo(String) from types Test1.A and Test1.A">E</error> extends A<String> {}
|
||||
}
|
||||
|
||||
class Test2 {
|
||||
interface A {
|
||||
default void foo(String x) { }
|
||||
}
|
||||
|
||||
interface B<T> extends A {
|
||||
void foo(T x);
|
||||
}
|
||||
|
||||
<error descr="Class 'C' must either be declared abstract or implement abstract method 'foo(T)' in 'B'">class <error descr="Class 'C' must either be declared abstract or implement abstract method 'foo(T)' in 'B'">C</error> implements B<String></error> { }
|
||||
abstract class D implements B<String> {}
|
||||
interface E extends B<String> {}
|
||||
}
|
||||
|
||||
class Test3 {
|
||||
interface A<T> {
|
||||
default void foo(T x) {}
|
||||
default void foo(String x) { }
|
||||
}
|
||||
|
||||
class <error descr="Test3.C inherits unrelated defaults for foo(T) from types Test3.A and Test3.A">C</error> implements A<String> { }
|
||||
abstract class <error descr="Test3.D inherits unrelated defaults for foo(T) from types Test3.A and Test3.A">D</error> implements A<String> {}
|
||||
interface <error descr="Test3.E inherits unrelated defaults for foo(T) from types Test3.A and Test3.A">E</error> extends A<String> {}
|
||||
}
|
||||
|
||||
class Test4 {
|
||||
interface A {
|
||||
default void foo(String x) { }
|
||||
}
|
||||
|
||||
interface B<T> extends A {
|
||||
default void foo(T x) {}
|
||||
}
|
||||
|
||||
class C implements B<String> { }
|
||||
abstract class D implements B<String> {}
|
||||
interface E extends B<String> {}
|
||||
}
|
||||
@@ -10,9 +10,9 @@ interface SecondParent {
|
||||
int doSomething();
|
||||
}
|
||||
|
||||
class <error descr="Class 'SecondParent' must either be declared abstract or implement abstract method 'doSomething()' in 'SecondParent'">FirstSon</error> implements FirstParent, SecondParent {}
|
||||
class <error descr="Class 'FirstSon' must either be declared abstract or implement abstract method 'doSomething()' in 'SecondParent'">FirstSon</error> implements FirstParent, SecondParent {}
|
||||
|
||||
<error descr="Class 'SecondSon' must either be declared abstract or implement abstract method 'doSomething()' in 'SecondParent'">class <error descr="SecondSon inherits abstract and default for doSomething() from types SecondParent and FirstParent">SecondSon</error> implements SecondParent, FirstParent</error> {}
|
||||
<error descr="Class 'SecondSon' must either be declared abstract or implement abstract method 'doSomething()' in 'SecondParent'">class <error descr="Class 'SecondSon' must either be declared abstract or implement abstract method 'doSomething()' in 'SecondParent'">SecondSon</error> implements SecondParent, FirstParent</error> {}
|
||||
|
||||
interface A {
|
||||
default int foo() {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package com.intellij.codeInsight.daemon.lambda;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -86,6 +85,10 @@ public class Interface8MethodsHighlightingTest extends LightCodeInsightFixtureTe
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testInherit2MethodsWithSameOverrideEquivalentSignatureFromOneSuperclass() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
doTest(false, false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user