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, statementFilter: StatementFilter,
analyzerServices: PlatformDependentAnalyzerServices, analyzerServices: PlatformDependentAnalyzerServices,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
moduleStructureOracle: ModuleStructureOracle moduleStructureOracle: ModuleStructureOracle,
sealedProvider: SealedClassInheritorsProvider = CliSealedClassInheritorsProvider
): StorageComponentContainer = createContainer("BodyResolve", analyzerServices) { ): StorageComponentContainer = createContainer("BodyResolve", analyzerServices) {
configureModule(moduleContext, platform, analyzerServices, bindingTrace, languageVersionSettings) configureModule(moduleContext, platform, analyzerServices, bindingTrace, languageVersionSettings, sealedProvider)
useInstance(statementFilter) useInstance(statementFilter)
@@ -156,10 +157,10 @@ fun createContainerForLazyBodyResolve(
analyzerServices: PlatformDependentAnalyzerServices, analyzerServices: PlatformDependentAnalyzerServices,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
moduleStructureOracle: ModuleStructureOracle, moduleStructureOracle: ModuleStructureOracle,
mainFunctionDetectorFactory: MainFunctionDetector.Factory mainFunctionDetectorFactory: MainFunctionDetector.Factory,
sealedProvider: SealedClassInheritorsProvider = CliSealedClassInheritorsProvider
): StorageComponentContainer = createContainer("LazyBodyResolve", analyzerServices) { ): StorageComponentContainer = createContainer("LazyBodyResolve", analyzerServices) {
configureModule(moduleContext, platform, analyzerServices, bindingTrace, languageVersionSettings) configureModule(moduleContext, platform, analyzerServices, bindingTrace, languageVersionSettings, sealedProvider)
useInstance(mainFunctionDetectorFactory) useInstance(mainFunctionDetectorFactory)
useInstance(kotlinCodeAnalyzer) useInstance(kotlinCodeAnalyzer)
useInstance(kotlinCodeAnalyzer.fileScopeProvider) 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.di.configureStandardResolveComponents
import org.jetbrains.kotlin.frontend.java.di.configureJavaSpecificComponents import org.jetbrains.kotlin.frontend.java.di.configureJavaSpecificComponents
import org.jetbrains.kotlin.frontend.java.di.initializeJavaSpecificComponents 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.configuration.IdeBuiltInsLoadingState
import org.jetbrains.kotlin.idea.project.IdeaEnvironment import org.jetbrains.kotlin.idea.project.IdeaEnvironment
import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver
@@ -189,7 +190,7 @@ class CompositeResolverForModuleFactory(
} }
// Called by all normal containers set-ups // Called by all normal containers set-ups
configureModule(moduleContext, targetPlatform, analyzerServices, trace, languageVersionSettings,) configureModule(moduleContext, targetPlatform, analyzerServices, trace, languageVersionSettings, IdeSealedClassInheritorsProvider)
configureStandardResolveComponents() configureStandardResolveComponents()
useInstance(moduleContentScope) useInstance(moduleContentScope)
useInstance(declarationProviderFactory) 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.clearInBlockModifications
import org.jetbrains.kotlin.idea.caches.trackers.inBlockModifications import org.jetbrains.kotlin.idea.caches.trackers.inBlockModifications
import org.jetbrains.kotlin.idea.compiler.IdeMainFunctionDetectorFactory 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.IdeaModuleStructureOracle
import org.jetbrains.kotlin.idea.project.findAnalyzerServices import org.jetbrains.kotlin.idea.project.findAnalyzerServices
import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.project.languageVersionSettings
@@ -482,8 +483,9 @@ private object KotlinResolveDataProvider {
targetPlatform.findAnalyzerServices(project), targetPlatform.findAnalyzerServices(project),
analyzableElement.languageVersionSettings, analyzableElement.languageVersionSettings,
IdeaModuleStructureOracle(), IdeaModuleStructureOracle(),
IdeMainFunctionDetectorFactory() IdeMainFunctionDetectorFactory(),
).get<LazyTopDownAnalyzer>() IdeSealedClassInheritorsProvider
).get<LazyTopDownAnalyzer>()
lazyTopDownAnalyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, listOf(analyzableElement)) lazyTopDownAnalyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, listOf(analyzableElement))
} finally { } finally {
@@ -5,19 +5,27 @@
package org.jetbrains.kotlin.idea.compiler package org.jetbrains.kotlin.idea.compiler
import com.intellij.openapi.module.ModuleManager
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.search.* import com.intellij.psi.search.*
import com.intellij.psi.search.searches.ClassInheritorsSearch import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.psi.search.searches.ClassInheritorsSearch.SearchParameters import com.intellij.psi.search.searches.ClassInheritorsSearch.SearchParameters
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.containingPackage 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.javaResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.util.resolveToDescriptor import org.jetbrains.kotlin.idea.caches.resolve.util.resolveToDescriptor
import org.jetbrains.kotlin.idea.util.module import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.resolve.SealedClassInheritorsProvider 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() { object IdeSealedClassInheritorsProvider : SealedClassInheritorsProvider() {
@@ -29,18 +37,28 @@ object IdeSealedClassInheritorsProvider : SealedClassInheritorsProvider() {
val sealedKtClass = sealedClass.findPsi() as? KtClass ?: return emptyList() val sealedKtClass = sealedClass.findPsi() as? KtClass ?: return emptyList()
val searchScope: SearchScope = if (allowSealedInheritorsInDifferentFilesOfSamePackage) { val searchScope: SearchScope = if (allowSealedInheritorsInDifferentFilesOfSamePackage) {
val module = sealedKtClass.module ?: return emptyList() 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 containingPackage = sealedClass.containingPackage() ?: return emptyList()
val psiPackage = getPackageViaDirectoryService(sealedKtClass) val psiPackage = KotlinJavaPsiFacade.getInstance(sealedKtClass.project)
?: JavaPsiFacade.getInstance(sealedKtClass.project).findPackage(containingPackage.asString()) .findPackage(containingPackage.asString(), GlobalSearchScope.moduleScope(module))
?: getPackageViaDirectoryService(sealedKtClass)
?: return emptyList() ?: return emptyList()
val packageScope = PackageScope(psiPackage, false, false) val packageScope = PackageScope(psiPackage, false, false)
moduleSourceScope.intersectWith(packageScope)
mppAwareSearchScope.intersectWith(packageScope)
} else { } else {
GlobalSearchScope.fileScope(sealedKtClass.containingFile) // Kotlin version prior to 1.5 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) val searchParameters = SearchParameters(lightClass, searchScope, false, true, false)
return ClassInheritorsSearch.search(searchParameters) return ClassInheritorsSearch.search(searchParameters)
@@ -51,6 +69,10 @@ object IdeSealedClassInheritorsProvider : SealedClassInheritorsProvider() {
.sortedBy(ClassDescriptor::getName) // order needs to be stable (at least for tests) .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? { private fun getPackageViaDirectoryService(ktClass: KtClass): PsiPackage? {
val directory = ktClass.containingFile.containingDirectory ?: return null val directory = ktClass.containingFile.containingDirectory ?: return null
return JavaDirectoryService.getInstance().getPackage(directory) 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.KotlinCodeBlockModificationListener
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListenerCompat import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListenerCompat
import org.jetbrains.kotlin.idea.caches.trackers.inBlockModificationCount 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.idea.util.application.isUnitTestMode
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.TargetPlatform
@@ -785,7 +785,8 @@ class ResolveElementCache(
statementFilter, statementFilter,
targetPlatform.findAnalyzerServices(file.project), targetPlatform.findAnalyzerServices(file.project),
file.languageVersionSettings, file.languageVersionSettings,
IdeaModuleStructureOracle() IdeaModuleStructureOracle(),
IdeSealedClassInheritorsProvider
).get() ).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 { public void testWeaklyIncompatibleActualInIntermediateModule() throws Exception {
runTest("idea/testData/multiplatform/weaklyIncompatibleActualInIntermediateModule/"); runTest("idea/testData/multiplatform/weaklyIncompatibleActualInIntermediateModule/");
} }
@TestMetadata("whenExhaustivenessForSealed")
public void testWhenExhaustivenessForSealed() throws Exception {
runTest("idea/testData/multiplatform/whenExhaustivenessForSealed/");
}
} }