[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:
Andrei Klunnyi
2021-02-27 19:32:00 +01:00
committed by Space
parent aa829c2321
commit e6ccdff2f7
20 changed files with 324 additions and 92 deletions

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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")
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -0,0 +1,5 @@
class ClassAnotherModuleInheritorA: SealedClass()
class ClassAnotherModuleInheritorB: SealedClass()
class InterfaceAnotherModuleInheritorA: SealedInterface
class InterfaceAnotherModuleInheritorB: SealedInterface

View File

@@ -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

View File

@@ -0,0 +1,5 @@
class ClassSamePackageInheritorA: SealedClass()
class ClassSamePackageInheritorB: SealedClass()
class InterfaceSamePackageInheritorA: SealedInterface
class InterfaceSamePackageInheritorB: SealedInterface

View File

@@ -0,0 +1,7 @@
package anotherpackage
class ClassAnotherPackageInheritorA: SealedClass()
class ClassAnotherPackageInheritorB: SealedClass()
class InterfaceAnotherPackageInheritorA: SealedInterface
class InterfaceAnotherPackageInheritorB: SealedInterface

View File

@@ -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
}

View File

@@ -0,0 +1,7 @@
{
"modules" : [
{ "name": "main", "dependsOn": ["anotherModule"] },
{ "name": "anotherModule"}
],
"fileToResolve": { "module": "main", "file": "main.kt" }
}

View File

@@ -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) {

View File

@@ -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)
}
}
}

View File

@@ -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()
}
}

View File

@@ -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/");
}
}

View File

@@ -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(),

View File

@@ -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)
}

View File

@@ -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"/>-->