KT-44487 [Sealed Interfaces]: sealed-inheritors-provider for MPP

This commit is contained in:
Andrei Klunnyi
2021-02-09 12:54:51 +00:00
committed by Space
parent 6f9bcf249b
commit e3c1aa599d
11 changed files with 79 additions and 15 deletions
@@ -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)
@@ -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)
@@ -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<LazyTopDownAnalyzer>()
IdeMainFunctionDetectorFactory(),
IdeSealedClassInheritorsProvider
).get<LazyTopDownAnalyzer>()
lazyTopDownAnalyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, listOf(analyzableElement))
} finally {
@@ -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<ModuleDescriptor> {
return implementedDescriptors.closure { it.implementedDescriptors }
}
private fun getPackageViaDirectoryService(ktClass: KtClass): PsiPackage? {
val directory = ktClass.containingFile.containingDirectory ?: return null
return JavaDirectoryService.getInstance().getPackage(directory)
@@ -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()
}
@@ -0,0 +1,7 @@
expect sealed class <!LINE_MARKER("descr='Is subclassed by CommonAImplTestClass CommonImplTestClass PlatformAXImplTestClass PlatformAYImplTestClass'")!>TestClass<!>()
class CommonImplTestClass: TestClass()
fun checkCommon(t: TestClass): Int = when (t) {
is CommonImplTestClass -> 0
}
@@ -0,0 +1 @@
class CommonAImplTestClass: <!SEALED_INHERITOR_IN_DIFFERENT_MODULE!>TestClass<!>()
@@ -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 }
@@ -0,0 +1,8 @@
actual sealed class <!LINE_MARKER("descr='Is subclassed by PlatformAXImplTestClass'")!>TestClass<!> actual constructor() {}
class PlatformAXImplTestClass: TestClass()
fun checkCommonAX(t: TestClass): Int = when (t) {
is CommonImplTestClass -> 0
is CommonAImplTestClass -> 1
is PlatformAXImplTestClass -> 2
}
@@ -0,0 +1,8 @@
actual sealed class <!LINE_MARKER("descr='Is subclassed by PlatformAYImplTestClass'")!>TestClass<!> actual constructor() {}
class PlatformAYImplTestClass: TestClass()
fun checkCommonAY(t: TestClass): Int = when (t) {
is CommonImplTestClass -> 0
is CommonAImplTestClass -> 1
is PlatformAYImplTestClass -> 2
}
@@ -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/");
}
}