Refine expect classes in CliSealedClassInheritorsProvider

KT-45796 Fixed
This commit is contained in:
Dmitriy Novozhilov
2021-04-05 12:11:10 +03:00
committed by TeamCityServer
parent 2ee8ac2e15
commit 1633190478
6 changed files with 140 additions and 3 deletions
@@ -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
@@ -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
}
}
@@ -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 <!EXPECT_TYPE_IN_WHEN_WITHOUT_ELSE, NO_ELSE_IN_WHEN!>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
}
}
@@ -0,0 +1,51 @@
// -- Module: <m1-common> --
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: <m1-jvm> --
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
}
}
}
@@ -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
@@ -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)
}
}
}