From 57a081c3990a4518e11efa63626bcbc471b64b0b Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Tue, 24 Nov 2020 14:42:10 +0300 Subject: [PATCH] [FE] Prohibit inheritance of sealed classes in different module KT-20423 --- ...irOldFrontendDiagnosticsTestGenerated.java | 10 ++++++ .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../resolve/PlatformConfiguratorBase.kt | 1 + .../SealedInheritorInSameModuleChecker.kt | 30 +++++++++++++++++ .../library/base.kt | 7 ++++ .../sealedInheritorInDifferentModule/main.kt | 5 +++ .../output.txt | 20 +++++++++++ .../sealed/inheritorInDifferentModule.fir.kt | 17 ++++++++++ .../sealed/inheritorInDifferentModule.kt | 17 ++++++++++ .../sealed/inheritorInDifferentModule.txt | 33 +++++++++++++++++++ .../inheritorInDifferentModule.fir.kt | 17 ++++++++++ .../interfaces/inheritorInDifferentModule.kt | 17 ++++++++++ .../interfaces/inheritorInDifferentModule.txt | 30 +++++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 10 ++++++ .../DiagnosticsUsingJavacTestGenerated.java | 10 ++++++ .../CompileKotlinAgainstCustomBinariesTest.kt | 6 ++++ 17 files changed, 232 insertions(+) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/SealedInheritorInSameModuleChecker.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/library/base.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/main.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/output.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.fir.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.txt create mode 100644 compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.fir.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.kt create mode 100644 compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.txt diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index 16395f5f8a4..5eadec45ee5 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -20773,6 +20773,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.kt"); } + @TestMetadata("inheritorInDifferentModule.kt") + public void testInheritorInDifferentModule() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.kt"); + } + @TestMetadata("Local.kt") public void testLocal() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/Local.kt"); @@ -20910,6 +20915,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/sealed/interfaces"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("inheritorInDifferentModule.kt") + public void testInheritorInDifferentModule() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.kt"); + } + @TestMetadata("sealedInterfacesDisabled.kt") public void testSealedInterfacesDisabled() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 7daa75e5199..3fcbcc93663 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -427,6 +427,7 @@ public interface Errors { DiagnosticFactory0 SEALED_SUPERTYPE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 SEALED_SUPERTYPE_IN_LOCAL_CLASS = DiagnosticFactory0.create(ERROR); DiagnosticFactory2 SEALED_INHERITOR_IN_DIFFERENT_PACKAGE = DiagnosticFactory2.create(ERROR); + DiagnosticFactory0 SEALED_INHERITOR_IN_DIFFERENT_MODULE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 CLASS_INHERITS_JAVA_SEALED_CLASS = DiagnosticFactory0.create(ERROR); // Companion objects diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 330b13fc9ed..254ffeef805 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -634,6 +634,7 @@ public class DefaultErrorMessages { MAP.put(SEALED_SUPERTYPE, "This type is sealed, so it can be inherited by only its own nested classes or objects"); MAP.put(SEALED_SUPERTYPE_IN_LOCAL_CLASS, "Local class cannot extend a sealed class"); MAP.put(SEALED_INHERITOR_IN_DIFFERENT_PACKAGE, "Inheritor of sealed class or interface declared in package {1} but it must be in package {2} where base class is declared", TO_STRING, TO_STRING); + MAP.put(SEALED_INHERITOR_IN_DIFFERENT_MODULE, "Inheritance of sealed classes or interfaces from different module is prohibited"); MAP.put(CLASS_INHERITS_JAVA_SEALED_CLASS, "Inheritance of java sealed classes is prohibited"); MAP.put(SINGLETON_IN_SUPERTYPE, "Cannot inherit from a singleton"); MAP.put(CLASS_CANNOT_BE_EXTENDED_DIRECTLY, "Class {0} cannot be extended directly", NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index 43734e9792a..746e376885f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -42,6 +42,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf( ContractDescriptionBlockChecker, PrivateInlineFunctionsReturningAnonymousObjectsChecker, SealedInheritorInSamePackageChecker, + SealedInheritorInSameModuleChecker, SealedInterfaceAllowedChecker, ) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/SealedInheritorInSameModuleChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/SealedInheritorInSameModuleChecker.kt new file mode 100644 index 00000000000..1e0d55233ad --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/SealedInheritorInSameModuleChecker.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.checkers + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.isSealed +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType +import org.jetbrains.kotlin.resolve.descriptorUtil.module + +object SealedInheritorInSameModuleChecker : DeclarationChecker { + override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { + if (descriptor !is ClassDescriptor || declaration !is KtClassOrObject) return + val currentModule = descriptor.module + for (superTypeListEntry in declaration.superTypeListEntries) { + val typeReference = superTypeListEntry.typeReference ?: continue + val superType = typeReference.getAbbreviatedTypeOrType(context.trace.bindingContext)?.unwrap() ?: continue + val superClass = superType.constructor.declarationDescriptor ?: continue + if (superClass.isSealed() && superClass.module != currentModule) { + context.trace.report(Errors.SEALED_INHERITOR_IN_DIFFERENT_MODULE.on(typeReference)) + } + } + } +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/library/base.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/library/base.kt new file mode 100644 index 00000000000..54b4f44da1b --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/library/base.kt @@ -0,0 +1,7 @@ +package a + +sealed class Base + +sealed interface IBase + +class A : Base(), IBase diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/main.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/main.kt new file mode 100644 index 00000000000..8558dd31d47 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/main.kt @@ -0,0 +1,5 @@ +// ISSUE: KT-20423 + +package a + +class B : Base(), IBase diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/output.txt new file mode 100644 index 00000000000..4f0b6da9911 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/output.txt @@ -0,0 +1,20 @@ +warning: ATTENTION! +This build uses unsafe internal compiler arguments: + +-XXLanguage:+FreedomForSealedClasses +-XXLanguage:+SealedInterfaces + +This mode is not recommended for production use, +as no stability/compatibility guarantees are given on +compiler or generated code. Use it at your own risk! + +compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/main.kt:5:11: error: cannot access '': it is internal in 'Base' +class B : Base(), IBase + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/main.kt:5:11: error: inheritance of sealed classes or interfaces from different module is prohibited +class B : Base(), IBase + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/sealedInheritorInDifferentModule/main.kt:5:19: error: inheritance of sealed classes or interfaces from different module is prohibited +class B : Base(), IBase + ^ +COMPILATION_ERROR diff --git a/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.fir.kt b/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.fir.kt new file mode 100644 index 00000000000..4587f2142b4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.fir.kt @@ -0,0 +1,17 @@ +// ISSUE: KT-20423 +// !LANGUAGE: +SealedInterfaces +FreedomForSealedClasses + +// MODULE: m1 +// FILE: a.kt +package a + +sealed class Base + +class A : Base() + +// MODULE: m2(m1) +// FILE: b.kt + +package a + +class B : Base() diff --git a/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.kt b/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.kt new file mode 100644 index 00000000000..44736417247 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.kt @@ -0,0 +1,17 @@ +// ISSUE: KT-20423 +// !LANGUAGE: +SealedInterfaces +FreedomForSealedClasses + +// MODULE: m1 +// FILE: a.kt +package a + +sealed class Base + +class A : Base() + +// MODULE: m2(m1) +// FILE: b.kt + +package a + +class B : Base() diff --git a/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.txt b/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.txt new file mode 100644 index 00000000000..f6c493cc02d --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.txt @@ -0,0 +1,33 @@ +// -- Module: -- +package + +package a { + + public final class A : a.Base { + 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 Base { + internal constructor Base() + 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 + +package a { + + public final class B : a.Base { + 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/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.fir.kt b/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.fir.kt new file mode 100644 index 00000000000..6ef6ef701ac --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.fir.kt @@ -0,0 +1,17 @@ +// ISSUE: KT-20423 +// !LANGUAGE: +SealedInterfaces +FreedomForSealedClasses + +// MODULE: m1 +// FILE: a.kt +package a + +sealed interface Base + +interface A : Base + +// MODULE: m2(m1) +// FILE: b.kt + +package a + +interface B : Base diff --git a/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.kt b/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.kt new file mode 100644 index 00000000000..4074595d4c3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.kt @@ -0,0 +1,17 @@ +// ISSUE: KT-20423 +// !LANGUAGE: +SealedInterfaces +FreedomForSealedClasses + +// MODULE: m1 +// FILE: a.kt +package a + +sealed interface Base + +interface A : Base + +// MODULE: m2(m1) +// FILE: b.kt + +package a + +interface B : Base diff --git a/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.txt b/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.txt new file mode 100644 index 00000000000..8d6d9f86841 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.txt @@ -0,0 +1,30 @@ +// -- Module: -- +package + +package a { + + public interface A : a.Base { + 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 interface Base { + 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 + +package a { + + public interface B : a.Base { + 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-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 04fa8c6fbbc..c2d9b7057ba 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -20850,6 +20850,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali runTest("compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.kt"); } + @TestMetadata("inheritorInDifferentModule.kt") + public void testInheritorInDifferentModule() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.kt"); + } + @TestMetadata("Local.kt") public void testLocal() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/Local.kt"); @@ -20987,6 +20992,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/sealed/interfaces"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("inheritorInDifferentModule.kt") + public void testInheritorInDifferentModule() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.kt"); + } + @TestMetadata("sealedInterfacesDisabled.kt") public void testSealedInterfacesDisabled() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.kt"); diff --git a/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 44bf9026e50..c48401eb6d1 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -20775,6 +20775,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/sealed/ExhaustiveWithFreedom.kt"); } + @TestMetadata("inheritorInDifferentModule.kt") + public void testInheritorInDifferentModule() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/inheritorInDifferentModule.kt"); + } + @TestMetadata("Local.kt") public void testLocal() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/Local.kt"); @@ -20912,6 +20917,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/sealed/interfaces"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @TestMetadata("inheritorInDifferentModule.kt") + public void testInheritorInDifferentModule() throws Exception { + runTest("compiler/testData/diagnostics/tests/sealed/interfaces/inheritorInDifferentModule.kt"); + } + @TestMetadata("sealedInterfacesDisabled.kt") public void testSealedInterfacesDisabled() throws Exception { runTest("compiler/testData/diagnostics/tests/sealed/interfaces/sealedInterfacesDisabled.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt index 64626e8ce3c..c72ba064348 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt @@ -700,6 +700,12 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration compileKotlin("main.kt", tmpdir, listOf(library), additionalOptions = features) } + fun testSealedInheritorInDifferentModule() { + val features = listOf("-XXLanguage:+FreedomForSealedClasses", "-XXLanguage:+SealedInterfaces") + val library = compileLibrary("library", additionalOptions = features, checkKotlinOutput = {}) + compileKotlin("main.kt", tmpdir, listOf(library), additionalOptions = features) + } + // If this test fails, then bootstrap compiler most likely should be advanced fun testPreReleaseFlagIsConsistentBetweenBootstrapAndCurrentCompiler() { val bootstrapCompiler = JarFile(PathUtil.kotlinPathsForCompiler.compilerPath)