diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractPhaseTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractPhaseTransformer.kt index 1af1a36c9c3..1dd543ae1af 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractPhaseTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirAbstractPhaseTransformer.kt @@ -44,14 +44,4 @@ abstract class FirAbstractPhaseTransformer( "File ${file.name} and transformer ${this::class} have inconsistent sessions" } } -} - -fun FirFile.runResolve(toPhase: FirResolvePhase, fromPhase: FirResolvePhase = FirResolvePhase.RAW_FIR) { - val scopeSession = ScopeSession() - var currentPhase = fromPhase - while (currentPhase < toPhase) { - currentPhase = currentPhase.next - val phaseProcessor = currentPhase.createTransformerBasedProcessorByPhase(session, scopeSession) - phaseProcessor.processFile(this) - } -} +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirLegacySealedClassInheritorsTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirLegacySealedClassInheritorsTransformer.kt deleted file mode 100644 index bc813056e6d..00000000000 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirLegacySealedClassInheritorsTransformer.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2010-2019 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.fir.resolve.transformers - -import org.jetbrains.kotlin.fir.FirElement -import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.declarations.FirDeclaration -import org.jetbrains.kotlin.fir.declarations.FirFile -import org.jetbrains.kotlin.fir.declarations.FirRegularClass -import org.jetbrains.kotlin.fir.resolve.ScopeSession -import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult -import org.jetbrains.kotlin.fir.visitors.FirTransformer -import org.jetbrains.kotlin.fir.visitors.compose -import org.jetbrains.kotlin.name.ClassId - -/* - * This processor is needed only for IDE until there won't be proper IDE implementation - * for detecting sealed inheritors in multiple files - */ -class FirLegacySealedClassInheritorsProcessor(session: FirSession, scopeSession: ScopeSession) : FirTransformerBasedResolveProcessor(session, scopeSession) { - override val transformer = FirLegacySealedClassInheritorsTransformer() -} - -class FirLegacySealedClassInheritorsTransformer : FirTransformer() { - override fun transformElement(element: E, data: Nothing?): CompositeTransformResult { - throw IllegalStateException("Should not be there") - } - - override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult { - val sealedClassInheritorsMap = mutableMapOf>() - file.accept(FirSealedClassInheritorsProcessor.InheritorsCollector, sealedClassInheritorsMap) - if (sealedClassInheritorsMap.isEmpty()) return file.compose() - return file.transform(FirSealedClassInheritorsProcessor.InheritorsTransformer(sealedClassInheritorsMap), null) - } -} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTotalResolveProcessor.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTotalResolveProcessor.kt index a385324ff3b..adda27981ae 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTotalResolveProcessor.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirTotalResolveProcessor.kt @@ -41,17 +41,7 @@ fun createAllCompilerResolveProcessors( } } -fun createAllTransformerBasedResolveProcessors( - session: FirSession, - scopeSession: ScopeSession? = null, - pluginPhasesEnabled: Boolean = false, -): List { - return createAllResolveProcessors(scopeSession, pluginPhasesEnabled) { - createTransformerBasedProcessorByPhase(session, it) - } -} - -private inline fun createAllResolveProcessors( +inline fun createAllResolveProcessors( scopeSession: ScopeSession? = null, pluginPhasesEnabled: Boolean, creator: FirResolvePhase.(ScopeSession) -> T diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/ResolvePhaseUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/ResolvePhaseUtils.kt index a4e76d1a194..2ee9a1b9312 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/ResolvePhaseUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/ResolvePhaseUtils.kt @@ -40,29 +40,7 @@ fun FirResolvePhase.createCompilerProcessorByPhase( } } -fun FirResolvePhase.createTransformerBasedProcessorByPhase( - session: FirSession, - scopeSession: ScopeSession -): FirTransformerBasedResolveProcessor { - return when (this) { - RAW_FIR -> throw IllegalStateException("Raw FIR building phase does not have a transformer") - ANNOTATIONS_FOR_PLUGINS -> FirPluginAnnotationsResolveProcessor(session, scopeSession) - CLASS_GENERATION -> FirDummyTransformerBasedProcessor(session, scopeSession) // TODO: remove - IMPORTS -> FirImportResolveProcessor(session, scopeSession) - SUPER_TYPES -> FirSupertypeResolverProcessor(session, scopeSession) - SEALED_CLASS_INHERITORS -> FirLegacySealedClassInheritorsProcessor(session, scopeSession) - TYPES -> FirTypeResolveProcessor(session, scopeSession) - ARGUMENTS_OF_PLUGIN_ANNOTATIONS -> FirAnnotationArgumentsResolveProcessor(session, scopeSession) - EXTENSION_STATUS_UPDATE -> FirTransformerBasedExtensionStatusProcessor(session, scopeSession) - STATUS -> FirStatusResolveProcessor(session, scopeSession) - CONTRACTS -> FirContractResolveProcessor(session, scopeSession) - NEW_MEMBERS_GENERATION -> FirDummyTransformerBasedProcessor(session, scopeSession) // TODO: remove - IMPLICIT_TYPES_BODY_RESOLVE -> FirImplicitTypeBodyResolveProcessor(session, scopeSession) - BODY_RESOLVE -> FirBodyResolveProcessor(session, scopeSession) - } -} - -private class FirDummyTransformerBasedProcessor( +class FirDummyTransformerBasedProcessor( session: FirSession, scopeSession: ScopeSession ) : FirTransformerBasedResolveProcessor(session, scopeSession) { diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index d5469712fb2..2284669adf5 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -79,6 +79,7 @@ import org.jetbrains.kotlin.idea.editor.backspaceHandler.AbstractBackspaceHandle import org.jetbrains.kotlin.idea.editor.quickDoc.AbstractQuickDocProviderTest import org.jetbrains.kotlin.idea.filters.AbstractKotlinExceptionFilterTest import org.jetbrains.kotlin.idea.fir.AbstractKtDeclarationAndFirDeclarationEqualityChecker +import org.jetbrains.kotlin.idea.fir.low.level.api.* import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirLazyDeclarationResolveTest import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirLazyResolveTest import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleLazyResolveTest @@ -1062,6 +1063,9 @@ fun main(args: Array) { testClass { model("multiModuleLazyResolve", recursive = false, extension = null) } + testClass { + model("resolveSealed", recursive = false, extension = null) + } testClass { model("lazyResolve") } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirIdeSealedHierarchyProcessor.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirIdeSealedHierarchyProcessor.kt new file mode 100644 index 00000000000..6357171a61e --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirIdeSealedHierarchyProcessor.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2010-2021 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.idea.fir.low.level.api + +import com.intellij.openapi.module.Module +import com.intellij.psi.JavaDirectoryService +import com.intellij.psi.PsiPackage +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.search.PackageScope +import com.intellij.psi.search.SearchScope +import com.intellij.psi.search.searches.ClassInheritorsSearch +import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.psi +import org.jetbrains.kotlin.fir.resolve.ScopeSession +import org.jetbrains.kotlin.fir.resolve.transformers.FirSealedClassInheritorsProcessor +import org.jetbrains.kotlin.fir.resolve.transformers.FirTransformerBasedResolveProcessor +import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult +import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor +import org.jetbrains.kotlin.fir.visitors.FirTransformer +import org.jetbrains.kotlin.fir.visitors.compose +import org.jetbrains.kotlin.idea.util.classIdIfNonLocal +import org.jetbrains.kotlin.idea.util.module +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade + + +class FirIdeSealedHierarchyProcessor(session: FirSession, scopeSession: ScopeSession) : + FirTransformerBasedResolveProcessor(session, scopeSession) { + + override val transformer: FirTransformer = SealedClassInheritorsTransformer + + + private object SealedClassInheritorsTransformer : FirTransformer() { + override fun transformElement(element: E, data: Nothing?): CompositeTransformResult { + throw IllegalStateException("Should not be there") + } + + override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult { + val sealedClassInheritorsMap = mutableMapOf>() + file.accept(SealedInheritorsCollector, sealedClassInheritorsMap) + + if (sealedClassInheritorsMap.isEmpty()) return file.compose() + return file.transform(FirSealedClassInheritorsProcessor.InheritorsTransformer(sealedClassInheritorsMap), null) + } + } + + private object SealedInheritorsCollector : FirDefaultVisitor>>() { + + override fun visitElement(element: FirElement, data: MutableMap>) {} + + override fun visitFile(file: FirFile, data: MutableMap>) { + file.declarations.forEach { it.accept(this, data) } + } + + override fun visitRegularClass(regularClass: FirRegularClass, data: MutableMap>) { + if (!regularClass.isSealed) { + regularClass.acceptChildren(this, data) + return + } + + val sealedKtClass = regularClass.psi as? KtClass ?: return + val module = sealedKtClass.module ?: return + val containingPackage = regularClass.classId.packageFqName + + val psiPackage = KotlinJavaPsiFacade.getInstance(sealedKtClass.project) + .findPackage(containingPackage.asString(), GlobalSearchScope.moduleScope(module)) + ?: getPackageViaDirectoryService(sealedKtClass) + ?: return + + val kotlinAsJavaSupport = KotlinAsJavaSupport.getInstance(sealedKtClass.project) + val lightClass = sealedKtClass.toLightClass() ?: kotlinAsJavaSupport.getFakeLightClass(sealedKtClass) + + val searchScope: SearchScope = getSearchScope(module, psiPackage) + val searchParameters = ClassInheritorsSearch.SearchParameters(lightClass, searchScope, false, true, false) + val subclasses = ClassInheritorsSearch.search(searchParameters) + .mapNotNull { it.classIdIfNonLocal() } + .toMutableList() + + data[regularClass] = subclasses + } + + private fun getSearchScope(module: Module, psiPackage: PsiPackage): GlobalSearchScope { + val packageScope = PackageScope(psiPackage, false, false) + // MPP multiple common modules are not supported!! + return module.moduleScope.intersectWith(packageScope) + } + } +} + +private fun getPackageViaDirectoryService(ktClass: KtClass): PsiPackage? { + val directory = ktClass.containingFile.containingDirectory ?: return null + return JavaDirectoryService.getInstance().getPackage(directory) +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirPhaseRunner.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirPhaseRunner.kt index 12d3be6addb..31d66909665 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirPhaseRunner.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/FirPhaseRunner.kt @@ -5,10 +5,17 @@ package org.jetbrains.kotlin.idea.fir.low.level.api +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.resolve.ScopeSession -import org.jetbrains.kotlin.fir.resolve.transformers.createTransformerBasedProcessorByPhase +import org.jetbrains.kotlin.fir.resolve.transformers.* +import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveProcessor +import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirImplicitTypeBodyResolveProcessor +import org.jetbrains.kotlin.fir.resolve.transformers.contracts.FirContractResolveProcessor +import org.jetbrains.kotlin.fir.resolve.transformers.plugin.FirAnnotationArgumentsResolveProcessor +import org.jetbrains.kotlin.fir.resolve.transformers.plugin.FirPluginAnnotationsResolveProcessor +import org.jetbrains.kotlin.fir.resolve.transformers.plugin.FirTransformerBasedExtensionStatusProcessor import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyBodiesCalculator import org.jetbrains.kotlin.idea.fir.low.level.api.util.executeWithoutPCE import java.util.concurrent.locks.ReentrantLock @@ -55,7 +62,6 @@ internal class FirPhaseRunner { } } - private fun runPhaseWithoutLock(firFile: FirFile, phase: FirResolvePhase, scopeSession: ScopeSession) { val phaseProcessor = phase.createTransformerBasedProcessorByPhase(firFile.session, scopeSession) executeWithoutPCE { @@ -64,3 +70,25 @@ internal class FirPhaseRunner { } } } + +internal fun FirResolvePhase.createTransformerBasedProcessorByPhase( + session: FirSession, + scopeSession: ScopeSession +): FirTransformerBasedResolveProcessor { + return when (this) { + FirResolvePhase.RAW_FIR -> throw IllegalStateException("Raw FIR building phase does not have a transformer") + FirResolvePhase.ANNOTATIONS_FOR_PLUGINS -> FirPluginAnnotationsResolveProcessor(session, scopeSession) + FirResolvePhase.CLASS_GENERATION -> FirDummyTransformerBasedProcessor(session, scopeSession) // TODO: remove + FirResolvePhase.IMPORTS -> FirImportResolveProcessor(session, scopeSession) + FirResolvePhase.SUPER_TYPES -> FirSupertypeResolverProcessor(session, scopeSession) + FirResolvePhase.SEALED_CLASS_INHERITORS -> FirIdeSealedHierarchyProcessor(session, scopeSession) + FirResolvePhase.TYPES -> FirTypeResolveProcessor(session, scopeSession) + FirResolvePhase.ARGUMENTS_OF_PLUGIN_ANNOTATIONS -> FirAnnotationArgumentsResolveProcessor(session, scopeSession) + FirResolvePhase.EXTENSION_STATUS_UPDATE -> FirTransformerBasedExtensionStatusProcessor(session, scopeSession) + FirResolvePhase.STATUS -> FirStatusResolveProcessor(session, scopeSession) + FirResolvePhase.CONTRACTS -> FirContractResolveProcessor(session, scopeSession) + FirResolvePhase.NEW_MEMBERS_GENERATION -> FirDummyTransformerBasedProcessor(session, scopeSession) // TODO: remove + FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE -> FirImplicitTypeBodyResolveProcessor(session, scopeSession) + FirResolvePhase.BODY_RESOLVE -> FirBodyResolveProcessor(session, scopeSession) + } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/anotherModule/AnotherModuleSamePackageDeclarations.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/anotherModule/AnotherModuleSamePackageDeclarations.kt new file mode 100644 index 00000000000..4e75fd12558 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/anotherModule/AnotherModuleSamePackageDeclarations.kt @@ -0,0 +1,5 @@ +class ClassAnotherModuleInheritorA: SealedClass() +class ClassAnotherModuleInheritorB: SealedClass() + +class InterfaceAnotherModuleInheritorA: SealedInterface +class InterfaceAnotherModuleInheritorB: SealedInterface \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/expected.txt b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/expected.txt new file mode 100644 index 00000000000..698f1d19a11 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/expected.txt @@ -0,0 +1,18 @@ +ClassSameFileInheritorA +ClassSameFileInheritorB +ClassSameFileInheritorC +ClassSamePackageInheritorA +ClassSamePackageInheritorB +InterfaceSameFileInheritorA +InterfaceSameFileInheritorB +InterfaceSameFileInheritorC +InterfaceSamePackageInheritorA +InterfaceSamePackageInheritorB +NonSealedClass.NestedSealedInheritorA +NonSealedClass.NestedSealedInheritorB +SealedClass.NestedInheritorA +SealedClass.NestedInheritorA.NestedNestedInheritorA +SealedClass.NestedInheritorA.NestedNestedInheritorB +SealedInterface.NestedInheritorA +SealedInterface.NestedInheritorA.NestedNestedInheritorA +SealedInterface.NestedInheritorA.NestedNestedInheritorB \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/main/SamePackageDeclarations.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/main/SamePackageDeclarations.kt new file mode 100644 index 00000000000..f6d4fc9d786 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/main/SamePackageDeclarations.kt @@ -0,0 +1,5 @@ +class ClassSamePackageInheritorA: SealedClass() +class ClassSamePackageInheritorB: SealedClass() + +class InterfaceSamePackageInheritorA: SealedInterface +class InterfaceSamePackageInheritorB: SealedInterface diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/main/anotherpackage/AnotherPackageDeclarations.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/main/anotherpackage/AnotherPackageDeclarations.kt new file mode 100644 index 00000000000..f39899b41e3 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/main/anotherpackage/AnotherPackageDeclarations.kt @@ -0,0 +1,7 @@ +package anotherpackage + +class ClassAnotherPackageInheritorA: SealedClass() +class ClassAnotherPackageInheritorB: SealedClass() + +class InterfaceAnotherPackageInheritorA: SealedInterface +class InterfaceAnotherPackageInheritorB: SealedInterface \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/main/main.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/main/main.kt new file mode 100644 index 00000000000..1010543c424 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/main/main.kt @@ -0,0 +1,29 @@ +sealed class SealedClass { // (1): top level sealed class + class NestedInheritorA: SealedClass() { + class NestedNestedInheritorA: SealedClass() + object NestedNestedInheritorB: SealedClass() + } +} + +class ClassSameFileInheritorA: SealedClass() +class ClassSameFileInheritorB: SealedClass() +object ClassSameFileInheritorC: SealedClass() + +sealed interface SealedInterface { // (2): top level sealed interface + class NestedInheritorA: SealedInterface { + interface NestedNestedInheritorA: SealedInterface + object NestedNestedInheritorB: SealedInterface + } +} + +class InterfaceSameFileInheritorA: SealedInterface +class InterfaceSameFileInheritorB: SealedInterface +object InterfaceSameFileInheritorC: SealedInterface + + +class NonSealedClass { + sealed class NestedSealedClass // (3): nested sealed class + sealed interface NestedSealedInterface // (4): nested sealed interface + class NestedSealedInheritorA: NestedSealedClass() + class NestedSealedInheritorB: NestedSealedInterface +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/structure.json b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/structure.json new file mode 100644 index 00000000000..991c4e67b28 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/structure.json @@ -0,0 +1,7 @@ +{ + "modules" : [ + { "name": "main", "dependsOn": ["anotherModule"] }, + { "name": "anotherModule"} + ], + "fileToResolve": { "module": "main", "file": "main.kt" } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirMultiModuleLazyResolveTest.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirMultiModuleLazyResolveTest.kt index 5812a1e36ce..d7e46ba96d9 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirMultiModuleLazyResolveTest.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirMultiModuleLazyResolveTest.kt @@ -9,8 +9,10 @@ import com.google.gson.JsonElement import com.google.gson.JsonObject import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.PsiManager +import org.jetbrains.kotlin.executeOnPooledThreadInReadAction +import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.render -import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFir +import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFirOfType import org.jetbrains.kotlin.idea.fir.low.level.api.api.getResolveState import org.jetbrains.kotlin.idea.jsonUtils.getString import org.jetbrains.kotlin.idea.stubs.AbstractMultiModuleTest @@ -47,8 +49,9 @@ abstract class AbstractFirMultiModuleLazyResolveTest : AbstractMultiModuleTest() val fails = testStructure.fails try { - val fir = ktFileToAnalyse.getOrBuildFir(resolveState) - KotlinTestUtils.assertEqualsToFile(File("$path/expected.txt"), fir.render()) + val fir = executeOnPooledThreadInReadAction { ktFileToAnalyse.getOrBuildFirOfType(resolveState) } + ?: throw AssertionError("Can't build FirFile from ${ktFileToAnalyse.virtualFilePath}") + checkFirFile(fir, path) } catch (e: Throwable) { if (!fails) throw e return @@ -57,6 +60,10 @@ abstract class AbstractFirMultiModuleLazyResolveTest : AbstractMultiModuleTest() throw AssertionError("Looks like test is passing, please remove `\"fails\": true` from structure.json") } } + + protected open fun checkFirFile(firFile: FirFile, path: String) { + KotlinTestUtils.assertEqualsToFile(File("$path/expected.txt"), firFile.render()) + } } private data class FileToResolve(val moduleName: String, val relativeFilePath: String) { diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirMultiModuleResolveTest.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirMultiModuleResolveTest.kt index 696a1cf2c59..607bf8b9d0a 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirMultiModuleResolveTest.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirMultiModuleResolveTest.kt @@ -11,16 +11,17 @@ import com.intellij.psi.PsiFile import com.intellij.psi.PsiManager import com.intellij.psi.search.FileTypeIndex import org.jetbrains.kotlin.fir.FirRenderer +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.builder.RawFirBuilder import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.dependenciesWithoutSelf import org.jetbrains.kotlin.fir.java.* import org.jetbrains.kotlin.fir.psi +import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.firProvider import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl -import org.jetbrains.kotlin.fir.resolve.transformers.FirTransformerBasedResolveProcessor -import org.jetbrains.kotlin.fir.resolve.transformers.createAllTransformerBasedResolveProcessors +import org.jetbrains.kotlin.fir.resolve.transformers.* import org.jetbrains.kotlin.fir.session.FirSessionFactory import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo @@ -155,5 +156,15 @@ abstract class AbstractFirMultiModuleResolveTest : AbstractMultiModuleTest() { // KotlinTestUtils.assertEqualsToFile(File("$dirPath/extraDump.java.txt"), javaFirDump) // } } + + private fun createAllTransformerBasedResolveProcessors( + session: FirSession, + scopeSession: ScopeSession? = null, + pluginPhasesEnabled: Boolean = false, + ): List { + return createAllResolveProcessors(scopeSession, pluginPhasesEnabled) { + createTransformerBasedProcessorByPhase(session, it) + } + } } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirSealedInheritorsTest.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirSealedInheritorsTest.kt new file mode 100644 index 00000000000..f0bc36f5037 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/AbstractFirSealedInheritorsTest.kt @@ -0,0 +1,39 @@ +/* + * 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.idea.fir.low.level.api + +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.util.KtTestUtil +import org.jetbrains.kotlin.types.typeUtil.closure +import org.jetbrains.kotlin.utils.addToStdlib.flatMapToNullable +import java.io.File + +/** + * The idea behind this test is to check that [FirIdeSealedHierarchyProcessor] finds all direct inheritors of sealed classes and interfaces. + * We use the fact that [SealedClassInheritorsKt#getSealedInheritors] property gets its value thanks to the class activity. + * + * Inheritors are collected for every sealed declaration of the 'fileToResolve' (see test data 'structure.json'). Resulting collection is + * compared with 'expected.txt'. + */ +abstract class AbstractFirSealedInheritorsTest : AbstractFirMultiModuleLazyResolveTest() { + override fun getTestDataPath(): String = + "${KtTestUtil.getHomeDirectory()}/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/" + + override fun checkFirFile(firFile: FirFile, path: String) { + val allClasses = firFile.listNestedClasses().closure { it.listNestedClasses() } + val inheritorNames = allClasses.flatMap { it.sealedInheritors ?: emptyList() }.map { it.asString() }.sorted() + KotlinTestUtils.assertEqualsToFile(File("$path/expected.txt"), inheritorNames.joinToString("\n")) + } +} + +private fun FirDeclaration.listNestedClasses(): List { + return when (this) { + is FirFile -> declarations.filterIsInstance() + is FirRegularClass -> declarations.filterIsInstance() + else -> emptyList() + } +} \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/FirSealedInheritorsTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/FirSealedInheritorsTestGenerated.java new file mode 100644 index 00000000000..23c2d47d918 --- /dev/null +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/FirSealedInheritorsTestGenerated.java @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2021 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.idea.fir.low.level.api; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class FirSealedInheritorsTestGenerated extends AbstractFirSealedInheritorsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInResolveSealed() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed"), Pattern.compile("^([^\\.]+)$"), null, false); + } + + @TestMetadata("directInheritors") + public void testDirectInheritors() throws Exception { + runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/resolveSealed/directInheritors/"); + } +} diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirTypeProvider.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirTypeProvider.kt index c109af8e63c..e31342c84d4 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirTypeProvider.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirTypeProvider.kt @@ -42,7 +42,7 @@ internal class KtFirTypeProvider( override fun buildSelfClassType(symbol: KtClassOrObjectSymbol): KtType { require(symbol is KtFirClassOrObjectSymbol) - val type = symbol.firRef.withFir(FirResolvePhase.TYPES) { firClass -> + val type = symbol.firRef.withFir(FirResolvePhase.SUPER_TYPES) { firClass -> ConeClassLikeTypeImpl( firClass.symbol.toLookupTag(), firClass.typeParameters.map { ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), isNullable = false) }.toTypedArray(), diff --git a/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/util/psiUtils.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/util/psiUtils.kt index 8c05e75bd25..adfc5b52bbc 100644 --- a/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/util/psiUtils.kt +++ b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/util/psiUtils.kt @@ -5,11 +5,14 @@ package org.jetbrains.kotlin.idea.util +import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.PsiJavaFile import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.util.parentOfType import com.intellij.psi.util.parentsOfType +import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.cfg.containingDeclarationForPseudocode import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -45,3 +48,15 @@ fun KtClassOrObject.classIdIfNonLocal(): ClassId? { return ClassId(packageName, FqName(classesNames.joinToString(separator = ".")), /*local=*/false) } +fun PsiClass.classIdIfNonLocal(): ClassId? { + if (this is KtLightClass) { + return this.kotlinOrigin?.classIdIfNonLocal() + } + val packageName = (containingFile as? PsiJavaFile)?.packageName ?: return null + val packageFqName = FqName(packageName) + + val classesNames = parentsOfType().map { it.name }.toList().asReversed() + if (classesNames.any { it == null }) return null + return ClassId(packageFqName, FqName(classesNames.joinToString(separator = ".")), false) +} + diff --git a/idea/resources-fir/META-INF/plugin.xml b/idea/resources-fir/META-INF/plugin.xml index 69c6efe8e26..49cb90daf46 100644 --- a/idea/resources-fir/META-INF/plugin.xml +++ b/idea/resources-fir/META-INF/plugin.xml @@ -262,7 +262,7 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu - +