Commit e6a34990 authored by Jinseong Jeon's avatar Jinseong Jeon
Browse files

K2 UAST: create fake PSI for inline functions from binary dependency

...same as K1 UAST does w/ UastFakeDescriptorLightMethod

^KTIJ-27686 fixed
parent abaef850
Showing with 120 additions and 5 deletions
+120 -5
......@@ -89,7 +89,7 @@ interface UastResolveEverythingTestBase : UastPluginSelection, UastFileCompariso
Regex("^LightMethodBuilder:.+$") to TAG_METHOD,
Regex("^KtLightMethodForDecompiledDeclaration of .+:.+$") to TAG_METHOD_DECOMPILED,
Regex("^UastFakeDeserializedLightMethod of .+$") to TAG_METHOD_DECOMPILED,
Regex("^UastFakeDeserialized.+LightMethod of .+$") to TAG_METHOD_DECOMPILED,
Regex("^PsiMethod:.+$") to TAG_METHOD_DECOMPILED,
Regex("^KtLightField:.+$") to TAG_VARIABLE,
......
......@@ -4,6 +4,7 @@ package org.jetbrains.uast.kotlin.internal
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.impl.file.impl.JavaFileManager
import com.intellij.psi.util.PsiTypesUtil
import org.jetbrains.kotlin.analysis.api.*
import org.jetbrains.kotlin.analysis.api.calls.KtCallableMemberCall
......@@ -25,7 +26,8 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.type.MapPsiToAsmDesc
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.*
import org.jetbrains.uast.kotlin.psi.UastFakeDeserializedLightMethod
import org.jetbrains.uast.kotlin.psi.UastFakeDeserializedSourceLightMethod
import org.jetbrains.uast.kotlin.psi.UastFakeDeserializedSymbolLightMethod
import org.jetbrains.uast.kotlin.psi.UastFakeSourceLightMethod
import org.jetbrains.uast.kotlin.psi.UastFakeSourceLightPrimaryConstructor
......@@ -81,7 +83,47 @@ internal fun toPsiMethod(
context: KtElement,
): PsiMethod? {
return when (val psi = psiForUast(functionSymbol, context.project)) {
null -> null
null -> {
// `inline` from binary dependency, which we can't find source PSI, so fake it
if (functionSymbol.origin == KtSymbolOrigin.LIBRARY && (functionSymbol as? KtFunctionSymbol)?.isInline == true) {
val containingSymbol = functionSymbol.getContainingSymbol()
if (containingSymbol != null) {
if (containingSymbol is KtClassLikeSymbol) {
// class member
(psiForUast(containingSymbol, context.project) as? PsiClass)?.let { containingClass ->
UastFakeDeserializedSymbolLightMethod(
functionSymbol.createPointer(),
functionSymbol.name.identifier,
containingClass,
context
)
}
} else {
// local
null
}
} else {
// top-level declaration
val callableId = functionSymbol.callableIdIfNonLocal ?: return null
val packageName = callableId.packageName.asString()
val callableName = callableId.callableName.identifier
val javaFileManager = context.project.getServiceIfCreated(JavaFileManager::class.java)
val classesInPackage = javaFileManager?.findPackage(packageName)?.classes
classesInPackage?.find { psiClass ->
// TODO: this may not pick the correct corresponding facade class.
psiClass.name?.endsWith("Kt") == true &&
psiClass.children.filterIsInstance<PsiMember>().none { psiMember -> psiMember.name == callableName }
}?.let { maybeFacade ->
UastFakeDeserializedSymbolLightMethod(
functionSymbol.createPointer(),
functionSymbol.name.identifier,
maybeFacade,
context
)
}
}
} else null
}
is PsiMethod -> psi
is KtClassOrObject -> {
// For synthetic members in enum classes, `psi` points to their containing enum class.
......@@ -135,7 +177,7 @@ private fun toPsiMethodForDeserialized(
else
methods.filter { it.name == psi.name }
return when (candidates.size) {
0 -> if (fake) UastFakeDeserializedLightMethod(psi, this) else null
0 -> if (fake) UastFakeDeserializedSourceLightMethod(psi, this) else null
1 -> candidates.single()
else -> {
candidates.firstOrNull { it.desc == desc(functionSymbol, it, context) } ?: candidates.first()
......
......@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.psi.KtFunction
* that are in source, but not converted/supported by light classes (e.g., due to the presence of @JvmSynthetic).
* To keep the semantics of [UastFakeSourceLightMethod], here we introduce a dummy abstraction as a placeholder.
*/
internal class UastFakeDeserializedLightMethod(
internal class UastFakeDeserializedSourceLightMethod(
private val original: KtFunction,
containingClass: PsiClass,
) : UastFakeSourceLightMethod(original, containingClass) {
......
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.uast.kotlin.psi
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiClass
import com.intellij.psi.impl.light.LightModifierList
import com.intellij.psi.impl.light.LightParameterListBuilder
import org.jetbrains.kotlin.analysis.api.annotations.annotations
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
import org.jetbrains.kotlin.asJava.toLightAnnotation
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.uast.UastLazyPart
import org.jetbrains.uast.getOrBuild
import org.jetbrains.uast.kotlin.internal.analyzeForUast
/**
* A fake light method from binary, which is not materialized for some reason (e.g., `inline`)
*
* Due to its origin, BINARY, we don't have source PSI, but at least we have a pointer to
* Analysis API symbol if it's resolved.
*/
internal class UastFakeDeserializedSymbolLightMethod(
private val original: KtSymbolPointer<KtFunctionSymbol>,
name: String,
containingClass: PsiClass,
private val context: KtElement,
) : UastFakeLightMethodBase(
context.manager,
context.language,
name,
LightParameterListBuilder(context.manager, context.language),
LightModifierList(context.manager),
containingClass
) {
private val _isSuspend = UastLazyPart<Boolean>()
override fun isSuspendFunction(): Boolean =
_isSuspend.getOrBuild {
analyzeForUast(context) {
val functionSymbol = original.restoreSymbol() ?: return@analyzeForUast false
functionSymbol.isSuspend
}
}
private val _isUnit = UastLazyPart<Boolean>()
override fun isUnitFunction(): Boolean =
_isUnit.getOrBuild {
analyzeForUast(context) {
val functionSymbol = original.restoreSymbol() ?: return@analyzeForUast false
functionSymbol.returnType.isUnit
}
}
override fun computeNullability(): KtTypeNullability? {
return analyzeForUast(context) {
val functionSymbol = original.restoreSymbol() ?: return@analyzeForUast null
functionSymbol.returnType.nullability
}
}
override fun computeAnnotations(annotations: SmartList<PsiAnnotation>) {
analyzeForUast(context) {
val functionSymbol = original.restoreSymbol() ?: return
functionSymbol.annotations.forEach { annoApp ->
annoApp.psi?.toLightAnnotation()?.let { annotations.add(it) }
}
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment