Don't allow fake override to inherit implementation with wrong return type
#KT-4763 Fixed
This commit is contained in:
@@ -379,40 +379,68 @@ public class OverrideResolver {
|
||||
Set<CallableMemberDescriptor> relevantDirectlyOverridden =
|
||||
getRelevantDirectlyOverridden(overriddenDeclarationsByDirectParent, allFilteredOverriddenDeclarations);
|
||||
|
||||
int implCount = countImplementations(relevantDirectlyOverridden);
|
||||
if (implCount == 0) {
|
||||
collectNotSynthesizedDescriptorsByModality(allFilteredOverriddenDeclarations, abstractNoImpl, Modality.ABSTRACT);
|
||||
List<CallableMemberDescriptor> implementations = collectImplementations(relevantDirectlyOverridden);
|
||||
if (implementations.size() == 1 && isReturnTypeOkForOverride(descriptor, implementations.get(0))) return;
|
||||
|
||||
List<CallableMemberDescriptor> abstractOverridden = new ArrayList<CallableMemberDescriptor>(allFilteredOverriddenDeclarations.size());
|
||||
List<CallableMemberDescriptor> concreteOverridden = new ArrayList<CallableMemberDescriptor>(allFilteredOverriddenDeclarations.size());
|
||||
filterNotSynthesizedDescriptorsByModality(allFilteredOverriddenDeclarations, abstractOverridden, concreteOverridden);
|
||||
|
||||
if (implementations.isEmpty()) {
|
||||
abstractNoImpl.addAll(abstractOverridden);
|
||||
}
|
||||
else if (implCount > 1) {
|
||||
collectNotSynthesizedDescriptorsByModality(allFilteredOverriddenDeclarations, manyImpl, Modality.OPEN, Modality.FINAL);
|
||||
else if (implementations.size() > 1) {
|
||||
manyImpl.addAll(concreteOverridden);
|
||||
}
|
||||
else {
|
||||
abstractNoImpl.addAll(collectAbstractMethodsWithMoreSpecificReturnType(abstractOverridden, implementations.get(0)));
|
||||
}
|
||||
}
|
||||
|
||||
private static int countImplementations(@NotNull Set<CallableMemberDescriptor> relevantDirectlyOverridden) {
|
||||
int implCount = 0;
|
||||
@NotNull
|
||||
private static List<CallableMemberDescriptor> collectImplementations(@NotNull Set<CallableMemberDescriptor> relevantDirectlyOverridden) {
|
||||
List<CallableMemberDescriptor> result = new ArrayList<CallableMemberDescriptor>(relevantDirectlyOverridden.size());
|
||||
for (CallableMemberDescriptor overriddenDescriptor : relevantDirectlyOverridden) {
|
||||
if (overriddenDescriptor.getModality() != Modality.ABSTRACT) {
|
||||
implCount++;
|
||||
result.add(overriddenDescriptor);
|
||||
}
|
||||
}
|
||||
return implCount;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void collectNotSynthesizedDescriptorsByModality(
|
||||
private static void filterNotSynthesizedDescriptorsByModality(
|
||||
@NotNull Set<CallableMemberDescriptor> allOverriddenDeclarations,
|
||||
@NotNull Set<CallableMemberDescriptor> result,
|
||||
Modality... modalities
|
||||
@NotNull List<CallableMemberDescriptor> abstractOverridden,
|
||||
@NotNull List<CallableMemberDescriptor> concreteOverridden
|
||||
) {
|
||||
Set<Modality> modalitySet = Sets.newHashSet(modalities);
|
||||
for (CallableMemberDescriptor overridden : allOverriddenDeclarations) {
|
||||
if (modalitySet.contains(overridden.getModality())) {
|
||||
if (!CallResolverUtil.isOrOverridesSynthesized(overridden)) {
|
||||
result.add(overridden);
|
||||
if (!CallResolverUtil.isOrOverridesSynthesized(overridden)) {
|
||||
if (overridden.getModality() == Modality.ABSTRACT) {
|
||||
abstractOverridden.add(overridden);
|
||||
}
|
||||
else {
|
||||
concreteOverridden.add(overridden);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<CallableMemberDescriptor> collectAbstractMethodsWithMoreSpecificReturnType(
|
||||
@NotNull List<CallableMemberDescriptor> abstractOverridden,
|
||||
@NotNull CallableMemberDescriptor implementation
|
||||
) {
|
||||
List<CallableMemberDescriptor> result = new ArrayList<CallableMemberDescriptor>(abstractOverridden.size());
|
||||
for (CallableMemberDescriptor abstractMember : abstractOverridden) {
|
||||
if (!isReturnTypeOkForOverride(abstractMember, implementation)) {
|
||||
result.add(abstractMember);
|
||||
}
|
||||
}
|
||||
assert !result.isEmpty() : "Implementation (" + implementation + ") doesn't have the most specific type, " +
|
||||
"but none of the other overridden methods does either: " + abstractOverridden;
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Set<CallableMemberDescriptor> getRelevantDirectlyOverridden(
|
||||
@NotNull Map<CallableMemberDescriptor, Set<CallableMemberDescriptor>> overriddenByParent,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class B () {}
|
||||
|
||||
open class A(val b : B) {
|
||||
fun a() = object: A(b) {}
|
||||
fun a(): A = object: A(b) {}
|
||||
}
|
||||
|
||||
fun box() : String {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
trait A {
|
||||
fun foo(): Object
|
||||
fun foo(): Any
|
||||
}
|
||||
|
||||
open class B {
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
trait A {
|
||||
fun f(): String = "string"
|
||||
}
|
||||
|
||||
open class B {
|
||||
open fun f(): CharSequence = "charSequence"
|
||||
}
|
||||
|
||||
class <!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>C<!> : B(), A
|
||||
|
||||
val obj: A = <!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>object<!> : B(), A {}
|
||||
@@ -0,0 +1,11 @@
|
||||
trait A {
|
||||
fun f(): String
|
||||
}
|
||||
|
||||
open class B {
|
||||
open fun f(): CharSequence = "charSequence"
|
||||
}
|
||||
|
||||
class <!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>C<!> : B(), A
|
||||
|
||||
val d: A = <!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>object<!> : B(), A {}
|
||||
@@ -0,0 +1,11 @@
|
||||
trait P {
|
||||
var f: Number
|
||||
}
|
||||
|
||||
open class Q {
|
||||
val x: Int = 42
|
||||
}
|
||||
|
||||
class <!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>R<!> : P, Q()
|
||||
|
||||
val s: Q = <!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>object<!> : Q(), P {}
|
||||
@@ -5307,6 +5307,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest("compiler/testData/diagnostics/tests/override/FakeOverrideAbstractAndNonAbstractFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FakeOverrideDifferentDeclarationSignatures.kt")
|
||||
public void testFakeOverrideDifferentDeclarationSignatures() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/override/FakeOverrideDifferentDeclarationSignatures.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Generics.kt")
|
||||
public void testGenerics() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/override/Generics.kt");
|
||||
@@ -5327,6 +5332,16 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest("compiler/testData/diagnostics/tests/override/kt2052.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt4763.kt")
|
||||
public void testKt4763() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/override/kt4763.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt4763property.kt")
|
||||
public void testKt4763property() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/override/kt4763property.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt4785.kt")
|
||||
public void testKt4785() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/override/kt4785.kt");
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// !DIAGNOSTICS_NUMBER: 1
|
||||
// !DIAGNOSTICS: ABSTRACT_MEMBER_NOT_IMPLEMENTED
|
||||
// !MESSAGE_TYPE: TEXT
|
||||
|
||||
trait T1
|
||||
trait T2 : T1
|
||||
trait T3 : T2
|
||||
trait T4 : T3
|
||||
|
||||
|
||||
trait A {
|
||||
fun f(): T1
|
||||
}
|
||||
|
||||
trait B {
|
||||
fun f(): T2 = null!!
|
||||
}
|
||||
|
||||
trait C {
|
||||
fun f(): T3
|
||||
}
|
||||
|
||||
abstract class D {
|
||||
abstract fun f(): T4
|
||||
}
|
||||
|
||||
class E : A, B, C, D()
|
||||
@@ -0,0 +1,2 @@
|
||||
<!-- wrongReturnTypeInImplementation1 -->
|
||||
Class 'E' must be declared abstract or implement abstract member internal abstract fun f(): T3 defined in C
|
||||
@@ -146,4 +146,9 @@ public class DiagnosticMessageTestGenerated extends AbstractDiagnosticMessageTes
|
||||
doTest("idea/testData/diagnosticMessage/upperBoundViolated.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("wrongReturnTypeInImplementation.kt")
|
||||
public void testWrongReturnTypeInImplementation() throws Exception {
|
||||
doTest("idea/testData/diagnosticMessage/wrongReturnTypeInImplementation.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user