[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
|
package org.jetbrains.kotlin.analysis.api.fir
|
||||||
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.KtFakeSourceElement
|
import org.jetbrains.kotlin.KtFakeSourceElement
|
||||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||||
import org.jetbrains.kotlin.KtRealPsiSourceElement
|
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.FirElement
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.psi
|
import org.jetbrains.kotlin.fir.psi
|
||||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
|
||||||
|
|
||||||
internal fun KtElement.isCompiled(): Boolean = containingKtFile.isCompiled
|
|
||||||
|
|
||||||
private val allowedFakeElementKinds = setOf(
|
private val allowedFakeElementKinds = setOf(
|
||||||
KtFakeSourceElementKind.FromUseSiteTarget,
|
KtFakeSourceElementKind.FromUseSiteTarget,
|
||||||
@@ -36,15 +30,11 @@ internal fun FirElement.getAllowedPsi() = when (val source = source) {
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FirElement.findPsi(project: Project): PsiElement? =
|
fun FirElement.findPsi(): PsiElement? =
|
||||||
getAllowedPsi()
|
getAllowedPsi()
|
||||||
?: FirDeserializedDeclarationSourceProvider.findPsi(this, project)
|
|
||||||
|
|
||||||
fun FirBasedSymbol<*>.findPsi(): PsiElement? =
|
fun FirBasedSymbol<*>.findPsi(): PsiElement? =
|
||||||
fir.findPsi(fir.moduleData.session)
|
fir.findPsi()
|
||||||
|
|
||||||
fun FirElement.findPsi(session: FirSession): PsiElement? =
|
|
||||||
findPsi((session as LLFirSession).project)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds [PsiElement] which will be used as go-to referenced element for [KtPsiReference]
|
* 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]
|
* Otherwise, behaves the same way as [findPsi] returns exact PSI declaration corresponding to passed [FirDeclaration]
|
||||||
*/
|
*/
|
||||||
fun FirDeclaration.findReferencePsi(): PsiElement? {
|
fun FirDeclaration.findReferencePsi(): PsiElement? {
|
||||||
psi?.let { return it }
|
return psi
|
||||||
val project = (moduleData.session as LLFirSession).project
|
|
||||||
return FirDeserializedDeclarationSourceProvider.findPsi(this, project)
|
|
||||||
?: FirDeserializedDeclarationSourceProvider.findClassPsiForGeneratedMembers(this, project)
|
|
||||||
}
|
}
|
||||||
|
|||||||
-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
|
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.project.structure.llFirModuleData
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirBuiltinsAndCloneableSession
|
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.ExceptionAttachmentBuilder
|
||||||
import org.jetbrains.kotlin.analysis.utils.errors.withClassEntry
|
import org.jetbrains.kotlin.analysis.utils.errors.withClassEntry
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.fir.realPsi
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.*
|
import org.jetbrains.kotlin.fir.resolve.providers.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
@@ -110,7 +110,7 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid
|
|||||||
val candidates = symbolProvider.findFunctionCandidates(declaration)
|
val candidates = symbolProvider.findFunctionCandidates(declaration)
|
||||||
val functionCandidate =
|
val functionCandidate =
|
||||||
candidates
|
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) {
|
?: errorWithFirSpecificEntries("We should be able to find a symbol for function", psi = declaration) {
|
||||||
withCandidates(candidates)
|
withCandidates(candidates)
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid
|
|||||||
|
|
||||||
val candidates = symbolProvider.findPropertyCandidates(declaration)
|
val candidates = symbolProvider.findPropertyCandidates(declaration)
|
||||||
val propertyCandidate =
|
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) {
|
?: errorWithFirSpecificEntries("We should be able to find a symbol for property", psi = declaration) {
|
||||||
withCandidates(candidates)
|
withCandidates(candidates)
|
||||||
}
|
}
|
||||||
@@ -181,7 +181,7 @@ private fun representSameConstructor(psiConstructor: KtConstructor<*>, firConstr
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return KtDeclarationAndFirDeclarationEqualityChecker.representsTheSameDeclaration(psiConstructor, firConstructor)
|
return firConstructor.realPsi === psiConstructor
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ExceptionAttachmentBuilder.withCandidates(candidates: List<FirBasedSymbol<*>>) {
|
private fun ExceptionAttachmentBuilder.withCandidates(candidates: List<FirBasedSymbol<*>>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user