AA: introduce new APIs to get containing file (symbol) and JvmClassName

^KTIJ-27686
This commit is contained in:
Jinseong Jeon
2023-11-17 11:08:21 -08:00
committed by Space Team
parent 5b6363b0df
commit 797174ee1f
24 changed files with 891 additions and 37 deletions
@@ -11,23 +11,28 @@ import com.intellij.psi.search.ProjectScope
import org.jetbrains.kotlin.analysis.api.components.KtSymbolContainingDeclarationProvider
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.components.base.Fe10KtAnalysisSessionComponent
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.KtFe10DescDefaultBackingFieldSymbol
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.KtFe10DynamicFunctionDescValueParameterSymbol
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.getDescriptor
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtSymbol
import org.jetbrains.kotlin.analysis.api.getModule
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
import org.jetbrains.kotlin.analysis.api.symbols.KtBackingFieldSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtDeclarationSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind
import org.jetbrains.kotlin.analysis.project.structure.*
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment
import org.jetbrains.kotlin.load.kotlin.*
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.descriptorUtil.platform
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
import java.nio.file.Path
@@ -46,13 +51,89 @@ internal class KtFe10SymbolContainingDeclarationProvider(
return when (symbol) {
is KtBackingFieldSymbol -> symbol.owningProperty
else -> symbol.getDescriptor()?.containingDeclaration?.toKtSymbol(analysisContext) as? KtDeclarationSymbol
is KtPropertyAccessorSymbol -> {
(symbol.getDescriptor() as? PropertyAccessorDescriptor)?.correspondingProperty
?.toKtSymbol(analysisContext) as? KtDeclarationSymbol
}
else -> {
symbol.getDescriptor()?.containingDeclaration
?.toKtSymbol(analysisContext) as? KtDeclarationSymbol
}
}
}
private val KtSymbol.containingSymbolOrSelf: KtSymbol
get() {
return when (this) {
is KtValueParameterSymbol -> {
getContainingDeclaration(this) as? KtFunctionLikeSymbol ?: this
}
is KtPropertyAccessorSymbol -> {
getContainingDeclaration(this) as? KtPropertySymbol ?: this
}
is KtBackingFieldSymbol -> this.owningProperty
else -> this
}
}
override fun getContainingFileSymbol(symbol: KtSymbol): KtFileSymbol? {
if (symbol is KtFileSymbol) return null
// psiBased
(symbol.psi?.containingFile as? KtFile)?.let { ktFile ->
with(analysisSession) {
return ktFile.getFileSymbol()
}
}
// descriptorBased
val descriptor = symbol.containingSymbolOrSelf.getDescriptor()
val ktFile = descriptor?.let(DescriptorToSourceUtils::getContainingFile) ?: return null
with(analysisSession) {
return ktFile.getFileSymbol()
}
}
override fun getContainingJvmClassName(symbol: KtCallableSymbol): JvmClassName? {
val platform = getContainingModule(symbol).platform
if (!platform.isCommon() && !platform.isJvm()) return null
val containingSymbolOrSelf = symbol.containingSymbolOrSelf as KtSymbolWithKind
return when (val descriptor = containingSymbolOrSelf.getDescriptor()) {
is DescriptorWithContainerSource -> {
when (val containerSource = descriptor.containerSource) {
is FacadeClassSource -> containerSource.facadeClassName ?: containerSource.className
is KotlinJvmBinarySourceElement -> JvmClassName.byClassId(containerSource.binaryClass.classId)
else -> null
}
}
else -> {
return if (containingSymbolOrSelf.symbolKind == KtSymbolKind.TOP_LEVEL) {
descriptor?.let(DescriptorToSourceUtils::getContainingFile)
?.takeUnless { it.isScript() }
?.let { JvmClassName.byFqNameWithoutInnerClasses(it.javaFileFacadeFqName) }
} else {
val classId = (containingSymbolOrSelf as? KtConstructorSymbol)?.containingClassIdIfNonLocal
?: (containingSymbolOrSelf as? KtCallableSymbol)?.callableIdIfNonLocal?.classId
classId?.takeUnless { it.shortClassName.isSpecial }
?.let { JvmClassName.byClassId(it) }
}
}
}
}
// TODO this is a dummy and incorrect implementation just to satisfy some tests
override fun getContainingModule(symbol: KtSymbol): KtModule {
val descriptor = symbol.getDescriptor()
val descriptor = when (symbol) {
is KtValueParameterSymbol -> {
val paramDescriptor = symbol.getDescriptor()
(paramDescriptor as? ValueParameterDescriptor)?.containingDeclaration ?: paramDescriptor
}
is KtPropertyAccessorSymbol -> {
val accessorDescriptor = symbol.getDescriptor()
(accessorDescriptor as? PropertyAccessorDescriptor)?.correspondingProperty ?: accessorDescriptor
}
else ->
symbol.getDescriptor()
}
val symbolPsi = descriptor?.let(DescriptorToSourceUtils::getContainingFile) ?: symbol.psi
if (symbolPsi != null) {
@@ -75,7 +156,24 @@ internal class KtFe10SymbolContainingDeclarationProvider(
}
private fun getFakeContainingKtModule(descriptor: DescriptorWithContainerSource): KtModule {
val libraryPath = Paths.get((descriptor.containerSource as JvmPackagePartSource).knownJvmBinaryClass?.containingLibrary!!)
val library = when (val containerSource = descriptor.containerSource) {
is JvmPackagePartSource -> containerSource.knownJvmBinaryClass?.containingLibrary
is KotlinJvmBinarySourceElement -> containerSource.binaryClass.containingLibrary
else -> {
when (val containingDeclaration = descriptor.containingDeclaration) {
is DescriptorWithContainerSource -> {
// Deserialized member
return getFakeContainingKtModule(containingDeclaration)
}
is LazyJavaPackageFragment -> {
// Deserialized top-level
(containingDeclaration.source as KotlinJvmBinarySourceElement).binaryClass.containingLibrary
}
else -> null
}
}
} ?: TODO(descriptor::class.java.name)
val libraryPath = Paths.get(library)
return object : KtLibraryModule {
override val libraryName: String = libraryPath.fileName.toString().substringBeforeLast(".")
override val librarySources: KtLibrarySourceModule? = null
@@ -139,8 +139,8 @@ internal fun KtSymbol.getDescriptor(): DeclarationDescriptor? {
is KtFe10DescSymbol<*> -> descriptor
is KtFe10DescSyntheticFieldSymbol -> descriptor
is KtFe10PsiDefaultPropertyGetterSymbol -> descriptor
is KtFe10PsiDefaultPropertySetterSymbol -> descriptor
is KtFe10PsiDefaultSetterParameterSymbol -> descriptor
is KtFe10PsiDefaultPropertySetterSymbol -> null
is KtFe10DescDefaultPropertySetterSymbol -> null
is KtFe10DynamicFunctionDescValueParameterSymbol -> null
is KtFe10FileSymbol -> null
@@ -172,6 +172,12 @@ public class Fe10IdeNormalAnalysisSourceModuleSymbolByPsiTestGenerated extends A
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/extensionFunction.kt");
}
@Test
@TestMetadata("facadeWithJvmName.kt")
public void testFacadeWithJvmName() throws Exception {
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/facadeWithJvmName.kt");
}
@Test
@TestMetadata("forLoopVariable.kt")
public void testForLoopVariable() throws Exception {
@@ -250,6 +256,12 @@ public class Fe10IdeNormalAnalysisSourceModuleSymbolByPsiTestGenerated extends A
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/memberProperties.kt");
}
@Test
@TestMetadata("multifilePart.kt")
public void testMultifilePart() throws Exception {
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/multifilePart.kt");
}
@Test
@TestMetadata("outerAndInnerClasses.kt")
public void testOuterAndInnerClasses() throws Exception {
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtRealSourceElementKind
import org.jetbrains.kotlin.analysis.api.components.KtSymbolContainingDeclarationProvider
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirReceiverParameterSymbol
import org.jetbrains.kotlin.analysis.api.fir.utils.firSymbol
import org.jetbrains.kotlin.analysis.api.fir.utils.getContainingKtModule
import org.jetbrains.kotlin.analysis.api.fir.utils.withSymbolAttachment
@@ -17,20 +18,26 @@ import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.jvmClassNameIfDeserialized
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getContainingFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalDeclaration
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.utils.printer.parentOfType
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.fir.FirElementWithResolveState
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirScript
import org.jetbrains.kotlin.fir.diagnostics.ConeDestructuringDeclarationsOnTopLevel
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirErrorPropertySymbol
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.psi
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
internal class KtFirSymbolContainingDeclarationProvider(
@@ -53,7 +60,7 @@ internal class KtFirSymbolContainingDeclarationProvider(
if (firSymbol is FirErrorPropertySymbol && firSymbol.diagnostic is ConeDestructuringDeclarationsOnTopLevel) return null
fun getParentSymbolByPsi() = getContainingPsi(symbol).let { with(analysisSession) { it.getSymbol() } }
return when (symbol) {
is KtPropertyAccessorSymbol -> firSymbolBuilder.buildSymbol(symbol.firSymbol.fir.propertySymbol) as? KtDeclarationSymbol
is KtPropertyAccessorSymbol -> firSymbolBuilder.buildSymbol(symbol.firSymbol.propertySymbol) as? KtDeclarationSymbol
is KtBackingFieldSymbol -> symbol.owningProperty
is KtTypeParameterSymbol -> firSymbolBuilder.buildSymbol(symbol.firSymbol.containingDeclarationSymbol) as? KtDeclarationSymbol
is KtLocalVariableSymbol -> getParentSymbolByPsi()
@@ -98,11 +105,56 @@ internal class KtFirSymbolContainingDeclarationProvider(
}
}
override fun getContainingFileSymbol(symbol: KtSymbol): KtFileSymbol? {
if (symbol is KtFileSymbol) return null
val firSymbol = when (symbol) {
is KtFirReceiverParameterSymbol -> {
// symbol from receiver parameter
symbol.firSymbol
}
else -> {
// general FIR-based symbol
symbol.firSymbol
}
}
val firFileSymbol = firSymbol.fir.getContainingFile()?.symbol ?: return null
return firSymbolBuilder.buildFileSymbol(firFileSymbol)
}
override fun getContainingJvmClassName(symbol: KtCallableSymbol): JvmClassName? {
val platform = getContainingModule(symbol).platform
if (!platform.isCommon() && !platform.isJvm()) return null
val containingSymbolOrSelf = when (symbol) {
is KtValueParameterSymbol -> {
getContainingDeclaration(symbol) as? KtFunctionLikeSymbol ?: symbol
}
is KtPropertyAccessorSymbol -> {
getContainingDeclaration(symbol) as? KtPropertySymbol ?: symbol
}
is KtBackingFieldSymbol -> symbol.owningProperty
else -> symbol
}
val firSymbol = containingSymbolOrSelf.firSymbol
firSymbol.jvmClassNameIfDeserialized()?.let { return it }
return if (containingSymbolOrSelf.symbolKind == KtSymbolKind.TOP_LEVEL) {
(firSymbol.fir.getContainingFile()?.psi as? KtFile)
?.takeUnless { it.isScript() }
?.let { JvmClassName.byFqNameWithoutInnerClasses(it.javaFileFacadeFqName) }
} else {
val classId = (containingSymbolOrSelf as? KtConstructorSymbol)?.containingClassIdIfNonLocal
?: containingSymbolOrSelf.callableIdIfNonLocal?.classId
classId?.takeUnless { it.shortClassName.isSpecial }
?.let { JvmClassName.byClassId(it) }
}
}
override fun getContainingModule(symbol: KtSymbol): KtModule {
return symbol.getContainingKtModule(analysisSession.firResolveSession)
}
private fun getContainingPsi(symbol: KtSymbol): KtDeclaration {
val source = symbol.firSymbol.source
val thisSource = when (source?.kind) {
@@ -172,6 +172,12 @@ public class FirIdeNormalAnalysisSourceModuleSymbolByPsiTestGenerated extends Ab
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/extensionFunction.kt");
}
@Test
@TestMetadata("facadeWithJvmName.kt")
public void testFacadeWithJvmName() throws Exception {
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/facadeWithJvmName.kt");
}
@Test
@TestMetadata("forLoopVariable.kt")
public void testForLoopVariable() throws Exception {
@@ -250,6 +256,12 @@ public class FirIdeNormalAnalysisSourceModuleSymbolByPsiTestGenerated extends Ab
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/memberProperties.kt");
}
@Test
@TestMetadata("multifilePart.kt")
public void testMultifilePart() throws Exception {
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/multifilePart.kt");
}
@Test
@TestMetadata("outerAndInnerClasses.kt")
public void testOuterAndInnerClasses() throws Exception {
@@ -6,12 +6,12 @@
package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components.containingDeclarationProvider
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.checkContainingFileSymbol
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.checkContainingJvmClassName
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtScriptSymbol
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiSingleFileTest
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScriptInitializer
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
@@ -19,7 +19,9 @@ import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractContainingDeclarationProviderByPsiTest : AbstractAnalysisApiSingleFileTest() {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val currentPath = mutableListOf<KtDeclaration>()
val ktClasses = mutableListOf<KtClassOrObject>()
analyseForTest(ktFile.declarations.first()) {
val expectedFileSymbol = ktFile.getFileSymbol()
ktFile.accept(object : KtVisitorVoid() {
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
@@ -39,9 +41,20 @@ abstract class AbstractContainingDeclarationProviderByPsiTest : AbstractAnalysis
}
}
checkContainingFileSymbol(expectedFileSymbol, currentDeclarationSymbol, testServices)
if (currentDeclarationSymbol is KtCallableSymbol) {
checkContainingJvmClassName(ktFile, ktClasses.lastOrNull(), currentDeclarationSymbol, testServices)
}
currentPath.add(dcl)
if (dcl is KtClassOrObject) {
ktClasses.add(dcl)
}
super.visitDeclaration(dcl)
currentPath.removeLast()
if (dcl is KtClassOrObject) {
ktClasses.removeLast()
}
}
})
}
@@ -9,6 +9,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiRecursiveElementVisitor
import org.jetbrains.kotlin.analysis.api.renderer.declarations.impl.KtDeclarationRendererForDebug
import org.jetbrains.kotlin.analysis.api.symbols.DebugSymbolRenderer
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter
@@ -35,9 +36,12 @@ abstract class AbstractMultiModuleSymbolByPsiTest : AbstractAnalysisApiBasedTest
prettyPrinter.appendLine(fileDirective)
analyseForTest(file) {
val fileSymbol = file.getFileSymbol()
file.forEachDescendantOfType<KtDeclaration>(predicate = { it.isValidForSymbolCreation }) { declaration ->
val symbol = declaration.getSymbol()
checkContainingFileSymbol(fileSymbol, symbol, testServices)
debugPrinter.appendLine(debugRenderer.render(symbol))
debugPrinter.appendLine()
@@ -91,6 +91,9 @@ abstract class AbstractSymbolTest : AbstractAnalysisApiSingleFileTest() {
val pointersWithRendered = executeOnPooledThreadInReadAction {
analyseForTest(ktFile) {
val (symbols, symbolForPrettyRendering) = collectSymbols(ktFile, testServices)
for (symbol in symbols) {
checkContainingFileSymbol(ktFile.getFileSymbol(), symbol, testServices)
}
val pointerWithRenderedSymbol = symbols
.asSequence()
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2023 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.analysis.api.impl.base.test.cases.symbols
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
context(KtAnalysisSession)
internal fun checkContainingFileSymbol(
ktFileSymbol: KtFileSymbol,
symbol: KtSymbol,
testServices: TestServices
) {
if (symbol.origin != KtSymbolOrigin.SOURCE) return
val containingFileSymbol = symbol.getContainingFileSymbol()
testServices.assertions.assertEquals(ktFileSymbol, containingFileSymbol) {
"Invalid file for $symbol, expected $ktFileSymbol but $containingFileSymbol found"
}
}
context(KtAnalysisSession)
internal fun checkContainingJvmClassName(
ktFile: KtFile,
ktClass: KtClassOrObject?,
symbol: KtCallableSymbol,
testServices: TestServices
) {
if (ktFile.isScript()) return
val expectedClassName = when {
symbol.symbolKind == KtSymbolKind.LOCAL ->
null
ktClass != null ->
// member
ktClass.getClassId()?.let { JvmClassName.byClassId(it) }
else ->
// top-level
JvmClassName.byFqNameWithoutInnerClasses(ktFile.javaFileFacadeFqName)
}
val actualClassName = symbol.getContainingJvmClassName()
testServices.assertions.assertEquals(expectedClassName, actualClassName) {
"Invalid JvmClassName for $symbol, expected $expectedClassName but $actualClassName found"
}
}
@@ -172,6 +172,12 @@ public class FirStandaloneNormalAnalysisSourceModuleSymbolByPsiTestGenerated ext
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/extensionFunction.kt");
}
@Test
@TestMetadata("facadeWithJvmName.kt")
public void testFacadeWithJvmName() throws Exception {
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/facadeWithJvmName.kt");
}
@Test
@TestMetadata("forLoopVariable.kt")
public void testForLoopVariable() throws Exception {
@@ -250,6 +256,12 @@ public class FirStandaloneNormalAnalysisSourceModuleSymbolByPsiTestGenerated ext
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/memberProperties.kt");
}
@Test
@TestMetadata("multifilePart.kt")
public void testMultifilePart() throws Exception {
runTest("analysis/analysis-api/testData/symbols/symbolByPsi/multifilePart.kt");
}
@Test
@TestMetadata("outerAndInnerClasses.kt")
public void testOuterAndInnerClasses() throws Exception {
@@ -6,14 +6,20 @@
package org.jetbrains.kotlin.analysis.api.components
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtDeclarationSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtFileSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
public abstract class KtSymbolContainingDeclarationProvider : KtAnalysisSessionComponent() {
public abstract fun getContainingDeclaration(symbol: KtSymbol): KtDeclarationSymbol?
public abstract fun getContainingFileSymbol(symbol: KtSymbol): KtFileSymbol?
public abstract fun getContainingJvmClassName(symbol: KtCallableSymbol): JvmClassName?
public abstract fun getContainingModule(symbol: KtSymbol): KtModule
}
@@ -27,6 +33,25 @@ public interface KtSymbolContainingDeclarationProviderMixIn : KtAnalysisSessionM
public fun KtSymbol.getContainingSymbol(): KtDeclarationSymbol? =
withValidityAssertion { analysisSession.containingDeclarationProvider.getContainingDeclaration(this) }
/**
* Returns containing [KtFile] as [KtFileSymbol]
*
* Caveat: returns null if the given symbol is already [KtFileSymbol], since there is no containing file.
*/
public fun KtSymbol.getContainingFileSymbol(): KtFileSymbol? =
withValidityAssertion { analysisSession.containingDeclarationProvider.getContainingFileSymbol(this) }
/**
* Returns containing class's [JvmClassName] if any for [KtCallableSymbol]
*
* even for deserialized callables! (which is useful to look up the containing facade in [PsiElement])
* for regular, non-local callables from source, it is a mere conversion of [ClassId] inside [CallableId]
*
* Note that this API is applicable for common or JVM modules only.
*/
public fun KtCallableSymbol.getContainingJvmClassName(): JvmClassName? =
withValidityAssertion { analysisSession.containingDeclarationProvider.getContainingJvmClassName(this) }
public fun KtSymbol.getContainingModule(): KtModule =
withValidityAssertion { analysisSession.containingDeclarationProvider.getContainingModule(this) }
}
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.types.Variance
@@ -71,9 +72,24 @@ public class DebugSymbolRenderer(
KtSymbolContainingDeclarationProviderMixIn::class
.declaredMemberExtensionFunctions
.filter { it.name == "getContainingModule" }
.filterNot {
// Rendering a containing symbol is prone to stack overflow.
// * function symbol will render its value parameter symbol(s)
// whose containing symbol is that function symbol.
// * property symbol contains accessor symbol(s) and/or backing field symbol
// whose containing symbol is that property symbol.
it.name == "getContainingSymbol"
}
.forEach {
appendLine()
renderFunction(it, renderSymbolsFully = false, this@KtAnalysisSession, symbol)
if (it.name == "getContainingJvmClassName") {
if (symbol is KtCallableSymbol) {
appendLine()
renderFunction(it, renderSymbolsFully = false, this@KtAnalysisSession, symbol)
}
} else {
appendLine()
renderFunction(it, renderSymbolsFully = false, this@KtAnalysisSession, symbol)
}
}
KtSymbolInfoProviderMixIn::class.declaredMemberExtensionProperties
@@ -188,6 +204,7 @@ public class DebugSymbolRenderer(
is KtClassLikeSymbol -> renderId(symbol.classIdIfNonLocal, symbol)
is KtCallableSymbol -> renderId(symbol.callableIdIfNonLocal, symbol)
is KtNamedSymbol -> renderValue(symbol.name, renderSymbolsFully = false)
is KtFileSymbol -> renderValue((symbol.psi as KtFile).name, renderSymbolsFully = false)
else -> error("Unsupported symbol ${symbol::class}")
}
append(")")
@@ -0,0 +1,10 @@
// DO_NOT_CHECK_NON_PSI_SYMBOL_RESTORE_K1
@file:kotlin.jvm.JvmName("DifferentFacadeName")
fun foo() {}
fun String.foo(): Int = 42
val x: Int = 42
val Int.y get() = this
@@ -0,0 +1,8 @@
fun foo()
fun kotlin.String.foo(): kotlin.Int
val x: kotlin.Int
val kotlin.Int.y: kotlin.Int
get()
@@ -0,0 +1,244 @@
KtFunctionSymbol:
annotationsList: []
callableIdIfNonLocal: /foo
contextReceivers: []
contractEffects: []
hasStableParameterNames: true
isActual: false
isBuiltinFunctionInvoke: false
isExpect: false
isExtension: false
isExternal: false
isInfix: false
isInline: false
isOperator: false
isOverride: false
isStatic: false
isSuspend: false
modality: FINAL
name: foo
origin: SOURCE
receiverParameter: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Unit
symbolKind: TOP_LEVEL
typeParameters: []
valueParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
KtFunctionSymbol:
annotationsList: []
callableIdIfNonLocal: /foo
contextReceivers: []
contractEffects: []
hasStableParameterNames: true
isActual: false
isBuiltinFunctionInvoke: false
isExpect: false
isExtension: true
isExternal: false
isInfix: false
isInline: false
isOperator: false
isOverride: false
isStatic: false
isSuspend: false
modality: FINAL
name: foo
origin: SOURCE
receiverParameter: KtReceiverParameterSymbol:
annotationsList: []
origin: SOURCE
owningCallableSymbol: KtFunctionSymbol(/foo)
type: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/String
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
symbolKind: TOP_LEVEL
typeParameters: []
valueParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
KtKotlinPropertySymbol:
annotationsList: []
backingFieldSymbol: KtBackingFieldSymbol:
annotationsList: []
callableIdIfNonLocal: null
contextReceivers: []
isExtension: false
name: field
origin: PROPERTY_BACKING_FIELD
owningProperty: KtKotlinPropertySymbol(/x)
receiverParameter: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
symbolKind: LOCAL
typeParameters: []
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
callableIdIfNonLocal: /x
contextReceivers: []
getter: KtPropertyGetterSymbol:
annotationsList: []
callableIdIfNonLocal: null
contextReceivers: []
hasBody: false
hasStableParameterNames: true
isDefault: true
isExtension: false
isInline: false
isOverride: false
modality: FINAL
origin: SOURCE
receiverParameter: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
symbolKind: ACCESSOR
typeParameters: []
valueParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
hasBackingField: true
hasGetter: true
hasSetter: false
initializer: KtConstantInitializerValue(42)
isActual: false
isConst: false
isDelegatedProperty: false
isExpect: false
isExtension: false
isFromPrimaryConstructor: false
isLateInit: false
isOverride: false
isStatic: false
isVal: true
modality: FINAL
name: x
origin: SOURCE
receiverParameter: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
setter: null
symbolKind: TOP_LEVEL
typeParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
getterDeprecationStatus: null
javaGetterName: getX
javaSetterName: null
setterDeprecationStatus: null
KtKotlinPropertySymbol:
annotationsList: []
backingFieldSymbol: KtBackingFieldSymbol:
annotationsList: []
callableIdIfNonLocal: null
contextReceivers: []
isExtension: false
name: field
origin: PROPERTY_BACKING_FIELD
owningProperty: KtKotlinPropertySymbol(/y)
receiverParameter: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
symbolKind: LOCAL
typeParameters: []
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
callableIdIfNonLocal: /y
contextReceivers: []
getter: KtPropertyGetterSymbol:
annotationsList: []
callableIdIfNonLocal: null
contextReceivers: []
hasBody: true
hasStableParameterNames: true
isDefault: false
isExtension: false
isInline: false
isOverride: false
modality: FINAL
origin: SOURCE
receiverParameter: KtReceiverParameterSymbol:
annotationsList: []
origin: SOURCE
owningCallableSymbol: KtKotlinPropertySymbol(/y)
type: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
symbolKind: ACCESSOR
typeParameters: []
valueParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
hasBackingField: false
hasGetter: true
hasSetter: false
initializer: null
isActual: false
isConst: false
isDelegatedProperty: false
isExpect: false
isExtension: true
isFromPrimaryConstructor: false
isLateInit: false
isOverride: false
isStatic: false
isVal: true
modality: FINAL
name: y
origin: SOURCE
receiverParameter: KtReceiverParameterSymbol:
annotationsList: []
origin: SOURCE
owningCallableSymbol: KtKotlinPropertySymbol(/y)
type: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
setter: null
symbolKind: TOP_LEVEL
typeParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
getterDeprecationStatus: null
javaGetterName: getY
javaSetterName: null
setterDeprecationStatus: null
@@ -0,0 +1,11 @@
// DO_NOT_CHECK_NON_PSI_SYMBOL_RESTORE_K1
@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("NotMultiFilePartKt")
fun foo() {}
fun String.foo(): Int = 42
val x: Int = 42
val Int.y get() = this
@@ -0,0 +1,8 @@
fun foo()
fun kotlin.String.foo(): kotlin.Int
val x: kotlin.Int
val kotlin.Int.y: kotlin.Int
get()
@@ -0,0 +1,244 @@
KtFunctionSymbol:
annotationsList: []
callableIdIfNonLocal: /foo
contextReceivers: []
contractEffects: []
hasStableParameterNames: true
isActual: false
isBuiltinFunctionInvoke: false
isExpect: false
isExtension: false
isExternal: false
isInfix: false
isInline: false
isOperator: false
isOverride: false
isStatic: false
isSuspend: false
modality: FINAL
name: foo
origin: SOURCE
receiverParameter: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Unit
symbolKind: TOP_LEVEL
typeParameters: []
valueParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
KtFunctionSymbol:
annotationsList: []
callableIdIfNonLocal: /foo
contextReceivers: []
contractEffects: []
hasStableParameterNames: true
isActual: false
isBuiltinFunctionInvoke: false
isExpect: false
isExtension: true
isExternal: false
isInfix: false
isInline: false
isOperator: false
isOverride: false
isStatic: false
isSuspend: false
modality: FINAL
name: foo
origin: SOURCE
receiverParameter: KtReceiverParameterSymbol:
annotationsList: []
origin: SOURCE
owningCallableSymbol: KtFunctionSymbol(/foo)
type: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/String
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
symbolKind: TOP_LEVEL
typeParameters: []
valueParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
KtKotlinPropertySymbol:
annotationsList: []
backingFieldSymbol: KtBackingFieldSymbol:
annotationsList: []
callableIdIfNonLocal: null
contextReceivers: []
isExtension: false
name: field
origin: PROPERTY_BACKING_FIELD
owningProperty: KtKotlinPropertySymbol(/x)
receiverParameter: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
symbolKind: LOCAL
typeParameters: []
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
callableIdIfNonLocal: /x
contextReceivers: []
getter: KtPropertyGetterSymbol:
annotationsList: []
callableIdIfNonLocal: null
contextReceivers: []
hasBody: false
hasStableParameterNames: true
isDefault: true
isExtension: false
isInline: false
isOverride: false
modality: FINAL
origin: SOURCE
receiverParameter: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
symbolKind: ACCESSOR
typeParameters: []
valueParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
hasBackingField: true
hasGetter: true
hasSetter: false
initializer: KtConstantInitializerValue(42)
isActual: false
isConst: false
isDelegatedProperty: false
isExpect: false
isExtension: false
isFromPrimaryConstructor: false
isLateInit: false
isOverride: false
isStatic: false
isVal: true
modality: FINAL
name: x
origin: SOURCE
receiverParameter: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
setter: null
symbolKind: TOP_LEVEL
typeParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
getterDeprecationStatus: null
javaGetterName: getX
javaSetterName: null
setterDeprecationStatus: null
KtKotlinPropertySymbol:
annotationsList: []
backingFieldSymbol: KtBackingFieldSymbol:
annotationsList: []
callableIdIfNonLocal: null
contextReceivers: []
isExtension: false
name: field
origin: PROPERTY_BACKING_FIELD
owningProperty: KtKotlinPropertySymbol(/y)
receiverParameter: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
symbolKind: LOCAL
typeParameters: []
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
callableIdIfNonLocal: /y
contextReceivers: []
getter: KtPropertyGetterSymbol:
annotationsList: []
callableIdIfNonLocal: null
contextReceivers: []
hasBody: true
hasStableParameterNames: true
isDefault: false
isExtension: false
isInline: false
isOverride: false
modality: FINAL
origin: SOURCE
receiverParameter: KtReceiverParameterSymbol:
annotationsList: []
origin: SOURCE
owningCallableSymbol: KtKotlinPropertySymbol(/y)
type: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
symbolKind: ACCESSOR
typeParameters: []
valueParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
hasBackingField: false
hasGetter: true
hasSetter: false
initializer: null
isActual: false
isConst: false
isDelegatedProperty: false
isExpect: false
isExtension: true
isFromPrimaryConstructor: false
isLateInit: false
isOverride: false
isStatic: false
isVal: true
modality: FINAL
name: y
origin: SOURCE
receiverParameter: KtReceiverParameterSymbol:
annotationsList: []
origin: SOURCE
owningCallableSymbol: KtKotlinPropertySymbol(/y)
type: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
returnType: KtUsualClassType:
annotationsList: []
ownTypeArguments: []
type: kotlin/Int
setter: null
symbolKind: TOP_LEVEL
typeParameters: []
visibility: Public
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
getterDeprecationStatus: null
javaGetterName: getY
javaSetterName: null
setterDeprecationStatus: null
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.JvmStubDeserializedBuiltInsContainerSource
import org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.StubBasedFirDeserializedSymbolProvider
import org.jetbrains.kotlin.analysis.utils.collections.buildSmartList
import org.jetbrains.kotlin.fir.FirSession
@@ -17,7 +16,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.load.kotlin.FacadeClassSource
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -169,7 +167,7 @@ internal class LLFirDependenciesSymbolProvider(
if (newSymbols.isEmpty()) return
val newFacades = SmartSet.create<JvmClassName>()
for (symbol in newSymbols) {
val facade = symbol.jvmClassName()
val facade = symbol.jvmClassNameIfDeserialized()
if (facade != null) {
newFacades += facade
if (facade !in facades) {
@@ -181,16 +179,4 @@ internal class LLFirDependenciesSymbolProvider(
}
facades += newFacades
}
private fun FirCallableSymbol<*>.jvmClassName(): JvmClassName? {
val containerSource = fir.containerSource
return when (containerSource) {
is JvmStubDeserializedBuiltInsContainerSource -> containerSource.facadeClassName
is FacadeClassSource -> containerSource.facadeClassName ?: containerSource.className
else -> null
}
}
}
@@ -5,6 +5,22 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.DeserializedContainerSourceWithJvmClassName
import org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.JvmStubDeserializedBuiltInsContainerSource
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.load.kotlin.FacadeClassSource
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import java.util.*
internal fun <T: Any> Optional<T>.getOrNull(): T? = orElse(null)
fun FirCallableSymbol<*>.jvmClassNameIfDeserialized(): JvmClassName? {
return when (val containerSource = fir.containerSource) {
is JvmStubDeserializedBuiltInsContainerSource -> containerSource.facadeClassName
is FacadeClassSource -> containerSource.facadeClassName ?: containerSource.className
is DeserializedContainerSourceWithJvmClassName -> containerSource.className
is KotlinJvmBinarySourceElement -> JvmClassName.byClassId(containerSource.binaryClass.classId)
else -> null
}
}
@@ -0,0 +1,13 @@
/*
* Copyright 2010-2023 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.analysis.low.level.api.fir.stubBased.deserialization
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
interface DeserializedContainerSourceWithJvmClassName : DeserializedContainerSource {
val className: JvmClassName
}
@@ -14,8 +14,8 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
//required for LLFirDependenciesSymbolProvider#jvmClassName, to resolve ambiguities
//todo check if moving builtins to stubs would solve the issue
internal class JvmStubDeserializedContainerSource(classId: ClassId) : DeserializedContainerSource {
private val className = JvmClassName.byClassId(classId)
internal class JvmStubDeserializedContainerSource(classId: ClassId) : DeserializedContainerSourceWithJvmClassName {
override val className = JvmClassName.byClassId(classId)
override val incompatibility: IncompatibleVersionErrorData<*>?
get() = null
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
internal class JvmStubDeserializedFacadeContainerSource(
override val className: JvmClassName,
override val facadeClassName: JvmClassName?
) : DeserializedContainerSource, FacadeClassSource {
) : DeserializedContainerSourceWithJvmClassName, FacadeClassSource {
override val incompatibility: IncompatibleVersionErrorData<*>?
get() = null
@@ -25,7 +25,17 @@ fun FirElementWithResolveState.getContainingFile(): FirFile? {
is FirBackingField -> propertySymbol.fir.getContainingFile()
is FirCallableDeclaration -> provider.getFirCallableContainerFile(symbol)
is FirClassLikeDeclaration -> provider.getFirClassifierContainerFileIfAny(symbol)
is FirAnonymousInitializer -> containingDeclarationSymbol?.fir?.getContainingFile()
is FirAnonymousInitializer -> {
val classId = containingClassIdOrNull()
if (classId?.isLocal == true) {
containingKtFileIfAny?.let {
val moduleComponents = llFirResolvableSession?.moduleComponents
moduleComponents?.cache?.getCachedFirFile(it)
}
} else {
containingDeclarationSymbol?.fir?.getContainingFile()
}
}
is FirDanglingModifierList, is FirCodeFragment -> {
val ktFile = psi?.containingFile as? KtFile
?: error("File for dangling modifier list cannot be null")