Don't allow fake override to inherit implementation with wrong return type

#KT-4763 Fixed
This commit is contained in:
Alexander Udalov
2014-03-28 23:09:52 +04:00
parent 05a04293ab
commit 9760f30c99
10 changed files with 128 additions and 18 deletions
@@ -379,40 +379,68 @@ public class OverrideResolver {
Set<CallableMemberDescriptor> relevantDirectlyOverridden = Set<CallableMemberDescriptor> relevantDirectlyOverridden =
getRelevantDirectlyOverridden(overriddenDeclarationsByDirectParent, allFilteredOverriddenDeclarations); getRelevantDirectlyOverridden(overriddenDeclarationsByDirectParent, allFilteredOverriddenDeclarations);
int implCount = countImplementations(relevantDirectlyOverridden); List<CallableMemberDescriptor> implementations = collectImplementations(relevantDirectlyOverridden);
if (implCount == 0) { if (implementations.size() == 1 && isReturnTypeOkForOverride(descriptor, implementations.get(0))) return;
collectNotSynthesizedDescriptorsByModality(allFilteredOverriddenDeclarations, abstractNoImpl, Modality.ABSTRACT);
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) { else if (implementations.size() > 1) {
collectNotSynthesizedDescriptorsByModality(allFilteredOverriddenDeclarations, manyImpl, Modality.OPEN, Modality.FINAL); manyImpl.addAll(concreteOverridden);
}
else {
abstractNoImpl.addAll(collectAbstractMethodsWithMoreSpecificReturnType(abstractOverridden, implementations.get(0)));
} }
} }
private static int countImplementations(@NotNull Set<CallableMemberDescriptor> relevantDirectlyOverridden) { @NotNull
int implCount = 0; private static List<CallableMemberDescriptor> collectImplementations(@NotNull Set<CallableMemberDescriptor> relevantDirectlyOverridden) {
List<CallableMemberDescriptor> result = new ArrayList<CallableMemberDescriptor>(relevantDirectlyOverridden.size());
for (CallableMemberDescriptor overriddenDescriptor : relevantDirectlyOverridden) { for (CallableMemberDescriptor overriddenDescriptor : relevantDirectlyOverridden) {
if (overriddenDescriptor.getModality() != Modality.ABSTRACT) { 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> allOverriddenDeclarations,
@NotNull Set<CallableMemberDescriptor> result, @NotNull List<CallableMemberDescriptor> abstractOverridden,
Modality... modalities @NotNull List<CallableMemberDescriptor> concreteOverridden
) { ) {
Set<Modality> modalitySet = Sets.newHashSet(modalities);
for (CallableMemberDescriptor overridden : allOverriddenDeclarations) { for (CallableMemberDescriptor overridden : allOverriddenDeclarations) {
if (modalitySet.contains(overridden.getModality())) { if (!CallResolverUtil.isOrOverridesSynthesized(overridden)) {
if (!CallResolverUtil.isOrOverridesSynthesized(overridden)) { if (overridden.getModality() == Modality.ABSTRACT) {
result.add(overridden); 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 @NotNull
private static Set<CallableMemberDescriptor> getRelevantDirectlyOverridden( private static Set<CallableMemberDescriptor> getRelevantDirectlyOverridden(
@NotNull Map<CallableMemberDescriptor, Set<CallableMemberDescriptor>> overriddenByParent, @NotNull Map<CallableMemberDescriptor, Set<CallableMemberDescriptor>> overriddenByParent,
@@ -1,7 +1,7 @@
class B () {} class B () {}
open class A(val b : B) { open class A(val b : B) {
fun a() = object: A(b) {} fun a(): A = object: A(b) {}
} }
fun box() : String { fun box() : String {
@@ -1,5 +1,5 @@
trait A { trait A {
fun foo(): Object fun foo(): Any
} }
open class B { open class B {
@@ -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"); 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") @TestMetadata("Generics.kt")
public void testGenerics() throws Exception { public void testGenerics() throws Exception {
doTest("compiler/testData/diagnostics/tests/override/Generics.kt"); 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"); 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") @TestMetadata("kt4785.kt")
public void testKt4785() throws Exception { public void testKt4785() throws Exception {
doTest("compiler/testData/diagnostics/tests/override/kt4785.kt"); 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"); doTest("idea/testData/diagnosticMessage/upperBoundViolated.kt");
} }
@TestMetadata("wrongReturnTypeInImplementation.kt")
public void testWrongReturnTypeInImplementation() throws Exception {
doTest("idea/testData/diagnosticMessage/wrongReturnTypeInImplementation.kt");
}
} }