diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java index ed736df6834..15ef93fc318 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java @@ -32,6 +32,7 @@ import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.ReadOnly; +import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2; import org.jetbrains.kotlin.diagnostics.DiagnosticFactoryWithPsiElement; @@ -618,13 +619,40 @@ public class OverrideResolver { private static List collectImplementations(@NotNull Set relevantDirectlyOverridden) { List result = new ArrayList(relevantDirectlyOverridden.size()); for (CallableMemberDescriptor overriddenDescriptor : relevantDirectlyOverridden) { - if (overriddenDescriptor.getModality() != Modality.ABSTRACT) { + if (isImplementation(overriddenDescriptor)) { result.add(overriddenDescriptor); } } return result; } + private static boolean isImplementation(@NotNull CallableMemberDescriptor callableMemberDescriptor) { + // An abstract member is not an implementation. + if (callableMemberDescriptor.getModality() == Modality.ABSTRACT) return false; + + // Interfaces contain fake overrides for 'toString', 'hashCode', 'equals'. + // They are not considered implementations if their dispatch receiver type is 'Any'. + DeclarationDescriptor containingDeclaration = callableMemberDescriptor.getContainingDeclaration(); + assert containingDeclaration instanceof ClassDescriptor : + "ClassDescriptor expected, got " + containingDeclaration + " for " + callableMemberDescriptor; + ClassDescriptor containingClassDescriptor = (ClassDescriptor) containingDeclaration; + if (containingClassDescriptor.getKind() == ClassKind.INTERFACE && callableMemberDescriptor.getKind() == FAKE_OVERRIDE) { + ReceiverParameterDescriptor dispatchReceiverParameter = callableMemberDescriptor.getDispatchReceiverParameter(); + if (dispatchReceiverParameter == null) return false; + if (KotlinBuiltIns.isAny(dispatchReceiverParameter.getType())) return false; + } + + // A FAKE_OVERRIDE is an implementation iff it overrides an implementation. + if (callableMemberDescriptor.getKind() == FAKE_OVERRIDE) { + for (CallableMemberDescriptor overriddenDescriptor : callableMemberDescriptor.getOverriddenDescriptors()) { + if (isImplementation(overriddenDescriptor)) return true; + } + return false; + } + + return true; + } + private static void filterNotSynthesizedDescriptorsByModality( @NotNull Set allOverriddenDeclarations, @NotNull List abstractOverridden, diff --git a/compiler/testData/diagnostics/tests/override/kt12358.kt b/compiler/testData/diagnostics/tests/override/kt12358.kt new file mode 100644 index 00000000000..8cd9370c5f1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/kt12358.kt @@ -0,0 +1,9 @@ +abstract class A { + abstract override fun toString(): String +} + +interface B + +abstract class C : A(), B + +class Test : C() diff --git a/compiler/testData/diagnostics/tests/override/kt12358.txt b/compiler/testData/diagnostics/tests/override/kt12358.txt new file mode 100644 index 00000000000..9f617cafbf3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/kt12358.txt @@ -0,0 +1,28 @@ +package + +public abstract class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ fun toString(): kotlin.String +} + +public interface B { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public abstract class C : A, B { + public constructor C() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Test : C { + public constructor Test() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 2125a385832..c1cc571e58e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -12216,6 +12216,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("kt12358.kt") + public void testKt12358() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/override/kt12358.kt"); + doTest(fileName); + } + @TestMetadata("kt1862.kt") public void testKt1862() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/override/kt1862.kt");