[Analysis API] introduce KtDestructuringDeclarationSymbol to represent a destructuring declaration

Mainly needed to avoid "Cannot build symbol for KtDestructuringDeclaration" from `KtSymbolProvider.getSymbol` on `KtDestructuringDeclaration`.
This commit is contained in:
Ilya Kirillov
2023-08-07 15:02:28 +02:00
committed by Space Team
parent 1b0034b6e7
commit 8ad5a48b98
20 changed files with 338 additions and 1 deletions
@@ -149,4 +149,8 @@ internal class KtFe10SymbolProvider(
override fun getDestructuringDeclarationEntrySymbol(psi: KtDestructuringDeclarationEntry): KtLocalVariableSymbol {
return KtFe10PsiLocalVariableSymbol(psi, analysisContext)
}
override fun getDestructuringDeclarationSymbol(psi: KtDestructuringDeclaration): KtDestructuringDeclarationSymbol {
return KtFe10PsiDestructuringDeclarationSymbol(psi, analysisSession)
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2022 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.descriptors.symbols.psiBased
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisContext
import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade.AnalysisMode
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.psiBased.base.KtFe10PsiSymbol
import org.jetbrains.kotlin.analysis.api.descriptors.utils.cached
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.symbols.KtDestructuringDeclarationSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtLocalVariableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.pointers.CanNotCreateSymbolPointerForLocalLibraryDeclarationException
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtPsiBasedSymbolPointer
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.resolve.BindingContext
internal class KtFe10PsiDestructuringDeclarationSymbol(
override val psi: KtDestructuringDeclaration,
private val analysisSession: KtFe10AnalysisSession,
) : KtDestructuringDeclarationSymbol(), KtFe10PsiSymbol<KtDestructuringDeclaration, CallableDescriptor> {
override val descriptor: CallableDescriptor? get() = null
override val annotationsObject: Annotations by cached {
val bindingContext = analysisContext.analyze(psi, AnalysisMode.PARTIAL)
Annotations.create(
psi.annotationEntries.mapNotNull { entry ->
bindingContext[BindingContext.ANNOTATION, entry]
}
)
}
override val analysisContext: Fe10AnalysisContext get() = analysisSession.analysisContext
override val entries: List<KtLocalVariableSymbol>
get() = withValidityAssertion {
psi.entries.map { entry ->
with(analysisSession) { entry.getDestructuringDeclarationEntrySymbol() }
}
}
context(KtAnalysisSession)
override fun createPointer(): KtSymbolPointer<KtDestructuringDeclarationSymbol> {
KtPsiBasedSymbolPointer.createForSymbolFromSource<KtDestructuringDeclarationSymbol>(this)?.let { return it }
throw CanNotCreateSymbolPointerForLocalLibraryDeclarationException(SpecialNames.DESTRUCT.asString())
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
return other is KtFe10PsiDestructuringDeclarationSymbol && other.psi == this.psi
}
override fun hashCode(): Int = psi.hashCode()
}
@@ -323,6 +323,18 @@ public class Fe10IdeNormalAnalysisSourceModuleSingleSymbolByPsiGenerated extends
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/symbols/singleSymbolByPsi/destructuring"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("destructuringDeclaration.kt")
public void testDestructuringDeclaration() throws Exception {
runTest("analysis/analysis-api/testData/symbols/singleSymbolByPsi/destructuring/destructuringDeclaration.kt");
}
@Test
@TestMetadata("destructuringDeclarationInLambda.kt")
public void testDestructuringDeclarationInLambda() throws Exception {
runTest("analysis/analysis-api/testData/symbols/singleSymbolByPsi/destructuring/destructuringDeclarationInLambda.kt");
}
@Test
@TestMetadata("destructuringDeclarationParameterInLambda.kt")
public void testDestructuringDeclarationParameterInLambda() throws Exception {
@@ -101,6 +101,13 @@ internal class KtSymbolByFirBuilder constructor(
}
}
fun buildDestructuringDeclarationSymbol(firSymbol: FirVariableSymbol<*>): KtDestructuringDeclarationSymbol {
return symbolsCache.cache(firSymbol) {
KtFirDestructuringDeclarationSymbol(firSymbol, analysisSession)
}
}
fun buildEnumEntrySymbol(firSymbol: FirEnumEntrySymbol) =
symbolsCache.cache(firSymbol) { KtFirEnumEntrySymbol(firSymbol, analysisSession) }
@@ -50,6 +50,7 @@ internal class KtFirSymbolContainingDeclarationProvider(
is KtLocalVariableSymbol -> getParentSymbolByPsi()
is KtAnonymousFunctionSymbol -> getParentSymbolByPsi()
is KtAnonymousObjectSymbol -> getParentSymbolByPsi()
is KtDestructuringDeclarationSymbol -> getParentSymbolByPsi()
is KtSamConstructorSymbol -> null // SAM constructors are always top-level
is KtScriptSymbol -> null // Scripts are always top-level
@@ -0,0 +1,73 @@
/*
* 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.fir.symbols
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationsList
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.analysis.api.fir.annotations.KtFirAnnotationListForDeclaration
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.symbols.KtDestructuringDeclarationSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtLocalVariableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.pointers.CanNotCreateSymbolPointerForLocalLibraryDeclarationException
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtPsiBasedSymbolPointer
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
import org.jetbrains.kotlin.utils.exceptions.withPsiEntry
internal class KtFirDestructuringDeclarationSymbol(
override val firSymbol: FirVariableSymbol<*>,
override val analysisSession: KtFirAnalysisSession,
) : KtDestructuringDeclarationSymbol(), KtFirSymbol<FirVariableSymbol<*>> {
init {
require(firSymbol.name == SpecialNames.DESTRUCT)
}
override val psi: KtDestructuringDeclaration
get() = withValidityAssertion {
when (val psi = firSymbol.fir.psi) {
is KtDestructuringDeclaration -> psi
is KtParameter -> psi.destructuringDeclaration
?: errorWithAttachment("Expected to find lambda ${KtParameter::class} with ${KtDestructuringDeclaration::class}") {
withPsiEntry("psi", psi)
}
else -> errorWithAttachment("Expected to find ${KtDestructuringDeclaration::class} or ${KtParameter::class} but ${psi?.let { it::class }} found") {
withPsiEntry("psi", psi)
}
}
}
override val annotationsList: KtAnnotationsList
get() = withValidityAssertion { KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token) }
override val entries: List<KtLocalVariableSymbol>
get() = withValidityAssertion {
psi.entries.map { entry ->
with(analysisSession) { entry.getDestructuringDeclarationEntrySymbol() }
}
}
context(KtAnalysisSession)
override fun createPointer(): KtSymbolPointer<KtDestructuringDeclarationSymbol> {
return KtPsiBasedSymbolPointer.createForSymbolFromSource<KtDestructuringDeclarationSymbol>(this)
?: throw CanNotCreateSymbolPointerForLocalLibraryDeclarationException(SpecialNames.DESTRUCT.asString())
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
return other is KtFirDestructuringDeclarationSymbol && firSymbol == other.firSymbol
}
override fun hashCode(): Int {
return firSymbol.hashCode()
}
}
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral
import org.jetbrains.kotlin.resolve.calls.util.isSingleUnderscore
internal class KtFirSymbolProvider(
override val analysisSession: KtFirAnalysisSession,
@@ -198,4 +197,9 @@ internal class KtFirSymbolProvider(
)
}
}
override fun getDestructuringDeclarationSymbol(psi: KtDestructuringDeclaration): KtDestructuringDeclarationSymbol {
val firSymbol = psi.resolveToFirSymbolOfType<FirVariableSymbol<*>>(firResolveSession)
return firSymbolBuilder.buildDestructuringDeclarationSymbol(firSymbol)
}
}
@@ -323,6 +323,18 @@ public class FirIdeNormalAnalysisSourceModuleSingleSymbolByPsiGenerated extends
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/symbols/singleSymbolByPsi/destructuring"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("destructuringDeclaration.kt")
public void testDestructuringDeclaration() throws Exception {
runTest("analysis/analysis-api/testData/symbols/singleSymbolByPsi/destructuring/destructuringDeclaration.kt");
}
@Test
@TestMetadata("destructuringDeclarationInLambda.kt")
public void testDestructuringDeclarationInLambda() throws Exception {
runTest("analysis/analysis-api/testData/symbols/singleSymbolByPsi/destructuring/destructuringDeclarationInLambda.kt");
}
@Test
@TestMetadata("destructuringDeclarationParameterInLambda.kt")
public void testDestructuringDeclarationParameterInLambda() throws Exception {
@@ -323,6 +323,18 @@ public class FirStandaloneNormalAnalysisSourceModuleSingleSymbolByPsiGenerated e
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/symbols/singleSymbolByPsi/destructuring"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("destructuringDeclaration.kt")
public void testDestructuringDeclaration() throws Exception {
runTest("analysis/analysis-api/testData/symbols/singleSymbolByPsi/destructuring/destructuringDeclaration.kt");
}
@Test
@TestMetadata("destructuringDeclarationInLambda.kt")
public void testDestructuringDeclarationInLambda() throws Exception {
runTest("analysis/analysis-api/testData/symbols/singleSymbolByPsi/destructuring/destructuringDeclarationInLambda.kt");
}
@Test
@TestMetadata("destructuringDeclarationParameterInLambda.kt")
public void testDestructuringDeclarationParameterInLambda() throws Exception {
@@ -74,6 +74,7 @@ public class KtDeclarationRenderer private constructor(
public val valueParameterRenderer: KtValueParameterSymbolRenderer,
public val samConstructorRenderer: KtSamConstructorSymbolRenderer,
public val propertyAccessorsRenderer: KtPropertyAccessorsRenderer,
public val destructuringDeclarationRenderer: KtDestructuringDeclarationRenderer,
public val classInitializerRender: KtClassInitializerRenderer,
public val classOrObjectRenderer: KtNamedClassOrObjectSymbolRenderer,
@@ -108,6 +109,7 @@ public class KtDeclarationRenderer private constructor(
is KtTypeParameterSymbol -> singleTypeParameterRenderer.renderSymbol(symbol, printer)
is KtClassInitializerSymbol -> classInitializerRender.renderClassInitializer(symbol, printer)
is KtScriptSymbol -> scriptRenderer.renderSymbol(symbol, printer)
is KtDestructuringDeclarationSymbol -> destructuringDeclarationRenderer.renderSymbol(symbol, printer)
}
}
@@ -161,6 +163,7 @@ public class KtDeclarationRenderer private constructor(
this.valueParameterRenderer = renderer.valueParameterRenderer
this.samConstructorRenderer = renderer.samConstructorRenderer
this.propertyAccessorsRenderer = renderer.propertyAccessorsRenderer
this.destructuringDeclarationRenderer = renderer.destructuringDeclarationRenderer
this.classInitializerRender = renderer.classInitializerRender
this.classOrObjectRenderer = renderer.classOrObjectRenderer
@@ -229,6 +232,7 @@ public class KtDeclarationRenderer private constructor(
public lateinit var valueParameterRenderer: KtValueParameterSymbolRenderer
public lateinit var samConstructorRenderer: KtSamConstructorSymbolRenderer
public lateinit var propertyAccessorsRenderer: KtPropertyAccessorsRenderer
public lateinit var destructuringDeclarationRenderer: KtDestructuringDeclarationRenderer
public lateinit var classInitializerRender: KtClassInitializerRenderer
public lateinit var classOrObjectRenderer: KtNamedClassOrObjectSymbolRenderer
@@ -286,6 +290,7 @@ public class KtDeclarationRenderer private constructor(
valueParameterRenderer,
samConstructorRenderer,
propertyAccessorsRenderer,
destructuringDeclarationRenderer,
classInitializerRender,
classOrObjectRenderer,
@@ -74,6 +74,7 @@ public object KtDeclarationRendererForSource {
anonymousObjectRenderer = KtAnonymousObjectSymbolRenderer.AS_SOURCE
singleTypeParameterRenderer = KtSingleTypeParameterSymbolRenderer.WITHOUT_BOUNDS
propertyAccessorsRenderer = KtPropertyAccessorsRenderer.NO_DEFAULT
destructuringDeclarationRenderer = KtDestructuringDeclarationRenderer.WITH_ENTRIES
callableReceiverRenderer = KtCallableReceiverRenderer.AS_TYPE_WITH_IN_APPROXIMATION
returnTypeRenderer = KtCallableReturnTypeRenderer.WITH_OUT_APPROXIMATION
@@ -0,0 +1,41 @@
/*
* 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.renderer.declarations.renderers.callables
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.renderer.declarations.KtDeclarationRenderer
import org.jetbrains.kotlin.analysis.api.renderer.declarations.modifiers.renderers.KtRendererKeywordFilter
import org.jetbrains.kotlin.analysis.api.symbols.KtDestructuringDeclarationSymbol
import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter
import org.jetbrains.kotlin.lexer.KtTokens
public interface KtDestructuringDeclarationRenderer {
context(KtAnalysisSession, KtDeclarationRenderer)
public fun renderSymbol(symbol: KtDestructuringDeclarationSymbol, printer: PrettyPrinter)
public object WITH_ENTRIES : KtDestructuringDeclarationRenderer {
context(KtAnalysisSession, KtDeclarationRenderer)
override fun renderSymbol(symbol: KtDestructuringDeclarationSymbol, printer: PrettyPrinter): Unit = printer {
codeStyle.getSeparatorBetweenAnnotationAndOwner(symbol).separated(
{ annotationRenderer.renderAnnotations(symbol, printer) },
{
// do not render (val a: Int, val b: Int), render `(a: Int, b: Int)` instead
val rendererWithoutValVar = with {
keywordsRenderer = keywordsRenderer.with {
keywordFilter = keywordFilter and KtRendererKeywordFilter.without(KtTokens.VAL_KEYWORD, KtTokens.VAR_KEYWORD)
}
}
printCollection(symbol.entries, prefix = "(", postfix = ")") {
with(rendererWithoutValVar) {
localVariableRenderer.renderSymbol(it, this@printCollection)
}
}
}
)
}
}
}
@@ -0,0 +1,46 @@
/*
* 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.symbols
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
/**
* A [KtSymbol] created from a [destructuring declaration][org.jetbrains.kotlin.psi.KtDestructuringDeclaration] (possibly from a lambda parameter).
*
* Examples:
* - `val (a, _) = Pair(1, 2)` leads to `KtDestructuringDeclarationSymbol(entries = [a, _])`
* - `Pair(1, _).let { (a, b) -> }` leads to `KtDestructuringDeclarationSymbol(entries = [a, _])`
*/
public abstract class KtDestructuringDeclarationSymbol : KtDeclarationSymbol, KtSymbolWithKind {
final override val symbolKind: KtSymbolKind get() = withValidityAssertion { KtSymbolKind.LOCAL }
final override val typeParameters: List<KtTypeParameterSymbol> get() = withValidityAssertion { emptyList() }
/**
* A list of [KtLocalVariableSymbol]s which were created from this destructuring declaration.
*
* E.g., for the following code:
* ```
* data class X(val y: Int, val z: String)
* fun foo() {
* val (a, _) = x // the destruction
* }
* ```
*
* the following symbols will be created (pseudocode)
* ```
* val a: Int
* val _: String
* ```
*/
public abstract val entries: List<KtLocalVariableSymbol>
context(KtAnalysisSession)
abstract override fun createPointer(): KtSymbolPointer<KtDestructuringDeclarationSymbol>
}
@@ -32,6 +32,7 @@ public abstract class KtSymbolProvider : KtAnalysisSessionComponent() {
is KtClassInitializer -> getClassInitializerSymbol(psi)
is KtDestructuringDeclarationEntry -> getDestructuringDeclarationEntrySymbol(psi)
is KtScript -> getScriptSymbol(psi)
is KtDestructuringDeclaration -> getDestructuringDeclarationSymbol(psi)
else -> error("Cannot build symbol for ${psi::class}")
}
@@ -52,6 +53,7 @@ public abstract class KtSymbolProvider : KtAnalysisSessionComponent() {
public abstract fun getPropertyAccessorSymbol(psi: KtPropertyAccessor): KtPropertyAccessorSymbol
public abstract fun getClassInitializerSymbol(psi: KtClassInitializer): KtClassInitializerSymbol
public abstract fun getDestructuringDeclarationEntrySymbol(psi: KtDestructuringDeclarationEntry): KtLocalVariableSymbol
public abstract fun getDestructuringDeclarationSymbol(psi: KtDestructuringDeclaration): KtDestructuringDeclarationSymbol
public abstract fun getPackageSymbolIfPackageExists(packageFqName: FqName): KtPackageSymbol?
@@ -0,0 +1,15 @@
// LOOK_UP_FOR_ELEMENT_OF_TYPE: KtDestructuringDeclaration
// DO_NOT_CHECK_NON_PSI_SYMBOL_RESTORE
data class X(val a: Int, val b: Int)
fun main(x: X) {
<expr>
@AAA("y")
val (@BBB("aaa") a, @BBB("bbb") b) = x
</expr>
}
annotation class AAA(val value: String)
annotation class BBB(val value: String)
@@ -0,0 +1,2 @@
@AAA(value = "y")
(@BBB(value = "aaa") a: kotlin.Int, @BBB(value = "bbb") b: kotlin.Int)
@@ -0,0 +1,14 @@
KtDestructuringDeclarationSymbol:
annotationsList: [
AAA(value = "y")
psi: KtAnnotationEntry
]
entries: [
KtLocalVariableSymbol(<local>/a)
KtLocalVariableSymbol(<local>/b)
]
origin: SOURCE
symbolKind: LOCAL
typeParameters: []
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null
@@ -0,0 +1,12 @@
// LOOK_UP_FOR_ELEMENT_OF_TYPE: KtDestructuringDeclaration
// DO_NOT_CHECK_NON_PSI_SYMBOL_RESTORE
data class X(val a: Int, val b: Int)
fun x(action: (X) -> Unit) {}
fun main() {
x { <expr>(a, b)</expr> ->
}
}
@@ -0,0 +1 @@
(a: kotlin.Int, b: kotlin.Int)
@@ -0,0 +1,11 @@
KtDestructuringDeclarationSymbol:
annotationsList: []
entries: [
KtLocalVariableSymbol(<local>/a)
KtLocalVariableSymbol(<local>/b)
]
origin: SOURCE
symbolKind: LOCAL
typeParameters: []
getContainingModule: KtSourceModule "Sources of main"
deprecationStatus: null