diff --git a/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt b/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt index 68d5d6a23a2..20b936b0438 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/frontend/di/injection.kt @@ -134,9 +134,10 @@ fun createContainerForBodyResolve( statementFilter: StatementFilter, analyzerServices: PlatformDependentAnalyzerServices, languageVersionSettings: LanguageVersionSettings, - moduleStructureOracle: ModuleStructureOracle + moduleStructureOracle: ModuleStructureOracle, + sealedProvider: SealedClassInheritorsProvider = CliSealedClassInheritorsProvider ): StorageComponentContainer = createContainer("BodyResolve", analyzerServices) { - configureModule(moduleContext, platform, analyzerServices, bindingTrace, languageVersionSettings) + configureModule(moduleContext, platform, analyzerServices, bindingTrace, languageVersionSettings, sealedProvider) useInstance(statementFilter) @@ -156,10 +157,10 @@ fun createContainerForLazyBodyResolve( analyzerServices: PlatformDependentAnalyzerServices, languageVersionSettings: LanguageVersionSettings, moduleStructureOracle: ModuleStructureOracle, - mainFunctionDetectorFactory: MainFunctionDetector.Factory + mainFunctionDetectorFactory: MainFunctionDetector.Factory, + sealedProvider: SealedClassInheritorsProvider = CliSealedClassInheritorsProvider ): StorageComponentContainer = createContainer("LazyBodyResolve", analyzerServices) { - configureModule(moduleContext, platform, analyzerServices, bindingTrace, languageVersionSettings) - + configureModule(moduleContext, platform, analyzerServices, bindingTrace, languageVersionSettings, sealedProvider) useInstance(mainFunctionDetectorFactory) useInstance(kotlinCodeAnalyzer) useInstance(kotlinCodeAnalyzer.fileScopeProvider) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt index d6d9fdf7947..ad6528edc56 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.frontend.di.configureModule import org.jetbrains.kotlin.frontend.di.configureStandardResolveComponents import org.jetbrains.kotlin.frontend.java.di.configureJavaSpecificComponents import org.jetbrains.kotlin.frontend.java.di.initializeJavaSpecificComponents +import org.jetbrains.kotlin.idea.compiler.IdeSealedClassInheritorsProvider import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState import org.jetbrains.kotlin.idea.project.IdeaEnvironment import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver @@ -189,7 +190,7 @@ class CompositeResolverForModuleFactory( } // Called by all normal containers set-ups - configureModule(moduleContext, targetPlatform, analyzerServices, trace, languageVersionSettings,) + configureModule(moduleContext, targetPlatform, analyzerServices, trace, languageVersionSettings, IdeSealedClassInheritorsProvider) configureStandardResolveComponents() useInstance(moduleContentScope) useInstance(declarationProviderFactory) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/PerFileAnalysisCache.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/PerFileAnalysisCache.kt index 84c206f2acd..dd57d976184 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/PerFileAnalysisCache.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/PerFileAnalysisCache.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.idea.caches.project.getModuleInfo import org.jetbrains.kotlin.idea.caches.trackers.clearInBlockModifications import org.jetbrains.kotlin.idea.caches.trackers.inBlockModifications import org.jetbrains.kotlin.idea.compiler.IdeMainFunctionDetectorFactory +import org.jetbrains.kotlin.idea.compiler.IdeSealedClassInheritorsProvider import org.jetbrains.kotlin.idea.project.IdeaModuleStructureOracle import org.jetbrains.kotlin.idea.project.findAnalyzerServices import org.jetbrains.kotlin.idea.project.languageVersionSettings @@ -482,8 +483,9 @@ private object KotlinResolveDataProvider { targetPlatform.findAnalyzerServices(project), analyzableElement.languageVersionSettings, IdeaModuleStructureOracle(), - IdeMainFunctionDetectorFactory() - ).get() + IdeMainFunctionDetectorFactory(), + IdeSealedClassInheritorsProvider + ).get() lazyTopDownAnalyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, listOf(analyzableElement)) } finally { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IdeSealedClassInheritorsProvider.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IdeSealedClassInheritorsProvider.kt index 13fdb4c15d7..e3b068dd23d 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IdeSealedClassInheritorsProvider.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IdeSealedClassInheritorsProvider.kt @@ -5,19 +5,27 @@ package org.jetbrains.kotlin.idea.compiler +import com.intellij.openapi.module.ModuleManager import com.intellij.psi.* import com.intellij.psi.search.* import com.intellij.psi.search.searches.ClassInheritorsSearch import com.intellij.psi.search.searches.ClassInheritorsSearch.SearchParameters +import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.containingPackage +import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors import org.jetbrains.kotlin.idea.caches.resolve.util.javaResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.util.resolveToDescriptor import org.jetbrains.kotlin.idea.util.module import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.resolve.SealedClassInheritorsProvider +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade +import org.jetbrains.kotlin.types.typeUtil.closure object IdeSealedClassInheritorsProvider : SealedClassInheritorsProvider() { @@ -29,18 +37,28 @@ object IdeSealedClassInheritorsProvider : SealedClassInheritorsProvider() { val sealedKtClass = sealedClass.findPsi() as? KtClass ?: return emptyList() val searchScope: SearchScope = if (allowSealedInheritorsInDifferentFilesOfSamePackage) { val module = sealedKtClass.module ?: return emptyList() - val moduleSourceScope = GlobalSearchScope.moduleScope(module) + val moduleManager = ModuleManager.getInstance(sealedKtClass.project) + + val modulesScope = sealedClass.module.listCommonModulesIfAny().toMutableList() + .apply { add(sealedClass.module) } + .mapNotNull { moduleManager.findModuleByName(JvmCodegenUtil.getModuleName(it))?.moduleScope } + + val mppAwareSearchScope = GlobalSearchScope.union(modulesScope) + val containingPackage = sealedClass.containingPackage() ?: return emptyList() - val psiPackage = getPackageViaDirectoryService(sealedKtClass) - ?: JavaPsiFacade.getInstance(sealedKtClass.project).findPackage(containingPackage.asString()) + val psiPackage = KotlinJavaPsiFacade.getInstance(sealedKtClass.project) + .findPackage(containingPackage.asString(), GlobalSearchScope.moduleScope(module)) + ?: getPackageViaDirectoryService(sealedKtClass) ?: return emptyList() val packageScope = PackageScope(psiPackage, false, false) - moduleSourceScope.intersectWith(packageScope) + + mppAwareSearchScope.intersectWith(packageScope) } else { GlobalSearchScope.fileScope(sealedKtClass.containingFile) // Kotlin version prior to 1.5 } - val lightClass = sealedKtClass.toLightClass() ?: return emptyList() + val kotlinAsJavaSupport = KotlinAsJavaSupport.getInstance(sealedKtClass.project) + val lightClass = sealedKtClass.toLightClass() ?: kotlinAsJavaSupport.getFakeLightClass(sealedKtClass) val searchParameters = SearchParameters(lightClass, searchScope, false, true, false) return ClassInheritorsSearch.search(searchParameters) @@ -51,6 +69,10 @@ object IdeSealedClassInheritorsProvider : SealedClassInheritorsProvider() { .sortedBy(ClassDescriptor::getName) // order needs to be stable (at least for tests) } + private fun ModuleDescriptor.listCommonModulesIfAny(): Collection { + return implementedDescriptors.closure { it.implementedDescriptors } + } + private fun getPackageViaDirectoryService(ktClass: KtClass): PsiPackage? { val directory = ktClass.containingFile.containingDirectory ?: return null return JavaDirectoryService.getInstance().getPackage(directory) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt index 76054150cd0..2b53bdea18f 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt @@ -29,7 +29,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.util.analyzeControlFlow import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListenerCompat import org.jetbrains.kotlin.idea.caches.trackers.inBlockModificationCount -import org.jetbrains.kotlin.idea.compiler.IdeMainFunctionDetectorFactory +import org.jetbrains.kotlin.idea.compiler.IdeSealedClassInheritorsProvider import org.jetbrains.kotlin.idea.util.application.isUnitTestMode import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.TargetPlatform @@ -785,7 +785,8 @@ class ResolveElementCache( statementFilter, targetPlatform.findAnalyzerServices(file.project), file.languageVersionSettings, - IdeaModuleStructureOracle() + IdeaModuleStructureOracle(), + IdeSealedClassInheritorsProvider ).get() } diff --git a/idea/testData/multiplatform/whenExhaustivenessForSealed/common/common.kt b/idea/testData/multiplatform/whenExhaustivenessForSealed/common/common.kt new file mode 100644 index 00000000000..9914c58c433 --- /dev/null +++ b/idea/testData/multiplatform/whenExhaustivenessForSealed/common/common.kt @@ -0,0 +1,7 @@ +expect sealed class TestClass() +class CommonImplTestClass: TestClass() + + +fun checkCommon(t: TestClass): Int = when (t) { + is CommonImplTestClass -> 0 +} diff --git a/idea/testData/multiplatform/whenExhaustivenessForSealed/commonA/commonA.kt b/idea/testData/multiplatform/whenExhaustivenessForSealed/commonA/commonA.kt new file mode 100644 index 00000000000..932279efdc9 --- /dev/null +++ b/idea/testData/multiplatform/whenExhaustivenessForSealed/commonA/commonA.kt @@ -0,0 +1 @@ +class CommonAImplTestClass: TestClass() \ No newline at end of file diff --git a/idea/testData/multiplatform/whenExhaustivenessForSealed/dependencies.txt b/idea/testData/multiplatform/whenExhaustivenessForSealed/dependencies.txt new file mode 100644 index 00000000000..fead550b979 --- /dev/null +++ b/idea/testData/multiplatform/whenExhaustivenessForSealed/dependencies.txt @@ -0,0 +1,8 @@ +MODULE common { platform=[JVM, JS, Native]; additionalCompilerArgs=: -XXLanguage:+SealedInterfaces -XXLanguage:+AllowSealedInheritorsInDifferentFilesOfSamePackage } +MODULE commonA { platform=[JVM]; additionalCompilerArgs=: -XXLanguage:+SealedInterfaces -XXLanguage:+AllowSealedInheritorsInDifferentFilesOfSamePackage } +MODULE platformAX { platform=[JVM]; additionalCompilerArgs=: -XXLanguage:+SealedInterfaces -XXLanguage:+AllowSealedInheritorsInDifferentFilesOfSamePackage } +MODULE platformAY { platform=[JVM]; additionalCompilerArgs=: -XXLanguage:+SealedInterfaces -XXLanguage:+AllowSealedInheritorsInDifferentFilesOfSamePackage } + +commonA -> common { kind=DEPENDS_ON } +platformAX -> commonA { kind=DEPENDS_ON } +platformAY -> commonA { kind=DEPENDS_ON } \ No newline at end of file diff --git a/idea/testData/multiplatform/whenExhaustivenessForSealed/platformAX/platformAX.kt b/idea/testData/multiplatform/whenExhaustivenessForSealed/platformAX/platformAX.kt new file mode 100644 index 00000000000..5d8a8717ce1 --- /dev/null +++ b/idea/testData/multiplatform/whenExhaustivenessForSealed/platformAX/platformAX.kt @@ -0,0 +1,8 @@ +actual sealed class TestClass actual constructor() {} +class PlatformAXImplTestClass: TestClass() + +fun checkCommonAX(t: TestClass): Int = when (t) { + is CommonImplTestClass -> 0 + is CommonAImplTestClass -> 1 + is PlatformAXImplTestClass -> 2 +} diff --git a/idea/testData/multiplatform/whenExhaustivenessForSealed/platformAY/platformAY.kt b/idea/testData/multiplatform/whenExhaustivenessForSealed/platformAY/platformAY.kt new file mode 100644 index 00000000000..6654412c806 --- /dev/null +++ b/idea/testData/multiplatform/whenExhaustivenessForSealed/platformAY/platformAY.kt @@ -0,0 +1,8 @@ +actual sealed class TestClass actual constructor() {} +class PlatformAYImplTestClass: TestClass() + +fun checkCommonAY(t: TestClass): Int = when (t) { + is CommonImplTestClass -> 0 + is CommonAImplTestClass -> 1 + is PlatformAYImplTestClass -> 2 +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiplatformAnalysisTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiplatformAnalysisTestGenerated.java index a0d86c053ff..dfc1a59e90e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiplatformAnalysisTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiplatformAnalysisTestGenerated.java @@ -268,4 +268,9 @@ public class MultiplatformAnalysisTestGenerated extends AbstractMultiplatformAna public void testWeaklyIncompatibleActualInIntermediateModule() throws Exception { runTest("idea/testData/multiplatform/weaklyIncompatibleActualInIntermediateModule/"); } + + @TestMetadata("whenExhaustivenessForSealed") + public void testWhenExhaustivenessForSealed() throws Exception { + runTest("idea/testData/multiplatform/whenExhaustivenessForSealed/"); + } }