From 1633190478909fd5a52d283288503f6ec9396e69 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Mon, 5 Apr 2021 12:11:10 +0300 Subject: [PATCH] Refine expect classes in CliSealedClassInheritorsProvider KT-45796 Fixed --- ...irOldFrontendDiagnosticsTestGenerated.java | 6 +++ .../exhaustiveness/kt45796.fir.kt | 30 +++++++++++ .../multiplatform/exhaustiveness/kt45796.kt | 30 +++++++++++ .../multiplatform/exhaustiveness/kt45796.txt | 51 +++++++++++++++++++ .../test/runners/DiagnosticTestGenerated.java | 6 +++ .../resolve/SealedClassInheritorsProvider.kt | 20 ++++++-- 6 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.fir.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.txt diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 58fb1274d75..5ee0abe7862 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -18041,6 +18041,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti public void testExpectSealedInterface() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/expectSealedInterface.kt"); } + + @Test + @TestMetadata("kt45796.kt") + public void testKt45796() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.kt"); + } } @Nested diff --git a/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.fir.kt new file mode 100644 index 00000000000..fe2ffa4b9c0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.fir.kt @@ -0,0 +1,30 @@ +// !LANGUAGE: +MultiPlatformProjects +// ISSUE: KT-45796 + +// MODULE: m1-common +expect sealed class SealedClass() { + class Nested : SealedClass { + class NestedDeeper : SealedClass + } +} + +fun whenForExpectSealed(s: SealedClass): Int { + return when (s) { // should be error, because actual sealed class may add more implementations + is SealedClass.Nested.NestedDeeper -> 7 + is SealedClass.Nested -> 8 + } +} + +// MODULE: m1-jvm(m1-common) +actual sealed class SealedClass { + actual class Nested : SealedClass() { + actual class NestedDeeper : SealedClass() + } +} + +fun whenForSealed(s: SealedClass): Int { + return when (s) { // Should be OK + is SealedClass.Nested.NestedDeeper -> 7 + is SealedClass.Nested -> 8 + } +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.kt b/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.kt new file mode 100644 index 00000000000..92df145901e --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.kt @@ -0,0 +1,30 @@ +// !LANGUAGE: +MultiPlatformProjects +// ISSUE: KT-45796 + +// MODULE: m1-common +expect sealed class SealedClass() { + class Nested : SealedClass { + class NestedDeeper : SealedClass + } +} + +fun whenForExpectSealed(s: SealedClass): Int { + return when (s) { // should be error, because actual sealed class may add more implementations + is SealedClass.Nested.NestedDeeper -> 7 + is SealedClass.Nested -> 8 + } +} + +// MODULE: m1-jvm(m1-common) +actual sealed class SealedClass { + actual class Nested : SealedClass() { + actual class NestedDeeper : SealedClass() + } +} + +fun whenForSealed(s: SealedClass): Int { + return when (s) { // Should be OK + is SealedClass.Nested.NestedDeeper -> 7 + is SealedClass.Nested -> 8 + } +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.txt b/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.txt new file mode 100644 index 00000000000..1096675e353 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.txt @@ -0,0 +1,51 @@ +// -- Module: -- +package + +public fun whenForExpectSealed(/*0*/ s: SealedClass): kotlin.Int + +public sealed expect class SealedClass { + protected constructor SealedClass() + 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 final expect class Nested : SealedClass { + 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 final expect class NestedDeeper : SealedClass { + 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 + } + } +} + +// -- Module: -- +package + +public fun whenForExpectSealed(/*0*/ s: SealedClass): kotlin.Int +public fun whenForSealed(/*0*/ s: SealedClass): kotlin.Int + +public sealed actual class SealedClass { + protected constructor SealedClass() + 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 final actual class Nested : SealedClass { + public constructor Nested() + 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 final actual class NestedDeeper : SealedClass { + public constructor NestedDeeper() + 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-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 1770c3e715f..372d5e61ac6 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -18047,6 +18047,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { public void testExpectSealedInterface() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/expectSealedInterface.kt"); } + + @Test + @TestMetadata("kt45796.kt") + public void testKt45796() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/kt45796.kt"); + } } @Nested diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/SealedClassInheritorsProvider.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/SealedClassInheritorsProvider.kt index 253e165ebd5..b73d6230fd3 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/SealedClassInheritorsProvider.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/SealedClassInheritorsProvider.kt @@ -8,9 +8,14 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.incremental.components.LookupLocation +import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.resolve.descriptorUtil.parents import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner +import org.jetbrains.kotlin.types.refinement.TypeRefinement +import org.jetbrains.kotlin.utils.addToStdlib.runIf abstract class SealedClassInheritorsProvider { /** @@ -26,6 +31,7 @@ object CliSealedClassInheritorsProvider : SealedClassInheritorsProvider() { // Note this is a generic and slow implementation which would work almost for any subclass of ClassDescriptor. // Please avoid using it in new code. // TODO: do something more clever instead at call sites of this function + @OptIn(TypeRefinement::class) override fun computeSealedSubclasses( sealedClass: ClassDescriptor, allowSealedInheritorsInDifferentFilesOfSamePackage: Boolean @@ -37,13 +43,21 @@ object CliSealedClassInheritorsProvider : SealedClassInheritorsProvider() { fun collectSubclasses(scope: MemberScope, collectNested: Boolean) { for (descriptor in scope.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS)) { if (descriptor !is ClassDescriptor) continue + /* + * scope.getContributedDescriptors() doesn't discriminate expect classes in presence + * of theirs actuals, so we need to lookup for descriptor once again via + * scope.getContributedClassifier() to match expects (if possible) + */ + val refinedDescriptor = runIf(descriptor.isExpect) { + scope.getContributedClassifier(descriptor.name, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS) as? ClassDescriptor + } ?: descriptor - if (DescriptorUtils.isDirectSubclass(descriptor, sealedClass)) { - result.add(descriptor) + if (DescriptorUtils.isDirectSubclass(refinedDescriptor, sealedClass)) { + result.add(refinedDescriptor) } if (collectNested) { - collectSubclasses(descriptor.unsubstitutedInnerClassesScope, collectNested) + collectSubclasses(refinedDescriptor.unsubstitutedInnerClassesScope, collectNested) } } }