From ac203591e52181647f159d6988f858ab1b1a774b Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Tue, 24 Oct 2023 10:00:18 +0200 Subject: [PATCH] [FE, Java resolve] Support inheritors of sealed Java type without permits clause This fixes a false negative NO_ELSE_IN_WHEN in K2 and incidentally also fixes a false positive NO_ELSE_IN_WHEN in K1 since the fix is in the common code. #KT-62491 Fixed --- ...cCompilerTestFirTestdataTestGenerated.java | 6 ++ ...osticCompilerFirTestDataTestGenerated.java | 6 ++ ...otlinViaStaticImportWithoutPermits.fir.txt | 11 +++ ...FromKotlinViaStaticImportWithoutPermits.kt | 25 ++++++ .../FirLightTreeDiagnosticsTestGenerated.java | 6 ++ .../FirPsiDiagnosticTestGenerated.java | 6 ++ .../kotlin/fir/java/FirJavaFacade.kt | 2 +- .../javac/resolve/KotlinClassifiersCache.kt | 2 +- .../wrappers/symbols/FakeSymbolBasedClass.kt | 2 +- .../wrappers/symbols/SymbolBasedClass.kt | 4 +- .../javac/wrappers/trees/TreeBasedClass.kt | 4 +- .../load/java/structure/impl/JavaClassImpl.kt | 33 +++++++- .../impl/classFiles/BinaryJavaClass.kt | 8 +- .../javaSealedClassExhaustiveness.kt | 63 ++++++++++++++- .../javaSealedClassExhaustiveness.txt | 77 +++++++++++++++++++ .../javaSealedInterfaceExhaustiveness.kt | 19 +++++ .../javaSealedInterfaceExhaustiveness.txt | 23 ++++++ .../load/java/structure/javaElements.kt | 2 +- .../descriptors/LazyJavaClassDescriptor.kt | 2 +- .../SyntheticJavaClassDescriptor.kt | 4 +- .../runtime/structure/ReflectJavaClass.kt | 5 +- .../lombok/k2/java/dummyJavaElements.kt | 2 +- 22 files changed, 291 insertions(+), 21 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.kt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java index ab942875d20..3e7edeb4812 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFirTestdataTestGenerated.java @@ -45,6 +45,12 @@ public class DiagnosticCompilerTestFirTestdataTestGenerated extends AbstractDiag runTest("compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportAndPermits.kt"); } + @Test + @TestMetadata("accessJavaFromKotlinViaStaticImportWithoutPermits.kt") + public void testAccessJavaFromKotlinViaStaticImportWithoutPermits() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.kt"); + } + @Test public void testAllFilesPresentInResolve() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve"), Pattern.compile("^([^.]+)\\.kt$"), null, true); diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java index e0456dbfd58..fd63a1f2294 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated.java @@ -45,6 +45,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated runTest("compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportAndPermits.kt"); } + @Test + @TestMetadata("accessJavaFromKotlinViaStaticImportWithoutPermits.kt") + public void testAccessJavaFromKotlinViaStaticImportWithoutPermits() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.kt"); + } + @Test public void testAllFilesPresentInResolve() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve"), Pattern.compile("^([^.]+)\\.kt$"), null, true); diff --git a/compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.fir.txt b/compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.fir.txt new file mode 100644 index 00000000000..ad57860018b --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.fir.txt @@ -0,0 +1,11 @@ +FILE: useSite.kt + public final fun foo(): R|kotlin/Int| { + ^foo Int(4) + } +FILE: KotlinInterface.kt + public abstract interface KotlinInterface : R|kotlin/Any| { + public abstract var selectedOptions: R|kotlin/Int| + public get(): R|kotlin/Int| + public set(value: R|kotlin/Int|): R|kotlin/Unit| + + } diff --git a/compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.kt b/compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.kt new file mode 100644 index 00000000000..1cbe54adf5b --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.kt @@ -0,0 +1,25 @@ +// FILE: useSite.kt + +import C.StaticConfigurationClass.INIT_INSPECTIONS + +fun foo(): Int = 4 + +// FILE: InspectionProfileImpl.java +import static Configuration.StaticConfigurationClass + +public static final class C extends StaticConfigurationClass { + public abstract sealed class StaticConfigurationClass { + public static boolean INIT_INSPECTIONS; + } +} + +// FILE: Configuration.java +public class Configuration implements KotlinInterface { + public static class StaticConfigurationClass { + } +} + +// FILE: KotlinInterface.kt +interface KotlinInterface { + var selectedOptions: Int +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java index aa572e49e07..75bc92e9299 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeDiagnosticsTestGenerated.java @@ -45,6 +45,12 @@ public class FirLightTreeDiagnosticsTestGenerated extends AbstractFirLightTreeDi runTest("compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportAndPermits.kt"); } + @Test + @TestMetadata("accessJavaFromKotlinViaStaticImportWithoutPermits.kt") + public void testAccessJavaFromKotlinViaStaticImportWithoutPermits() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.kt"); + } + @Test public void testAllFilesPresentInResolve() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve"), Pattern.compile("^([^.]+)\\.kt$"), null, true); diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java index aff4944958c..8631c17192f 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiDiagnosticTestGenerated.java @@ -45,6 +45,12 @@ public class FirPsiDiagnosticTestGenerated extends AbstractFirPsiDiagnosticTest runTest("compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportAndPermits.kt"); } + @Test + @TestMetadata("accessJavaFromKotlinViaStaticImportWithoutPermits.kt") + public void testAccessJavaFromKotlinViaStaticImportWithoutPermits() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/accessJavaFromKotlinViaStaticImportWithoutPermits.kt"); + } + @Test public void testAllFilesPresentInResolve() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve"), Pattern.compile("^([^.]+)\\.kt$"), null, true); diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt index d187683bcdf..5cbcf07c409 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaFacade.kt @@ -387,7 +387,7 @@ abstract class FirJavaFacade( if (modality == Modality.SEALED) { val permittedTypes = javaClass.permittedTypes setSealedClassInheritors { - permittedTypes.mapNotNull { classifierType -> + permittedTypes.mapNotNullTo(mutableListOf()) { classifierType -> val classifier = classifierType.classifier as? JavaClass classifier?.let { JavaToKotlinClassMap.mapJavaToKotlin(it.fqName!!) ?: it.classId } } diff --git a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/resolve/KotlinClassifiersCache.kt b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/resolve/KotlinClassifiersCache.kt index 3326f3a68ba..3ebf33e287c 100644 --- a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/resolve/KotlinClassifiersCache.kt +++ b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/resolve/KotlinClassifiersCache.kt @@ -162,7 +162,7 @@ class MockKotlinClassifier(override val classId: ClassId, override val isEnum get() = shouldNotBeCalled() override val isRecord get() = shouldNotBeCalled() override val isSealed: Boolean get() = shouldNotBeCalled() - override val permittedTypes: Collection get() = shouldNotBeCalled() + override val permittedTypes: Sequence get() = shouldNotBeCalled() override val methods get() = shouldNotBeCalled() override val fields get() = shouldNotBeCalled() override val constructors get() = shouldNotBeCalled() diff --git a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/FakeSymbolBasedClass.kt b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/FakeSymbolBasedClass.kt index a59c094bfe7..ab8e0b1fff5 100644 --- a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/FakeSymbolBasedClass.kt +++ b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/FakeSymbolBasedClass.kt @@ -71,7 +71,7 @@ class FakeSymbolBasedClass( override val isSealed: Boolean get() = false - override val permittedTypes: Collection get() = emptyList() + override val permittedTypes: Sequence get() = emptySequence() override val lightClassOriginKind: LightClassOriginKind? get() = null diff --git a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/SymbolBasedClass.kt b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/SymbolBasedClass.kt index 16dddafab71..d1aeac8787f 100644 --- a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/SymbolBasedClass.kt +++ b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/symbols/SymbolBasedClass.kt @@ -111,8 +111,8 @@ class SymbolBasedClass( override val isSealed: Boolean get() = false - override val permittedTypes: Collection - get() = emptyList() + override val permittedTypes: Sequence + get() = emptySequence() override val lightClassOriginKind: LightClassOriginKind? get() = null diff --git a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/TreeBasedClass.kt b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/TreeBasedClass.kt index de034c743aa..ee3c709d4bb 100644 --- a/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/TreeBasedClass.kt +++ b/compiler/javac-wrapper/src/org/jetbrains/kotlin/javac/wrappers/trees/TreeBasedClass.kt @@ -125,8 +125,8 @@ class TreeBasedClass( override val isSealed: Boolean get() = false - override val permittedTypes: Collection - get() = emptyList() + override val permittedTypes: Sequence + get() = emptySequence() override val lightClassOriginKind: LightClassOriginKind? get() = null diff --git a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/JavaClassImpl.kt b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/JavaClassImpl.kt index d3ff5de7b67..3401c54ee06 100644 --- a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/JavaClassImpl.kt +++ b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/JavaClassImpl.kt @@ -19,7 +19,9 @@ package org.jetbrains.kotlin.load.java.structure.impl import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElementFactory import com.intellij.psi.PsiTypeParameter +import com.intellij.psi.SyntaxTraverser import com.intellij.psi.search.SearchScope import org.jetbrains.kotlin.asJava.KtLightClassMarker import org.jetbrains.kotlin.asJava.isSyntheticValuesOrValueOfMethod @@ -68,9 +70,18 @@ class JavaClassImpl(psiClassSource: JavaElementPsiSource) : JavaClassi override val isSealed: Boolean get() = JavaElementUtil.isSealed(this) - override val permittedTypes: Collection - get() = psi.permitsListTypes.convertIndexed { index, _ -> - JavaClassifierTypeImpl(sourceFactory.createPermittedTypeSource(psiElementSource, index)) + override val permittedTypes: Sequence + get() { + if (!isSealed) return emptySequence() + + val permitsListTypes = psi.permitsListTypes + return if (permitsListTypes.isNotEmpty()) { + permitsListTypes.convertIndexed { index, _ -> + JavaClassifierTypeImpl(sourceFactory.createPermittedTypeSource(psiElementSource, index)) + }.asSequence() + } else { + lazilyComputePermittedTypesInSameFile(psiElementSource) + } } override val isRecord: Boolean @@ -163,3 +174,19 @@ class JavaClassImpl(psiClassSource: JavaElementPsiSource) : JavaClassi private val LOGGER = Logger.getInstance(JavaClassImpl::class.java) } } + +private fun lazilyComputePermittedTypesInSameFile(psiElementSource: JavaElementPsiSource): Sequence { + return Sequence { + // Don't capture PSI directly but only via psiElementSource + val psi = psiElementSource.psi + val elementFactory = PsiElementFactory.getInstance(psi.project) + + SyntaxTraverser.psiTraverser(psi.containingFile) + .filter(PsiClass::class.java) + // isInheritor can lead to resolution which can cause contract violations, + // that's why we compute it lazily only when the sequence is iterated. + .filter { it.isInheritor(psi, /* checkDeep: */ false) } + .map { JavaClassifierTypeImpl(psiElementSource.factory.createTypeSource(elementFactory.createType(it))) } + .iterator() + } +} \ No newline at end of file diff --git a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt index e3ba4ba56d7..598651e8efa 100644 --- a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt +++ b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt @@ -79,8 +79,10 @@ class BinaryJavaClass( override val lightClassOriginKind: LightClassOriginKind? get() = null - override val isSealed: Boolean get() = permittedTypes.isNotEmpty() - override val permittedTypes = arrayListOf() + private val permittedTypesList: ArrayList = arrayListOf() + override val isSealed: Boolean get() = permittedTypesList.isNotEmpty() + override val permittedTypes: Sequence + get() = permittedTypesList.asSequence() override fun isFromSourceCodeInScope(scope: SearchScope): Boolean = false @@ -258,6 +260,6 @@ class BinaryJavaClass( } override fun visitPermittedSubclass(permittedSubclass: String?) { - permittedTypes.addIfNotNull(permittedSubclass?.convertInternalNameToClassifierType()) + permittedTypesList.addIfNotNull(permittedSubclass?.convertInternalNameToClassifierType()) } } diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedClassExhaustiveness.kt b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedClassExhaustiveness.kt index e7972fb507b..a7ba2c76aaf 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedClassExhaustiveness.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedClassExhaustiveness.kt @@ -10,11 +10,29 @@ public final class A extends Base {} // FILE: B.java public sealed class B extends Base permits B.C, B.D { - public static final class C implements B {} + public static final class C extends B {} public static non-sealed class D extends B {} } +// FILE: SameFile.java +public sealed class SameFile { + public static final class A extends SameFile {} + public static sealed class B extends SameFile { + public static final class C extends B {} + public static non-sealed class D extends B {} + } +} + +// FILE: SameFileNonSealed.java +public class SameFileNonSealed { + public static final class A extends SameFileNonSealed {} + public static class B extends SameFileNonSealed { + public static final class C extends B {} + public static class D extends B {} + } +} + // FILE: main.kt fun test_ok_1(base: Base) { val x = when (base) { @@ -31,6 +49,21 @@ fun test_ok_2(base: Base) { } } +fun test_ok_3(sameFile: SameFile) { + val x = when (sameFile) { + is SameFile.A -> 1 + is SameFile.B -> 2 + } +} + +fun test_ok_4(sameFile: SameFile) { + val x = when (sameFile) { + is SameFile.A -> 1 + is SameFile.B.C -> 2 + is SameFile.B.D -> 3 + } +} + fun test_error_1(base: Base) { val x = when (base) { is A -> 1 @@ -43,3 +76,31 @@ fun test_error_2(base: Base) { is B.C -> 2 } } + +fun test_error_3(sameFile: SameFile) { + val x = when (sameFile) { + is SameFile.A -> 1 + } +} + +fun test_error_4(sameFile: SameFile) { + val x = when (sameFile) { + is SameFile.A -> 1 + is SameFile.B.C -> 2 + } +} + +fun test_error_5(sameFile: SameFileNonSealed) { + val x = when (sameFile) { + is SameFileNonSealed.A -> 1 + is SameFileNonSealed.B -> 2 + } +} + +fun test_error_6(sameFile: SameFileNonSealed) { + val x = when (sameFile) { + is SameFileNonSealed.A -> 1 + is SameFileNonSealed.B.C -> 2 + is SameFileNonSealed.B.D -> 2 + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedClassExhaustiveness.txt b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedClassExhaustiveness.txt index 8d90f4c6fcf..e0046aeb22a 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedClassExhaustiveness.txt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedClassExhaustiveness.txt @@ -2,8 +2,14 @@ package public fun test_error_1(/*0*/ base: Base): kotlin.Unit public fun test_error_2(/*0*/ base: Base): kotlin.Unit +public fun test_error_3(/*0*/ sameFile: SameFile): kotlin.Unit +public fun test_error_4(/*0*/ sameFile: SameFile): kotlin.Unit +public fun test_error_5(/*0*/ sameFile: SameFileNonSealed): kotlin.Unit +public fun test_error_6(/*0*/ sameFile: SameFileNonSealed): kotlin.Unit public fun test_ok_1(/*0*/ base: Base): kotlin.Unit public fun test_ok_2(/*0*/ base: Base): kotlin.Unit +public fun test_ok_3(/*0*/ sameFile: SameFile): kotlin.Unit +public fun test_ok_4(/*0*/ sameFile: SameFile): kotlin.Unit public final class A : Base { public constructor A() @@ -39,3 +45,74 @@ public sealed class Base { public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + +public sealed class SameFile { + public constructor SameFile() + 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 class A : SameFile { + public constructor A() + 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 sealed class B : SameFile { + public constructor B() + 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 class C : SameFile.B { + public constructor C() + 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 open class D : SameFile.B { + public constructor D() + 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 open class SameFileNonSealed { + public constructor SameFileNonSealed() + 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 class A : SameFileNonSealed { + public constructor A() + 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 open class B : SameFileNonSealed { + public constructor B() + 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 class C : SameFileNonSealed.B { + public constructor C() + 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 open class D : SameFileNonSealed.B { + public constructor D() + 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/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedInterfaceExhaustiveness.kt b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedInterfaceExhaustiveness.kt index 4cc7b5e56b5..69ead003c38 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedInterfaceExhaustiveness.kt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedInterfaceExhaustiveness.kt @@ -20,6 +20,12 @@ public enum E implements Base { First, Second } +// FILE: SameFile.java +public sealed interface SameFile { + public static final class A implements SameFile {} + public static non-sealed class B implements SameFile {} +} + // FILE: main.kt fun test_ok_1(base: Base) { val x = when (base) { @@ -39,6 +45,13 @@ fun test_ok_2(base: Base) { } } +fun test_ok_3(sameFile: SameFile) { + val x = when (sameFile) { + is SameFile.A -> 1 + is SameFile.B -> 2 + } +} + fun test_error_1(base: Base) { val x = when (base) { is A -> 1 @@ -63,3 +76,9 @@ fun test_error_3(base: Base) { E.Second -> 5 } } + +fun test_error_4(sameFile: SameFile) { + val x = when (sameFile) { + is SameFile.A -> 1 + } +} diff --git a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedInterfaceExhaustiveness.txt b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedInterfaceExhaustiveness.txt index 4a7f1846bc3..1dbb37d2ed1 100644 --- a/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedInterfaceExhaustiveness.txt +++ b/compiler/testData/diagnostics/tests/testsWithJava17/sealedClasses/javaSealedInterfaceExhaustiveness.txt @@ -3,8 +3,10 @@ package public fun test_error_1(/*0*/ base: Base): kotlin.Unit public fun test_error_2(/*0*/ base: Base): kotlin.Unit public fun test_error_3(/*0*/ base: Base): kotlin.Unit +public fun test_error_4(/*0*/ sameFile: SameFile): kotlin.Unit public fun test_ok_1(/*0*/ base: Base): kotlin.Unit public fun test_ok_2(/*0*/ base: Base): kotlin.Unit +public fun test_ok_3(/*0*/ sameFile: SameFile): kotlin.Unit public interface A : Base { public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean @@ -59,3 +61,24 @@ public final enum class E : kotlin.Enum, Base { public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): E public final /*synthesized*/ fun values(): kotlin.Array } + +public sealed interface SameFile { + 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 class A : SameFile { + public constructor A() + 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 open class B : SameFile { + public constructor B() + 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/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt index 4d09bf287b0..cbd0058b3f9 100644 --- a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt +++ b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/structure/javaElements.kt @@ -102,7 +102,7 @@ interface JavaClass : JavaClassifier, JavaTypeParameterListOwner, JavaModifierLi val isEnum: Boolean val isRecord: Boolean val isSealed: Boolean - val permittedTypes: Collection + val permittedTypes: Sequence val lightClassOriginKind: LightClassOriginKind? val methods: Collection diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt index 1640e7e5674..f3412ad98c4 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassDescriptor.kt @@ -197,7 +197,7 @@ class LazyJavaClassDescriptor( override fun getSealedSubclasses(): Collection = if (modality == Modality.SEALED) { val attributes = TypeUsage.COMMON.toAttributes() - jClass.permittedTypes.mapNotNull { + jClass.permittedTypes.mapNotNullTo(mutableListOf()) { c.typeResolver.transformJavaType(it, attributes).constructor.declarationDescriptor as? ClassDescriptor }.sortedBy { it.fqNameSafe.asString() } } else { diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/SyntheticJavaClassDescriptor.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/SyntheticJavaClassDescriptor.kt index 22a65668086..1b64c6640bf 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/SyntheticJavaClassDescriptor.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/lazy/descriptors/SyntheticJavaClassDescriptor.kt @@ -193,8 +193,8 @@ class SyntheticJavaClassDescriptor( get() = this@SyntheticJavaClassDescriptor.isRecord override val isSealed: Boolean get() = modality == Modality.SEALED - override val permittedTypes: Collection - get() = emptyList() + override val permittedTypes: Sequence + get() = emptySequence() override val lightClassOriginKind: LightClassOriginKind? get() = null override val methods: Collection diff --git a/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/structure/ReflectJavaClass.kt b/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/structure/ReflectJavaClass.kt index b590ba6d84c..ed455f89732 100644 --- a/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/structure/ReflectJavaClass.kt +++ b/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/structure/ReflectJavaClass.kt @@ -133,10 +133,11 @@ class ReflectJavaClass( override val isSealed: Boolean get() = Java16SealedRecordLoader.loadIsSealed(klass) ?: false - override val permittedTypes: Collection + override val permittedTypes: Sequence get() = Java16SealedRecordLoader.loadGetPermittedSubclasses(klass) ?.map(::ReflectJavaClassifierType) - ?: emptyList() + ?.asSequence() + ?: emptySequence() override fun equals(other: Any?) = other is ReflectJavaClass && klass == other.klass diff --git a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/java/dummyJavaElements.kt b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/java/dummyJavaElements.kt index 95ffd311370..1be3dc3f8b3 100644 --- a/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/java/dummyJavaElements.kt +++ b/plugins/lombok/lombok.k2/src/org/jetbrains/kotlin/lombok/k2/java/dummyJavaElements.kt @@ -75,7 +75,7 @@ class DummyJavaClass(name: String, override val fqName: FqName, numberOfTypePara get() = shouldNotBeCalled() override val isSealed: Boolean get() = shouldNotBeCalled() - override val permittedTypes: Collection + override val permittedTypes: Sequence get() = shouldNotBeCalled() override val lightClassOriginKind: LightClassOriginKind? get() = shouldNotBeCalled()