[LL] [cls] remove search for sources for decompiled FIR
stub based deserializer provides sources during initialization, no need to search afterward
This commit is contained in:
-229
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* 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.analysis.api.fir
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.ProjectScope
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.KtDeclarationAndFirDeclarationEqualityChecker
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtBuiltinsModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtLibrarySourceModule
|
||||
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
|
||||
import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.containingClassLookupTag
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isData
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
||||
import org.jetbrains.kotlin.fir.resolve.getContainingClass
|
||||
import org.jetbrains.kotlin.fir.delegatedWrapperData
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.unwrapFakeOverrides
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.filterIsInstanceWithChecker
|
||||
|
||||
//todo introduce LibraryModificationTracker based cache?
|
||||
internal object FirDeserializedDeclarationSourceProvider {
|
||||
fun findPsi(fir: FirElement, project: Project): PsiElement? {
|
||||
return when (fir) {
|
||||
is FirSimpleFunction -> provideSourceForFunction(fir.delegatedWrapperData?.wrapped ?: fir, project)
|
||||
is FirProperty -> provideSourceForProperty(fir, project)
|
||||
is FirClass -> provideSourceForClass(fir, project)
|
||||
is FirTypeAlias -> provideSourceForTypeAlias(fir, project)
|
||||
is FirConstructor -> provideSourceForConstructor(fir, project)
|
||||
is FirEnumEntry -> provideSourceForEnumEntry(fir, project)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun findClassPsiForGeneratedMembers(fir: FirElement, project: Project): PsiElement? {
|
||||
if (fir !is FirCallableDeclaration) {
|
||||
return null
|
||||
}
|
||||
val containingClass = fir.getContainingClass(fir.moduleData.session) ?: return null
|
||||
if (isGeneratedMemberNotWrittenToMetadata(fir.symbol, containingClass)) {
|
||||
return provideSourceForClass(containingClass, project)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun isGeneratedMemberNotWrittenToMetadata(symbol: FirCallableSymbol<*>, containingClass: FirRegularClass): Boolean {
|
||||
return when {
|
||||
containingClass.isEnumClass -> symbol.name in setOf(
|
||||
StandardNames.ENUM_VALUES,
|
||||
StandardNames.ENUM_VALUE_OF,
|
||||
StandardNames.ENUM_ENTRIES,
|
||||
)
|
||||
containingClass.isData -> symbol.name in setOf(
|
||||
OperatorNameConventions.EQUALS,
|
||||
OperatorNameConventions.TO_STRING,
|
||||
StandardNames.HASHCODE_NAME,
|
||||
StandardNames.DATA_CLASS_COPY,
|
||||
)
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun provideSourceForFunction(function: FirSimpleFunction, project: Project): PsiElement? {
|
||||
return provideSourceForCallable(
|
||||
function,
|
||||
project,
|
||||
topLevelSearcher = { declarationProvider -> declarationProvider.getTopLevelFunctions(function.symbol.callableId) },
|
||||
predicate = { it is KtFunction && it.name == function.name.asString() }
|
||||
)
|
||||
}
|
||||
|
||||
private fun provideSourceForProperty(property: FirProperty, project: Project): PsiElement? {
|
||||
return provideSourceForCallable(
|
||||
property,
|
||||
project,
|
||||
topLevelSearcher = { declarationProvider -> declarationProvider.getTopLevelProperties(property.symbol.callableId) },
|
||||
predicate = { it is KtProperty && it.name == property.name.asString() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun <T : KtCallableDeclaration> provideSourceForCallable(
|
||||
callable: FirCallableDeclaration,
|
||||
project: Project,
|
||||
topLevelSearcher: (KotlinDeclarationProvider) -> Collection<T>,
|
||||
predicate: (KtCallableDeclaration) -> Boolean
|
||||
): PsiElement? {
|
||||
fun chooseCandidate(candidates: Collection<KtCallableDeclaration>): PsiElement? {
|
||||
return callable.unwrapFakeOverrides().chooseCorrespondingPsi(candidates)
|
||||
}
|
||||
|
||||
if (callable.isTopLevel) {
|
||||
return getTargetScopes(callable, project)
|
||||
.asSequence()
|
||||
.map { scope -> topLevelSearcher(project.createDeclarationProvider(scope)).filter(KtElement::isCompiled) }
|
||||
.filter { it.isNotEmpty() }
|
||||
.firstNotNullOfOrNull { chooseCandidate(it) }
|
||||
}
|
||||
|
||||
val containingKtClass = getContainingKtClassOrObject(callable, getTargetScopes(callable, project), project)
|
||||
val containingKtFile = containingKtClass?.containingKtFile
|
||||
|
||||
if (containingKtFile == null || !containingKtFile.isCompiled) {
|
||||
return null
|
||||
}
|
||||
|
||||
val candidates = containingKtClass.declarations.filterIsInstanceWithChecker<KtCallableDeclaration>(predicate)
|
||||
return chooseCandidate(candidates)
|
||||
}
|
||||
|
||||
private fun provideSourceForClass(klass: FirClass, project: Project): PsiElement? {
|
||||
return getTargetScopes(klass, project)
|
||||
.asSequence()
|
||||
.mapNotNull { scope -> classByClassId(klass.symbol.classId, scope, project) }
|
||||
.filter { it.isCompiled() }
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
private fun provideSourceForTypeAlias(typeAlias: FirTypeAlias, project: Project): PsiElement? {
|
||||
return getTargetScopes(typeAlias, project)
|
||||
.asSequence()
|
||||
.flatMap { scope -> project.createDeclarationProvider(scope).getAllTypeAliasesByClassId(typeAlias.symbol.classId) }
|
||||
.firstOrNull { it.isCompiled() }
|
||||
}
|
||||
|
||||
private fun provideSourceForConstructor(constructor: FirConstructor, project: Project): PsiElement? {
|
||||
val scopes = getTargetScopes(constructor, project)
|
||||
val containingKtClass = getContainingKtClassOrObject(constructor, scopes, project) ?: return null
|
||||
|
||||
return if (constructor.isPrimary) {
|
||||
containingKtClass.primaryConstructor
|
||||
} else {
|
||||
constructor.unwrapFakeOverrides()
|
||||
.chooseCorrespondingPsi(containingKtClass.secondaryConstructors)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirCallableDeclaration.chooseCorrespondingPsi(candidates: Collection<KtCallableDeclaration>): KtCallableDeclaration? {
|
||||
if (candidates.isEmpty()) return null
|
||||
for (candidate in candidates) {
|
||||
assert(candidate.isCompiled()) {
|
||||
"Candidate should be decompiled from metadata because it should have fqName types as we don't use resolve here"
|
||||
}
|
||||
if (KtDeclarationAndFirDeclarationEqualityChecker.representsTheSameDeclaration(candidate, this)) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun provideSourceForEnumEntry(enumEntry: FirEnumEntry, project: Project): PsiElement? {
|
||||
val scopes = getTargetScopes(enumEntry, project)
|
||||
val containingKtClass = getContainingKtClassOrObject(enumEntry, scopes, project) ?: return null
|
||||
|
||||
if (containingKtClass.isCompiled()) {
|
||||
return containingKtClass.body?.enumEntries?.firstOrNull { it.name == enumEntry.name.asString() }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getTargetScopes(declaration: FirDeclaration, project: Project): List<GlobalSearchScope> {
|
||||
val originalDeclaration = when (declaration) {
|
||||
is FirCallableDeclaration -> declaration.unwrapFakeOverrides()
|
||||
else -> declaration
|
||||
}
|
||||
|
||||
val moduleData = originalDeclaration.llFirModuleData
|
||||
val module = moduleData.ktModule
|
||||
|
||||
return buildList {
|
||||
add(module.contentScope)
|
||||
|
||||
when (module) {
|
||||
is KtBuiltinsModule -> {
|
||||
add(ProjectScope.getLibrariesScope(project))
|
||||
add(ProjectScope.getContentScope(project))
|
||||
}
|
||||
is KtLibrarySourceModule, is KtLibraryModule -> {
|
||||
// Search in other libraries (that might be dependencies of this library)
|
||||
add(ProjectScope.getLibrariesScope(project))
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getContainingKtClassOrObject(
|
||||
firCallable: FirCallableDeclaration,
|
||||
scopes: List<GlobalSearchScope>,
|
||||
project: Project
|
||||
): KtClassOrObject? {
|
||||
val classId = firCallable.unwrapFakeOverrides().containingClassLookupTag()?.classId ?: return null
|
||||
return scopes.firstNotNullOfOrNull { scope -> classByClassId(classId, scope, project) }
|
||||
}
|
||||
|
||||
private fun classByClassId(classId: ClassId, scope: GlobalSearchScope, project: Project): KtClassOrObject? {
|
||||
val correctedClassId = classIdMapping[classId] ?: classId
|
||||
return project.createDeclarationProvider(scope)
|
||||
.getAllClassesByClassId(correctedClassId)
|
||||
.firstOrNull(KtElement::isCompiled)
|
||||
}
|
||||
|
||||
private val FirCallableDeclaration.isTopLevel
|
||||
get() = symbol.callableId.className == null
|
||||
|
||||
private val classIdMapping = (0..23).associate { i ->
|
||||
StandardClassIds.FunctionN(i) to ClassId(FqName("kotlin.jvm.functions"), Name.identifier("Function$i"))
|
||||
}
|
||||
}
|
||||
@@ -5,20 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.analysis.api.fir
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.KtFakeSourceElement
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.KtRealPsiSourceElement
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
internal fun KtElement.isCompiled(): Boolean = containingKtFile.isCompiled
|
||||
|
||||
private val allowedFakeElementKinds = setOf(
|
||||
KtFakeSourceElementKind.FromUseSiteTarget,
|
||||
@@ -36,15 +30,11 @@ internal fun FirElement.getAllowedPsi() = when (val source = source) {
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun FirElement.findPsi(project: Project): PsiElement? =
|
||||
fun FirElement.findPsi(): PsiElement? =
|
||||
getAllowedPsi()
|
||||
?: FirDeserializedDeclarationSourceProvider.findPsi(this, project)
|
||||
|
||||
fun FirBasedSymbol<*>.findPsi(): PsiElement? =
|
||||
fir.findPsi(fir.moduleData.session)
|
||||
|
||||
fun FirElement.findPsi(session: FirSession): PsiElement? =
|
||||
findPsi((session as LLFirSession).project)
|
||||
fir.findPsi()
|
||||
|
||||
/**
|
||||
* Finds [PsiElement] which will be used as go-to referenced element for [KtPsiReference]
|
||||
@@ -52,8 +42,5 @@ fun FirElement.findPsi(session: FirSession): PsiElement? =
|
||||
* Otherwise, behaves the same way as [findPsi] returns exact PSI declaration corresponding to passed [FirDeclaration]
|
||||
*/
|
||||
fun FirDeclaration.findReferencePsi(): PsiElement? {
|
||||
psi?.let { return it }
|
||||
val project = (moduleData.session as LLFirSession).project
|
||||
return FirDeserializedDeclarationSourceProvider.findPsi(this, project)
|
||||
?: FirDeserializedDeclarationSourceProvider.findClassPsiForGeneratedMembers(this, project)
|
||||
return psi
|
||||
}
|
||||
|
||||
-281
@@ -1,281 +0,0 @@
|
||||
/*
|
||||
* 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.api
|
||||
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.createEmptySession
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.errorWithFirSpecificEntries
|
||||
import org.jetbrains.kotlin.analysis.utils.printer.parentsOfType
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.builder.BodyBuildingMode
|
||||
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.realPsi
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtConstructor
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
// TODO replace with structural type comparison?
|
||||
object KtDeclarationAndFirDeclarationEqualityChecker {
|
||||
fun representsTheSameDeclaration(psi: KtCallableDeclaration, fir: FirCallableDeclaration): Boolean {
|
||||
if (fir.realPsi == psi) return true
|
||||
if (!modifiersMatch(psi, fir)) return false
|
||||
if (!receiverTypeMatch(psi, fir)) return false
|
||||
if (!returnTypesMatch(psi, fir)) return false
|
||||
if (!typeParametersMatch(psi, fir)) return false
|
||||
if (fir is FirFunction && !valueParametersMatch(psi, fir)) return false
|
||||
return true
|
||||
}
|
||||
|
||||
private fun modifiersMatch(psi: KtCallableDeclaration, fir: FirCallableDeclaration): Boolean {
|
||||
// According to asymmetric logic in 'RawFirBuilder'
|
||||
if (psi.parentsOfType<KtDeclaration>().any { it.hasExpectModifier() } != fir.symbol.rawStatus.isExpect) return false
|
||||
if (psi.hasActualModifier() != fir.symbol.rawStatus.isActual) return false
|
||||
return true
|
||||
}
|
||||
|
||||
private fun receiverTypeMatch(psi: KtCallableDeclaration, fir: FirCallableDeclaration): Boolean {
|
||||
if ((fir.receiverParameter != null) != (psi.receiverTypeReference != null)) return false
|
||||
if (fir.receiverParameter != null && !isTheSameTypes(
|
||||
psi.receiverTypeReference!!,
|
||||
fir.receiverParameter!!.typeRef,
|
||||
isVararg = false,
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun returnTypesMatch(psi: KtCallableDeclaration, fir: FirCallableDeclaration): Boolean {
|
||||
if (psi is KtConstructor<*>) return true
|
||||
return isTheSameTypes(psi.typeReference!!, fir.returnTypeRef, isVararg = false)
|
||||
}
|
||||
|
||||
private fun typeParametersMatch(psiFunction: KtCallableDeclaration, firFunction: FirCallableDeclaration): Boolean {
|
||||
if (firFunction.typeParameters.size != psiFunction.typeParameters.size) return false
|
||||
val boundsByName = psiFunction.typeConstraints.groupBy { it.subjectTypeParameterName?.getReferencedName() }
|
||||
firFunction.typeParameters.zip(psiFunction.typeParameters) { expectedTypeParameter, candidateTypeParameter ->
|
||||
if (expectedTypeParameter.symbol.name.toString() != candidateTypeParameter.name) return false
|
||||
val candidateBounds = mutableListOf<KtTypeReference>()
|
||||
candidateBounds.addIfNotNull(candidateTypeParameter.extendsBound)
|
||||
boundsByName[candidateTypeParameter.name]?.forEach {
|
||||
candidateBounds.addIfNotNull(it.boundTypeReference)
|
||||
}
|
||||
val expectedBounds = expectedTypeParameter.symbol.resolvedBounds.filter { it !is FirImplicitNullableAnyTypeRef }
|
||||
if (candidateBounds.size != expectedBounds.size) return false
|
||||
expectedBounds.zip(candidateBounds) { expectedBound, candidateBound ->
|
||||
if (!isTheSameTypes(
|
||||
candidateBound,
|
||||
expectedBound,
|
||||
isVararg = false
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun valueParametersMatch(psiFunction: KtCallableDeclaration, firFunction: FirFunction): Boolean {
|
||||
if (firFunction.valueParameters.size != psiFunction.valueParameters.size) return false
|
||||
firFunction.valueParameters.zip(psiFunction.valueParameters) { expectedParameter, candidateParameter ->
|
||||
if (expectedParameter.name.toString() != candidateParameter.name) return false
|
||||
if (expectedParameter.isVararg != candidateParameter.isVarArg) return false
|
||||
val candidateParameterType = candidateParameter.typeReference ?: return false
|
||||
if (!isTheSameTypes(
|
||||
candidateParameterType,
|
||||
expectedParameter.returnTypeRef,
|
||||
isVararg = expectedParameter.isVararg
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun FirTypeRef.renderTypeAsKotlinType(isVararg: Boolean = false): String {
|
||||
val rendered = when (this) {
|
||||
is FirResolvedTypeRef -> type.renderTypeAsKotlinType()
|
||||
is FirUserTypeRef -> {
|
||||
val renderedQualifier = qualifier.joinToString(separator = ".") { part ->
|
||||
buildString {
|
||||
append(part.name)
|
||||
if (part.typeArgumentList.typeArguments.isNotEmpty()) {
|
||||
part.typeArgumentList.typeArguments.joinTo(this, prefix = "<", postfix = ">") { it.renderTypeAsKotlinType() }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isMarkedNullable) "$renderedQualifier?" else renderedQualifier
|
||||
}
|
||||
is FirFunctionTypeRef -> {
|
||||
val classId = if (isSuspend) {
|
||||
StandardNames.getSuspendFunctionClassId(parametersCount)
|
||||
} else {
|
||||
StandardNames.getFunctionClassId(parametersCount)
|
||||
}
|
||||
buildString {
|
||||
append(classId.asSingleFqName().toString())
|
||||
val parameters = buildList {
|
||||
receiverTypeRef?.let(::add)
|
||||
parameters.mapTo(this) { it.returnTypeRef }
|
||||
returnTypeRef.let(::add)
|
||||
}
|
||||
if (parameters.isNotEmpty()) {
|
||||
append(parameters.joinToString(prefix = "<", postfix = ">") { it.renderTypeAsKotlinType() })
|
||||
}
|
||||
if (isMarkedNullable) {
|
||||
append("?")
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> errorWithFirSpecificEntries("Invalid type reference", fir = this)
|
||||
}
|
||||
return if (isVararg) {
|
||||
rendered.asArrayType()
|
||||
} else {
|
||||
rendered
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.asArrayType(): String {
|
||||
classIdToName[this]?.let { return it }
|
||||
return "kotlin.Array<out $this>"
|
||||
}
|
||||
|
||||
private val classIdToName: Map<String, String> = buildList<Pair<String, String>> {
|
||||
StandardClassIds.primitiveArrayTypeByElementType.mapTo(this) { (classId, arrayClassId) ->
|
||||
classId.asString().replace('/', '.') to arrayClassId.asString().replace('/', '.')
|
||||
}
|
||||
StandardClassIds.unsignedArrayTypeByElementType.mapTo(this) { (classId, arrayClassId) ->
|
||||
classId.asString().replace('/', '.') to arrayClassId.asString().replace('/', '.')
|
||||
}
|
||||
}.toMap()
|
||||
|
||||
private fun FirTypeProjection.renderTypeAsKotlinType() = when (this) {
|
||||
is FirStarProjection -> "*"
|
||||
is FirTypeProjectionWithVariance -> buildString {
|
||||
append(variance.label)
|
||||
if (variance != Variance.INVARIANT) {
|
||||
append(" ")
|
||||
}
|
||||
append(typeRef.renderTypeAsKotlinType())
|
||||
}
|
||||
else -> errorWithFirSpecificEntries("Invalid type reference", fir = this)
|
||||
}
|
||||
|
||||
private fun isTheSameTypes(
|
||||
psiTypeReference: KtTypeReference,
|
||||
coneTypeReference: FirTypeRef,
|
||||
isVararg: Boolean
|
||||
): Boolean =
|
||||
psiTypeReference.toKotlinTypReference().renderTypeAsKotlinType(isVararg) == coneTypeReference.renderTypeAsKotlinType()
|
||||
|
||||
@Suppress("DEPRECATION_ERROR")
|
||||
private fun KtTypeReference.toKotlinTypReference(): FirTypeRef {
|
||||
// Maybe resolve all types here to not to work with FirTypeRef directly
|
||||
return RawFirBuilder(
|
||||
createEmptySession(),
|
||||
DummyScopeProvider,
|
||||
bodyBuildingMode = BodyBuildingMode.NORMAL
|
||||
).buildTypeReference(this)
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.renderTypeAsKotlinType(): String {
|
||||
val rendered = when (this) {
|
||||
is ConeClassLikeType -> buildString {
|
||||
append(lookupTag.classId.asString())
|
||||
if (typeArguments.isNotEmpty()) {
|
||||
append(typeArguments.joinToString(prefix = "<", postfix = ">", separator = ", ") { it.renderTypeAsKotlinType() })
|
||||
}
|
||||
}
|
||||
is ConeTypeVariableType -> lookupTag.name.asString()
|
||||
is ConeLookupTagBasedType -> lookupTag.name.asString()
|
||||
|
||||
// NOTE: Flexible types can occur not only as implicit return types,
|
||||
// but also as implicit parameter types, for example in setters with implicit types
|
||||
is ConeFlexibleType -> {
|
||||
// since Kotlin decompiler always "renders" flexible types as their lower bound, we can do the same here
|
||||
lowerBound.renderTypeAsKotlinType()
|
||||
}
|
||||
|
||||
else -> errorWithFirSpecificEntries("Type should not be present in Kotlin declaration", coneType = this)
|
||||
}.replace('/', '.')
|
||||
|
||||
// UNKNOWN nullability occurs only on flexible types
|
||||
val nullabilitySuffix = nullability.takeUnless { it == ConeNullability.UNKNOWN }?.suffix.orEmpty()
|
||||
|
||||
return rendered + nullabilitySuffix
|
||||
}
|
||||
|
||||
private fun ConeTypeProjection.renderTypeAsKotlinType(): String = when (this) {
|
||||
ConeStarProjection -> "*"
|
||||
is ConeKotlinTypeProjectionIn -> "in ${type.renderTypeAsKotlinType()}"
|
||||
is ConeKotlinTypeProjectionOut -> "out ${type.renderTypeAsKotlinType()}"
|
||||
is ConeKotlinTypeConflictingProjection -> "CONFLICTING-PROJECTION ${type.renderTypeAsKotlinType()}"
|
||||
is ConeKotlinType -> renderTypeAsKotlinType()
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun renderPsi(ktFunction: KtFunction): String = buildString {
|
||||
appendLine("receiver: ${ktFunction.receiverTypeReference?.toKotlinTypReference()?.renderTypeAsKotlinType()}")
|
||||
ktFunction.valueParameters.forEach { parameter ->
|
||||
appendLine("${parameter.name}: ${parameter.typeReference?.toKotlinTypReference()?.renderTypeAsKotlinType()}")
|
||||
}
|
||||
appendLine("return: ${ktFunction.typeReference?.toKotlinTypReference()?.renderTypeAsKotlinType()}")
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun renderFir(firFunction: FirFunction): String = buildString {
|
||||
appendLine("receiver: ${firFunction.receiverParameter?.typeRef?.renderTypeAsKotlinType()}")
|
||||
firFunction.valueParameters.forEach { parameter ->
|
||||
appendLine("${parameter.name}: ${parameter.returnTypeRef.renderTypeAsKotlinType()}")
|
||||
}
|
||||
appendLine("return: ${firFunction.returnTypeRef.renderTypeAsKotlinType()}")
|
||||
}
|
||||
|
||||
private object DummyScopeProvider : FirScopeProvider() {
|
||||
override fun getUseSiteMemberScope(
|
||||
klass: FirClass,
|
||||
useSiteSession: FirSession,
|
||||
scopeSession: ScopeSession,
|
||||
memberRequiredPhase: FirResolvePhase?,
|
||||
): FirTypeScope = shouldNotBeCalled()
|
||||
|
||||
override fun getStaticMemberScopeForCallables(
|
||||
klass: FirClass,
|
||||
useSiteSession: FirSession,
|
||||
scopeSession: ScopeSession
|
||||
): FirContainingNamesAwareScope? = shouldNotBeCalled()
|
||||
|
||||
override fun getNestedClassifierScope(
|
||||
klass: FirClass,
|
||||
useSiteSession: FirSession,
|
||||
scopeSession: ScopeSession
|
||||
): FirContainingNamesAwareScope? = shouldNotBeCalled()
|
||||
|
||||
private fun shouldNotBeCalled(): Nothing = error("Should not be called in RawFirBuilder while converting KtTypeReference")
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.analysis.low.level.api.fir.providers
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirModuleData
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirBuiltinSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class LLFirBuiltinSymbolProvider(
|
||||
session: FirSession,
|
||||
moduleData: FirModuleData,
|
||||
kotlinScopeProvider: FirKotlinScopeProvider
|
||||
) : FirBuiltinSymbolProvider(session, moduleData, kotlinScopeProvider) {
|
||||
private val functionsCache = session.firCachesFactory.createCache { callableId: CallableId ->
|
||||
buildList {
|
||||
getTopLevelFunctionSymbolsToByPackageFragments(this, callableId.packageName, callableId.callableName)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(FirSymbolProviderInternals::class)
|
||||
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
|
||||
// valid until FirBuiltinSymbolProvider provide property symbols
|
||||
destination += functionsCache.getValue(CallableId(packageFqName, name))
|
||||
}
|
||||
|
||||
@OptIn(FirSymbolProviderInternals::class)
|
||||
override fun getTopLevelFunctionSymbolsTo(destination: MutableList<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name) {
|
||||
destination += functionsCache.getValue(CallableId(packageFqName, name))
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.analysis.low.level.api.fir.util
|
||||
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.KtDeclarationAndFirDeclarationEqualityChecker
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirBuiltinsAndCloneableSession
|
||||
@@ -13,6 +12,7 @@ import org.jetbrains.kotlin.analysis.project.structure.getKtModule
|
||||
import org.jetbrains.kotlin.analysis.utils.errors.ExceptionAttachmentBuilder
|
||||
import org.jetbrains.kotlin.analysis.utils.errors.withClassEntry
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.realPsi
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.*
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
@@ -110,7 +110,7 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid
|
||||
val candidates = symbolProvider.findFunctionCandidates(declaration)
|
||||
val functionCandidate =
|
||||
candidates
|
||||
.firstOrNull { KtDeclarationAndFirDeclarationEqualityChecker.representsTheSameDeclaration(declaration, it.fir) }
|
||||
.firstOrNull { it.fir.realPsi === declaration }
|
||||
?: errorWithFirSpecificEntries("We should be able to find a symbol for function", psi = declaration) {
|
||||
withCandidates(candidates)
|
||||
}
|
||||
@@ -124,7 +124,7 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid
|
||||
|
||||
val candidates = symbolProvider.findPropertyCandidates(declaration)
|
||||
val propertyCandidate =
|
||||
candidates.firstOrNull { KtDeclarationAndFirDeclarationEqualityChecker.representsTheSameDeclaration(declaration, it.fir) }
|
||||
candidates.firstOrNull { it.fir.realPsi === declaration }
|
||||
?: errorWithFirSpecificEntries("We should be able to find a symbol for property", psi = declaration) {
|
||||
withCandidates(candidates)
|
||||
}
|
||||
@@ -181,7 +181,7 @@ private fun representSameConstructor(psiConstructor: KtConstructor<*>, firConstr
|
||||
return false
|
||||
}
|
||||
|
||||
return KtDeclarationAndFirDeclarationEqualityChecker.representsTheSameDeclaration(psiConstructor, firConstructor)
|
||||
return firConstructor.realPsi === psiConstructor
|
||||
}
|
||||
|
||||
private fun ExceptionAttachmentBuilder.withCandidates(candidates: List<FirBasedSymbol<*>>) {
|
||||
|
||||
Reference in New Issue
Block a user