[AA] KT-56617 Introduce PSI-based Java type parameter symbols

- Instead of extending the API with `typeParameterNames`, this commit
  creates a PSI-based Java type parameter symbol similarly to the
  PSI-based Java class symbol. Because completion requires only the type
  parameter name, this approach works fine, and without exposing an
  optimization via the API.
This commit is contained in:
Marco Pennekamp
2023-03-06 22:18:04 +01:00
committed by Space Team
parent 1e43e8d975
commit 037984b0e0
5 changed files with 115 additions and 63 deletions
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.analysis.project.structure.getKtModule
import org.jetbrains.kotlin.analysis.utils.classIdIfNonLocal
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
import org.jetbrains.kotlin.fir.java.classKind
import org.jetbrains.kotlin.fir.java.modality
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
@@ -82,16 +83,21 @@ internal class KtFirPsiJavaClassSymbol(
psi.containingClass?.let { KtFirPsiJavaClassSymbol(it, analysisSession) }
}
override val typeParameterNames: List<Name> by cached {
// The parent Java class might contribute `FirOuterClassTypeParameterRef`s to the FIR class's type parameters (see
// `FirJavaFacade.createFirJavaClass`), but since they are filtered out by `createRegularKtTypeParameters`, we do not need to
// include them in the list of type parameter names.
javaClass.typeParameters.map { it.name }
override val typeParameters: List<KtTypeParameterSymbol> by cached {
// The parent Java class might contribute type parameters to the Java type parameter stack, but for this KtSymbol, parent type
// parameters aren't relevant.
psi.typeParameters.mapIndexed { index, psiTypeParameter ->
KtFirPsiJavaTypeParameterSymbol(psiTypeParameter, analysisSession) {
// `psi.typeParameters` should align with the list of regular `FirTypeParameter`s, making the use of `index` valid.
val firTypeParameter = firSymbol.fir.typeParameters.filterIsInstance<FirTypeParameter>().getOrNull(index)
require(firTypeParameter != null) {
"The FIR symbol's ${FirTypeParameter::class.simpleName}s should have an entry at $index."
}
firTypeParameter.symbol
}
}
}
val hasTypeParameters: Boolean
get() = withValidityAssertion { psi.typeParameters.isNotEmpty() }
val annotationSimpleNames: List<String?>
get() = withValidityAssertion { psi.annotations.map { it.nameReferenceElement?.referenceName } }
@@ -122,11 +128,6 @@ internal class KtFirPsiJavaClassSymbol(
firClassSymbol
}
override val typeParameters: List<KtTypeParameterSymbol> by cached {
if (hasTypeParameters) firSymbol.createRegularKtTypeParameters(builder)
else emptyList()
}
override val annotationsList: KtAnnotationsList by cached {
if (hasAnnotations) KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token)
else KtEmptyAnnotationsList(token)
@@ -0,0 +1,44 @@
/*
* 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 com.intellij.psi.PsiTypeParameter
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.analysis.api.fir.utils.cached
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.types.Variance
/**
* [KtFirPsiJavaTypeParameterSymbol] is a PSI-based type parameter symbol with a lazy [firSymbol]. Some properties such as [name] are
* computed based on the PSI. This is used by [KtFirPsiJavaClassSymbol] to avoid building its own FIR symbol when a list of type parameters
* is requested.
*/
internal class KtFirPsiJavaTypeParameterSymbol(
override val psi: PsiTypeParameter,
override val analysisSession: KtFirAnalysisSession,
private val computeFirSymbol: () -> FirTypeParameterSymbol,
) : KtFirTypeParameterSymbolBase(), KtFirPsiSymbol<PsiTypeParameter, FirTypeParameterSymbol> {
override val name: Name = withValidityAssertion {
psi.name?.let { Name.identifier(it) } ?: SpecialNames.NO_NAME_PROVIDED
}
override val origin: KtSymbolOrigin
get() = withValidityAssertion { KtSymbolOrigin.JAVA }
override val variance: Variance
get() = withValidityAssertion { Variance.INVARIANT }
override val isReified: Boolean
get() = withValidityAssertion { false }
override val firSymbol: FirTypeParameterSymbol by cached {
computeFirSymbol()
}
}
@@ -6,65 +6,24 @@
package org.jetbrains.kotlin.analysis.api.fir.symbols
import com.intellij.psi.PsiElement
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.fir.findPsi
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.KtFirTypeParameterSymbolPointer
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.requireOwnerPointer
import org.jetbrains.kotlin.analysis.api.fir.utils.cached
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.symbols.KtTypeParameterSymbol
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtPsiBasedSymbolPointer
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.fir.analysis.checkers.typeParameterSymbols
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.isNullableAny
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance
internal class KtFirTypeParameterSymbol(
override val firSymbol: FirTypeParameterSymbol,
override val analysisSession: KtFirAnalysisSession,
) : KtTypeParameterSymbol(), KtFirSymbol<FirTypeParameterSymbol> {
) : KtFirTypeParameterSymbolBase() {
override val token: KtLifetimeToken get() = builder.token
override val psi: PsiElement? by cached { firSymbol.findPsi() }
override val annotationsList: KtAnnotationsList
get() = withValidityAssertion {
KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token)
}
override val name: Name get() = withValidityAssertion { firSymbol.name }
override val upperBounds: List<KtType> by cached {
firSymbol.resolvedBounds.mapNotNull { type ->
if (type.isNullableAny) return@mapNotNull null
builder.typeBuilder.buildKtType(type)
}
}
override val variance: Variance get() = withValidityAssertion { firSymbol.variance }
override val isReified: Boolean get() = withValidityAssertion { firSymbol.isReified }
context(KtAnalysisSession)
override fun createPointer(): KtSymbolPointer<KtTypeParameterSymbol> = withValidityAssertion {
KtPsiBasedSymbolPointer.createForSymbolFromSource<KtTypeParameterSymbol>(this)?.let { return it }
val containingDeclarationSymbol = firSymbol.containingDeclarationSymbol
val typeParameters = containingDeclarationSymbol.typeParameterSymbols
requireNotNull(typeParameters) { "Containing declaration symbol: ${containingDeclarationSymbol::class.simpleName}" }
KtFirTypeParameterSymbolPointer(
ownerPointer = requireOwnerPointer(),
name = name,
index = typeParameters.indexOf(firSymbol),
)
}
override fun equals(other: Any?): Boolean = symbolEquals(other)
override fun hashCode(): Int = symbolHashCode()
}
@@ -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.fir.symbols
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationsList
import org.jetbrains.kotlin.analysis.api.fir.annotations.KtFirAnnotationListForDeclaration
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.KtFirTypeParameterSymbolPointer
import org.jetbrains.kotlin.analysis.api.fir.symbols.pointers.requireOwnerPointer
import org.jetbrains.kotlin.analysis.api.fir.utils.cached
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.symbols.KtTypeParameterSymbol
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtPsiBasedSymbolPointer
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.isNullableAny
import org.jetbrains.kotlin.fir.analysis.checkers.typeParameterSymbols
/**
* [KtFirTypeParameterSymbolBase] provides shared implementations for [KtFirTypeParameterSymbol] and [KtFirPsiJavaTypeParameterSymbol].
*/
internal sealed class KtFirTypeParameterSymbolBase : KtTypeParameterSymbol(), KtFirSymbol<FirTypeParameterSymbol> {
override val annotationsList: KtAnnotationsList
get() = withValidityAssertion {
KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token)
}
override val upperBounds: List<KtType> by cached {
firSymbol.resolvedBounds.mapNotNull { type ->
if (type.isNullableAny) return@mapNotNull null
builder.typeBuilder.buildKtType(type)
}
}
context(KtAnalysisSession)
override fun createPointer(): KtSymbolPointer<KtTypeParameterSymbol> = withValidityAssertion {
KtPsiBasedSymbolPointer.createForSymbolFromSource<KtTypeParameterSymbol>(this)?.let { return it }
val containingDeclarationSymbol = firSymbol.containingDeclarationSymbol
val typeParameters = containingDeclarationSymbol.typeParameterSymbols
requireNotNull(typeParameters) { "Containing declaration symbol: ${containingDeclarationSymbol::class.simpleName}" }
KtFirTypeParameterSymbolPointer(
ownerPointer = requireOwnerPointer(),
name = name,
index = typeParameters.indexOf(firSymbol),
)
}
override fun equals(other: Any?): Boolean = symbolEquals(other)
override fun hashCode(): Int = symbolHashCode()
}
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.analysis.api.symbols.markers
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtTypeParameterSymbol
import org.jetbrains.kotlin.name.Name
@@ -20,11 +19,4 @@ public interface KtNamedSymbol : KtPossiblyNamedSymbol {
public interface KtSymbolWithTypeParameters : KtSymbol {
public val typeParameters: List<KtTypeParameterSymbol>
/**
* The names of [typeParameters]. Using this property should be preferred if only the names of type parameters are needed, because some
* symbol implementations may avoid building the full list of type parameters.
*/
public val typeParameterNames: List<Name>
get() = withValidityAssertion { typeParameters.map { it.name } }
}