mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-04-02 00:21:31 +00:00
[FIR]: sealed hierarchy processor for IDE
From now on sealed declarations get resolved with the help of FirIdeSealedHierarchyProcessor. This change entails correct IDE side check for sealed when exhaustiveness.
This commit is contained in:
@@ -44,14 +44,4 @@ abstract class FirAbstractPhaseTransformer<D>(
|
||||
"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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Nothing?>() {
|
||||
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
|
||||
throw IllegalStateException("Should not be there")
|
||||
}
|
||||
|
||||
override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirDeclaration> {
|
||||
val sealedClassInheritorsMap = mutableMapOf<FirRegularClass, MutableList<ClassId>>()
|
||||
file.accept(FirSealedClassInheritorsProcessor.InheritorsCollector, sealedClassInheritorsMap)
|
||||
if (sealedClassInheritorsMap.isEmpty()) return file.compose()
|
||||
return file.transform(FirSealedClassInheritorsProcessor.InheritorsTransformer(sealedClassInheritorsMap), null)
|
||||
}
|
||||
}
|
||||
@@ -41,17 +41,7 @@ fun createAllCompilerResolveProcessors(
|
||||
}
|
||||
}
|
||||
|
||||
fun createAllTransformerBasedResolveProcessors(
|
||||
session: FirSession,
|
||||
scopeSession: ScopeSession? = null,
|
||||
pluginPhasesEnabled: Boolean = false,
|
||||
): List<FirTransformerBasedResolveProcessor> {
|
||||
return createAllResolveProcessors(scopeSession, pluginPhasesEnabled) {
|
||||
createTransformerBasedProcessorByPhase(session, it)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T : FirResolveProcessor> createAllResolveProcessors(
|
||||
inline fun <T : FirResolveProcessor> createAllResolveProcessors(
|
||||
scopeSession: ScopeSession? = null,
|
||||
pluginPhasesEnabled: Boolean,
|
||||
creator: FirResolvePhase.(ScopeSession) -> T
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<String>) {
|
||||
testClass<AbstractFirMultiModuleLazyResolveTest> {
|
||||
model("multiModuleLazyResolve", recursive = false, extension = null)
|
||||
}
|
||||
testClass<AbstractFirSealedInheritorsTest> {
|
||||
model("resolveSealed", recursive = false, extension = null)
|
||||
}
|
||||
testClass<AbstractFirLazyDeclarationResolveTest> {
|
||||
model("lazyResolve")
|
||||
}
|
||||
|
||||
@@ -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<Nothing?> = SealedClassInheritorsTransformer
|
||||
|
||||
|
||||
private object SealedClassInheritorsTransformer : FirTransformer<Nothing?>() {
|
||||
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
|
||||
throw IllegalStateException("Should not be there")
|
||||
}
|
||||
|
||||
override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirDeclaration> {
|
||||
val sealedClassInheritorsMap = mutableMapOf<FirRegularClass, MutableList<ClassId>>()
|
||||
file.accept(SealedInheritorsCollector, sealedClassInheritorsMap)
|
||||
|
||||
if (sealedClassInheritorsMap.isEmpty()) return file.compose()
|
||||
return file.transform(FirSealedClassInheritorsProcessor.InheritorsTransformer(sealedClassInheritorsMap), null)
|
||||
}
|
||||
}
|
||||
|
||||
private object SealedInheritorsCollector : FirDefaultVisitor<Unit, MutableMap<FirRegularClass, MutableList<ClassId>>>() {
|
||||
|
||||
override fun visitElement(element: FirElement, data: MutableMap<FirRegularClass, MutableList<ClassId>>) {}
|
||||
|
||||
override fun visitFile(file: FirFile, data: MutableMap<FirRegularClass, MutableList<ClassId>>) {
|
||||
file.declarations.forEach { it.accept(this, data) }
|
||||
}
|
||||
|
||||
override fun visitRegularClass(regularClass: FirRegularClass, data: MutableMap<FirRegularClass, MutableList<ClassId>>) {
|
||||
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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class ClassAnotherModuleInheritorA: SealedClass()
|
||||
class ClassAnotherModuleInheritorB: SealedClass()
|
||||
|
||||
class InterfaceAnotherModuleInheritorA: SealedInterface
|
||||
class InterfaceAnotherModuleInheritorB: SealedInterface
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
class ClassSamePackageInheritorA: SealedClass()
|
||||
class ClassSamePackageInheritorB: SealedClass()
|
||||
|
||||
class InterfaceSamePackageInheritorA: SealedInterface
|
||||
class InterfaceSamePackageInheritorB: SealedInterface
|
||||
@@ -0,0 +1,7 @@
|
||||
package anotherpackage
|
||||
|
||||
class ClassAnotherPackageInheritorA: SealedClass()
|
||||
class ClassAnotherPackageInheritorB: SealedClass()
|
||||
|
||||
class InterfaceAnotherPackageInheritorA: SealedInterface
|
||||
class InterfaceAnotherPackageInheritorB: SealedInterface
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"modules" : [
|
||||
{ "name": "main", "dependsOn": ["anotherModule"] },
|
||||
{ "name": "anotherModule"}
|
||||
],
|
||||
"fileToResolve": { "module": "main", "file": "main.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<FirFile>(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) {
|
||||
|
||||
@@ -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<FirTransformerBasedResolveProcessor> {
|
||||
return createAllResolveProcessors(scopeSession, pluginPhasesEnabled) {
|
||||
createTransformerBasedProcessorByPhase(session, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<FirRegularClass> {
|
||||
return when (this) {
|
||||
is FirFile -> declarations.filterIsInstance<FirRegularClass>()
|
||||
is FirRegularClass -> declarations.filterIsInstance<FirRegularClass>()
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
@@ -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/");
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
|
||||
@@ -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<KtDeclaration>().map { it.name }.toList().asReversed()
|
||||
if (classesNames.any { it == null }) return null
|
||||
return ClassId(packageFqName, FqName(classesNames.joinToString(separator = ".")), false)
|
||||
}
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
|
||||
<definitionsScopedSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDefinitionsSearcher"/>
|
||||
<methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinOverridingMethodReferenceSearcher"/>
|
||||
<methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinPropertyAccessorsReferenceSearcher"/>
|
||||
<!-- <directClassInheritorsSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDirectInheritorsSearcher"/>-->
|
||||
<directClassInheritorsSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDirectInheritorsSearcher"/>
|
||||
<!-- <overridingMethodsSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinOverridingMethodsWithGenericsSearcher"/>-->
|
||||
<!-- <annotatedElementsSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinAnnotatedElementsSearcher"/>-->
|
||||
<!-- <classesWithAnnotatedMembersSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinClassesWithAnnotatedMembersSearcher"/>-->
|
||||
|
||||
Reference in New Issue
Block a user