Distinguish classes from different sourcesets in SealedInheritorInSameModuleChecker
#KT-45848 Fixed #KT-46031
This commit is contained in:
committed by
TeamCityServer
parent
66e4d5664a
commit
803d47daaa
@@ -62,8 +62,9 @@ class K2MetadataCompiler : CLICompiler<K2MetadataCompilerArguments>() {
|
|||||||
val pluginLoadResult = loadPlugins(paths, arguments, configuration)
|
val pluginLoadResult = loadPlugins(paths, arguments, configuration)
|
||||||
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
|
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
|
||||||
|
|
||||||
|
val commonSources = arguments.commonSources?.toSet() ?: emptySet()
|
||||||
for (arg in arguments.freeArgs) {
|
for (arg in arguments.freeArgs) {
|
||||||
configuration.addKotlinSourceRoot(arg, isCommon = true)
|
configuration.addKotlinSourceRoot(arg, isCommon = arg in commonSources)
|
||||||
}
|
}
|
||||||
if (arguments.classpath != null) {
|
if (arguments.classpath != null) {
|
||||||
configuration.addJvmClasspathRoots(arguments.classpath!!.split(File.pathSeparatorChar).map(::File))
|
configuration.addJvmClasspathRoots(arguments.classpath!!.split(File.pathSeparatorChar).map(::File))
|
||||||
|
|||||||
+6
@@ -18118,6 +18118,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("commonSealedWithPlatformInheritor.kt")
|
||||||
|
public void testCommonSealedWithPlatformInheritor() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/commonSealedWithPlatformInheritor.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("expectEnum.kt")
|
@TestMetadata("expectEnum.kt")
|
||||||
public void testExpectEnum() throws Exception {
|
public void testExpectEnum() throws Exception {
|
||||||
|
|||||||
+40
-3
@@ -6,14 +6,17 @@
|
|||||||
package org.jetbrains.kotlin.resolve.checkers
|
package org.jetbrains.kotlin.resolve.checkers
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
|
||||||
import org.jetbrains.kotlin.descriptors.isSealed
|
import org.jetbrains.kotlin.descriptors.isSealed
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
|
import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource
|
||||||
|
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
|
||||||
|
|
||||||
object SealedInheritorInSameModuleChecker : DeclarationChecker {
|
object SealedInheritorInSameModuleChecker : DeclarationChecker {
|
||||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||||
@@ -24,16 +27,50 @@ object SealedInheritorInSameModuleChecker : DeclarationChecker {
|
|||||||
val superType = typeReference.getAbbreviatedTypeOrType(context.trace.bindingContext)?.unwrap() ?: continue
|
val superType = typeReference.getAbbreviatedTypeOrType(context.trace.bindingContext)?.unwrap() ?: continue
|
||||||
val superClass = superType.constructor.declarationDescriptor ?: continue
|
val superClass = superType.constructor.declarationDescriptor ?: continue
|
||||||
if (superClass.isSealed()) {
|
if (superClass.isSealed()) {
|
||||||
|
/*
|
||||||
|
* If this condition is true then we compile code from CLI mode and class came from
|
||||||
|
* common source set (in CLI mode we have single ModuleDescriptor for all MPP modules)
|
||||||
|
*
|
||||||
|
* We don't want to analyze classes from common modules, because there are problems with determining
|
||||||
|
* relation between module of class and module of super class (it's allowed to declare sealed
|
||||||
|
* inheritors between `expect` class declaration and its actual), so we assume that incorrect
|
||||||
|
* declarations will be reported while compiling common module to get metadata
|
||||||
|
*
|
||||||
|
* common expect sealed class Base
|
||||||
|
* | class A : Base() // OK, A in same module with Base
|
||||||
|
* |
|
||||||
|
* jvm-js-native class B : Base() // OK, B inherits `expect` class, not `actual`
|
||||||
|
* |
|
||||||
|
* jvm-js actual sealed class Base
|
||||||
|
* | class C : Base() // OK, C in same module with actual Base
|
||||||
|
* |
|
||||||
|
* jvm-js-2 class D : Base() // Error, D not in same module with actual Base
|
||||||
|
* |
|
||||||
|
* jvm class E : Base() // Error, E not in same module with actual Base
|
||||||
|
*
|
||||||
|
* In this hierarchy error on `D` will be reported in process of compiling `jvm-js-2` module in metadata mode
|
||||||
|
* (so `jvm-js-2` assumed as "platform" module and others are "common") and won't be reported during
|
||||||
|
* compilation of `jvm`.
|
||||||
|
*
|
||||||
|
* There is one problem with this condition: if modules hierarchy is a bamboo (no modules with two children)
|
||||||
|
* then we won't compile intermediate modules in metadata mode, only the last one, so error on class `D`
|
||||||
|
* won't be reported. See KT-46031
|
||||||
|
*/
|
||||||
|
if (descriptor.isFromCommonSource && superClass.module == currentModule) {
|
||||||
|
return
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* It's allowed to declare inheritors of expect sealed class in any dependent module until actual
|
* It's allowed to declare inheritors of expect sealed class in any dependent module until actual
|
||||||
* counterpart for this class will be declared. So if super class is resolved to expect sealed
|
* counterpart for this class will be declared. So if super class is resolved to expect sealed
|
||||||
* class then its allowed to declare inheritor
|
* class then its allowed to declare inheritor
|
||||||
*/
|
*/
|
||||||
val inheritorAllowed = superClass.isExpect || superClass.module == currentModule
|
if (!(superClass.isExpect || (superClass.module == currentModule && !superClass.isFromCommonSource))) {
|
||||||
if (!inheritorAllowed) {
|
|
||||||
context.trace.report(Errors.SEALED_INHERITOR_IN_DIFFERENT_MODULE.on(typeReference))
|
context.trace.report(Errors.SEALED_INHERITOR_IN_DIFFERENT_MODULE.on(typeReference))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val ClassifierDescriptor.isFromCommonSource: Boolean
|
||||||
|
get() = ((this.source.containingFile as? PsiSourceFile)?.psiFile as? KtFile)?.isCommonSource ?: false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
$TESTDATA_DIR$/inheritorOfExpectSealedClass/common-1.kt
|
||||||
|
$TESTDATA_DIR$/inheritorOfExpectSealedClass/common-2.kt
|
||||||
|
$TESTDATA_DIR$/inheritorOfExpectSealedClass/common-3.kt
|
||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xcommon-sources=$TESTDATA_DIR$/inheritorOfExpectSealedClass/common-1.kt,$TESTDATA_DIR$/inheritorOfExpectSealedClass/common-2.kt
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
compiler/testData/cli/metadata/inheritorOfExpectSealedClass/common-3.kt:1:17: error: inheritance of sealed classes or interfaces from different module is prohibited
|
||||||
|
class Derived : Base()
|
||||||
|
^
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
expect sealed class Base
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
actual sealed class Base
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
class Derived : Base()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
$TESTDATA_DIR$/optionalExpectationUsage.kt
|
||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-cp
|
||||||
|
$TESTDATA_DIR$/../../../../dist/common/kotlin-stdlib-common.jar
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
@kotlin.ExperimentalMultiplatform
|
||||||
|
@kotlin.OptionalExpectation
|
||||||
|
expect annotation class Ann()
|
||||||
|
|
||||||
|
@Ann
|
||||||
|
@kotlin.ExperimentalMultiplatform
|
||||||
|
fun foo() {}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
// !LANGUAGE: +MultiPlatformProjects
|
||||||
|
// ISSUE: KT-45848
|
||||||
|
// MODULE: m1-common
|
||||||
|
|
||||||
|
sealed class Base
|
||||||
|
|
||||||
|
class Derived : Base()
|
||||||
|
|
||||||
|
fun test_1(b: Base) = when (b) {
|
||||||
|
is Derived -> 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODULE: m1-jvm(m1-common)
|
||||||
|
|
||||||
|
class PlatfromDerived : Base() // must be an error
|
||||||
|
|
||||||
|
fun test_2(b: Base) = when (b) {
|
||||||
|
is Derived -> 1
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
// !LANGUAGE: +MultiPlatformProjects
|
||||||
|
// ISSUE: KT-45848
|
||||||
|
// MODULE: m1-common
|
||||||
|
|
||||||
|
sealed class Base
|
||||||
|
|
||||||
|
class Derived : Base()
|
||||||
|
|
||||||
|
fun test_1(b: Base) = <!NO_ELSE_IN_WHEN{JVM}!>when<!> (b) {
|
||||||
|
is Derived -> 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODULE: m1-jvm(m1-common)
|
||||||
|
|
||||||
|
class PlatfromDerived : <!SEALED_INHERITOR_IN_DIFFERENT_MODULE!>Base<!>() // must be an error
|
||||||
|
|
||||||
|
fun test_2(b: Base) = <!NO_ELSE_IN_WHEN!>when<!> (b) {
|
||||||
|
is Derived -> 1
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
// -- Module: <m1-common> --
|
||||||
|
package
|
||||||
|
|
||||||
|
public fun test_1(/*0*/ b: Base): kotlin.Int
|
||||||
|
|
||||||
|
public sealed class Base {
|
||||||
|
protected 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
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Derived : Base {
|
||||||
|
public constructor Derived()
|
||||||
|
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 test_1(/*0*/ b: Base): kotlin.Int
|
||||||
|
public fun test_2(/*0*/ b: Base): kotlin.Int
|
||||||
|
|
||||||
|
public sealed class Base {
|
||||||
|
protected 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
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Derived : Base {
|
||||||
|
public constructor Derived()
|
||||||
|
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 PlatfromDerived : Base {
|
||||||
|
public constructor PlatfromDerived()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
Generated
+6
@@ -18124,6 +18124,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("commonSealedWithPlatformInheritor.kt")
|
||||||
|
public void testCommonSealedWithPlatformInheritor() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/multiplatform/exhaustiveness/commonSealedWithPlatformInheritor.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("expectEnum.kt")
|
@TestMetadata("expectEnum.kt")
|
||||||
public void testExpectEnum() throws Exception {
|
public void testExpectEnum() throws Exception {
|
||||||
|
|||||||
@@ -1195,6 +1195,11 @@ public class CliTestGenerated extends AbstractCliTest {
|
|||||||
runTest("compiler/testData/cli/metadata/anonymousObjectType.args");
|
runTest("compiler/testData/cli/metadata/anonymousObjectType.args");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inheritorOfExpectSealedClass.args")
|
||||||
|
public void testInheritorOfExpectSealedClass() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/metadata/inheritorOfExpectSealedClass.args");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("kotlinPackage.args")
|
@TestMetadata("kotlinPackage.args")
|
||||||
public void testKotlinPackage() throws Exception {
|
public void testKotlinPackage() throws Exception {
|
||||||
runTest("compiler/testData/cli/metadata/kotlinPackage.args");
|
runTest("compiler/testData/cli/metadata/kotlinPackage.args");
|
||||||
@@ -1204,5 +1209,10 @@ public class CliTestGenerated extends AbstractCliTest {
|
|||||||
public void testModuleName() throws Exception {
|
public void testModuleName() throws Exception {
|
||||||
runTest("compiler/testData/cli/metadata/moduleName.args");
|
runTest("compiler/testData/cli/metadata/moduleName.args");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("optionalExpectationUsage.args")
|
||||||
|
public void testOptionalExpectationUsage() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/metadata/optionalExpectationUsage.args");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// ISSUE: KT-45848
|
||||||
|
|
||||||
|
sealed class <!LINE_MARKER("descr='Is subclassed by Derived PlatfromDerived'")!>Base<!>
|
||||||
|
|
||||||
|
class Derived : Base()
|
||||||
|
|
||||||
|
fun test_1(b: Base) = <!NO_ELSE_IN_WHEN{JVM}!>when<!> (b) {
|
||||||
|
is Derived -> 1
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
MODULE common { platform=[JVM, JS, Native] }
|
||||||
|
MODULE jvm { platform=[JVM] }
|
||||||
|
|
||||||
|
jvm -> common { kind=DEPENDS_ON }
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class PlatfromDerived : <!SEALED_INHERITOR_IN_DIFFERENT_MODULE!>Base<!>() // must be an error
|
||||||
|
|
||||||
|
fun test_2(b: Base) = when (b) {
|
||||||
|
is Derived -> 1
|
||||||
|
}
|
||||||
Generated
+5
@@ -44,6 +44,11 @@ public class MultiplatformAnalysisTestGenerated extends AbstractMultiplatformAna
|
|||||||
runTest("idea/testData/multiplatform/callableReferences/");
|
runTest("idea/testData/multiplatform/callableReferences/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("commonSealedWithPlatformInheritor")
|
||||||
|
public void testCommonSealedWithPlatformInheritor() throws Exception {
|
||||||
|
runTest("idea/testData/multiplatform/commonSealedWithPlatformInheritor/");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("constructorsOfExpect")
|
@TestMetadata("constructorsOfExpect")
|
||||||
public void testConstructorsOfExpect() throws Exception {
|
public void testConstructorsOfExpect() throws Exception {
|
||||||
runTest("idea/testData/multiplatform/constructorsOfExpect/");
|
runTest("idea/testData/multiplatform/constructorsOfExpect/");
|
||||||
|
|||||||
Reference in New Issue
Block a user