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 =
|
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 {
|
||||||
|
|||||||
+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");
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user