diff options
Diffstat (limited to 'compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/utils.kt')
-rw-r--r-- | compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/utils.kt | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/utils.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/utils.kt new file mode 100644 index 00000000..4e19baf1 --- /dev/null +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/utils.kt @@ -0,0 +1,559 @@ +/* + * Copyright 2020 Google LLC + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.devtools.ksp.symbol.impl + +import com.google.devtools.ksp.BinaryClassInfoCache +import com.google.devtools.ksp.ExceptionMessage +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.processing.impl.ResolverImpl +import com.google.devtools.ksp.processing.impl.workaroundForNested +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.symbol.Variance +import com.google.devtools.ksp.symbol.impl.binary.KSClassDeclarationDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSDeclarationDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSFunctionDeclarationDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSPropertyDeclarationDescriptorImpl +import com.google.devtools.ksp.symbol.impl.binary.KSTypeArgumentDescriptorImpl +import com.google.devtools.ksp.symbol.impl.java.* +import com.google.devtools.ksp.symbol.impl.kotlin.* +import com.google.devtools.ksp.symbol.impl.synthetic.KSPropertyGetterSyntheticImpl +import com.google.devtools.ksp.symbol.impl.synthetic.KSPropertySetterSyntheticImpl +import com.google.devtools.ksp.symbol.impl.synthetic.KSValueParameterSyntheticImpl +import com.intellij.psi.* +import com.intellij.psi.impl.light.LightMethod +import com.intellij.psi.impl.source.PsiClassImpl +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMapper +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.load.java.descriptors.JavaClassConstructorDescriptor +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor +import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver +import org.jetbrains.kotlin.load.java.sources.JavaSourceElement +import org.jetbrains.kotlin.load.java.structure.impl.JavaConstructorImpl +import org.jetbrains.kotlin.load.java.structure.impl.JavaMethodImpl +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaField +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaMethodBase +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement +import org.jetbrains.kotlin.load.kotlin.getContainingKotlinJvmBinaryClass +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.getOwnerForEffectiveDispatchReceiverParameter +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor +import org.jetbrains.kotlin.types.* +import java.util.* +import kotlin.Comparator +import kotlin.collections.ArrayDeque + +fun PsiElement.findParentAnnotated(): KSAnnotated? { + var parent = when (this) { + // Unfortunately, LightMethod doesn't implement parent. + is LightMethod -> this.containingClass + else -> this.parent + } + + while (parent != null && parent !is KtDeclaration && parent !is KtFile && parent !is PsiClass && + parent !is PsiMethod && parent !is PsiJavaFile && parent !is KtTypeAlias + ) { + parent = parent.parent + } + + return when (parent) { + is KtClassOrObject -> KSClassDeclarationImpl.getCached(parent) + is KtFile -> KSFileImpl.getCached(parent) + is KtFunction -> KSFunctionDeclarationImpl.getCached(parent) + is PsiClass -> KSClassDeclarationJavaImpl.getCached(parent) + is PsiJavaFile -> KSFileJavaImpl.getCached(parent) + is PsiMethod -> KSFunctionDeclarationJavaImpl.getCached(parent) + is KtProperty -> KSPropertyDeclarationImpl.getCached(parent) + is KtPropertyAccessor -> if (parent.isGetter) { + KSPropertyGetterImpl.getCached(parent) + } else { + KSPropertySetterImpl.getCached(parent) + } + is KtTypeAlias -> KSTypeAliasImpl.getCached(parent) + else -> null + } +} + +fun PsiElement.findParentDeclaration(): KSDeclaration? { + return this.findParentAnnotated() as? KSDeclaration +} + +fun PsiElement.toLocation(): Location { + val file = this.containingFile + val document = ResolverImpl.instance!!.psiDocumentManager.getDocument(file) ?: return NonExistLocation + return FileLocation(file.virtualFile.path, document.getLineNumber(this.textOffset) + 1) +} + +// TODO: handle local functions/classes correctly +fun Sequence<KtElement>.getKSDeclarations(): Sequence<KSDeclaration> = + this.mapNotNull { + when (it) { + is KtFunction -> KSFunctionDeclarationImpl.getCached(it) + is KtProperty -> KSPropertyDeclarationImpl.getCached(it) + is KtClassOrObject -> KSClassDeclarationImpl.getCached(it) + is KtTypeAlias -> KSTypeAliasImpl.getCached(it) + else -> null + } + } + +fun List<PsiElement>.getKSJavaDeclarations() = + this.mapNotNull { + when (it) { + is PsiClass -> KSClassDeclarationJavaImpl.getCached(it) + is PsiMethod -> KSFunctionDeclarationJavaImpl.getCached(it) + is PsiField -> KSPropertyDeclarationJavaImpl.getCached(it) + else -> null + } + } + +fun org.jetbrains.kotlin.types.Variance.toKSVariance(): Variance { + return when (this) { + org.jetbrains.kotlin.types.Variance.IN_VARIANCE -> Variance.CONTRAVARIANT + org.jetbrains.kotlin.types.Variance.OUT_VARIANCE -> Variance.COVARIANT + org.jetbrains.kotlin.types.Variance.INVARIANT -> Variance.INVARIANT + else -> throw IllegalStateException("Unexpected variance value $this, $ExceptionMessage") + } +} + +private fun KSTypeReference.toKotlinType() = (resolve() as? KSTypeImpl)?.kotlinType + +// returns null if error +internal fun KotlinType.replaceTypeArguments(newArguments: List<KSTypeArgument>): KotlinType? { + if (newArguments.isNotEmpty() && this.arguments.size != newArguments.size) + return null + return replace( + newArguments.mapIndexed { index, ksTypeArgument -> + val variance = when (ksTypeArgument.variance) { + Variance.INVARIANT -> org.jetbrains.kotlin.types.Variance.INVARIANT + Variance.COVARIANT -> org.jetbrains.kotlin.types.Variance.OUT_VARIANCE + Variance.CONTRAVARIANT -> org.jetbrains.kotlin.types.Variance.IN_VARIANCE + Variance.STAR -> return@mapIndexed StarProjectionImpl(constructor.parameters[index]) + } + + val type = when (ksTypeArgument) { + is KSTypeArgumentKtImpl, is KSTypeArgumentJavaImpl, is KSTypeArgumentLiteImpl -> ksTypeArgument.type!! + is KSTypeArgumentDescriptorImpl -> return@mapIndexed ksTypeArgument.descriptor + else -> throw IllegalStateException( + "Unexpected psi for type argument: ${ksTypeArgument.javaClass}, $ExceptionMessage" + ) + }.toKotlinType() ?: return null + + TypeProjectionImpl(variance, type) + } + ) +} + +internal fun FunctionDescriptor.toKSDeclaration(): KSDeclaration { + if (this.kind != CallableMemberDescriptor.Kind.DECLARATION) + return KSFunctionDeclarationDescriptorImpl.getCached(this) + val psi = this.findPsi() ?: return KSFunctionDeclarationDescriptorImpl.getCached(this) + // Java default constructor has a kind DECLARATION of while still being synthetic. + if (psi is PsiClassImpl && this is JavaClassConstructorDescriptor) { + return KSFunctionDeclarationDescriptorImpl.getCached(this) + } + return when (psi) { + is KtFunction -> KSFunctionDeclarationImpl.getCached(psi) + is PsiMethod -> KSFunctionDeclarationJavaImpl.getCached(psi) + is KtProperty -> KSPropertyDeclarationImpl.getCached(psi) + else -> throw IllegalStateException("unexpected psi: ${psi.javaClass}") + } +} + +internal fun PropertyDescriptor.toKSPropertyDeclaration(): KSPropertyDeclaration { + if (this.kind != CallableMemberDescriptor.Kind.DECLARATION) + return KSPropertyDeclarationDescriptorImpl.getCached(this) + val psi = this.findPsi() ?: return KSPropertyDeclarationDescriptorImpl.getCached(this) + return when (psi) { + is KtProperty -> KSPropertyDeclarationImpl.getCached(psi) + is KtParameter -> KSPropertyDeclarationParameterImpl.getCached(psi) + is PsiField -> KSPropertyDeclarationJavaImpl.getCached(psi) + is PsiMethod -> { + // happens when a java class implements a kotlin interface that declares properties. + KSPropertyDeclarationDescriptorImpl.getCached(this) + } + else -> throw IllegalStateException("unexpected psi: ${psi.javaClass}") + } +} + +/** + * @see KSFunctionDeclaration.findOverridee / [KSPropertyDeclaration.findOverridee] for docs. + */ +internal inline fun <reified T : CallableMemberDescriptor> T.findClosestOverridee(): T? { + // When there is an intermediate class between the overridden and our function, we might receive + // a FAKE_OVERRIDE function which is not desired as we are trying to find the actual + // declared method. + + // we also want to return the closes function declaration. That is either the closest + // class / interface method OR in case of equal distance (e.g. diamon dinheritance), pick the + // one declared first in the code. + + (getOwnerForEffectiveDispatchReceiverParameter() as? ClassDescriptor)?.defaultType?.let { + ResolverImpl.instance!!.incrementalContext.recordLookupWithSupertypes(it) + } + + val queue = ArrayDeque<T>() + queue.add(this) + + while (queue.isNotEmpty()) { + val current = queue.removeFirst() + ResolverImpl.instance!!.incrementalContext.recordLookupForCallableMemberDescriptor(current.original) + val overriddenDescriptors: Collection<T> = current.original.overriddenDescriptors.filterIsInstance<T>() + overriddenDescriptors.firstOrNull { + it.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE + }?.let { + ResolverImpl.instance!!.incrementalContext.recordLookupForCallableMemberDescriptor(it.original) + return it.original as T? + } + // if all methods are fake, add them to the queue + queue.addAll(overriddenDescriptors) + } + return null +} + +internal fun ModuleClassResolver.resolveContainingClass(psiMethod: PsiMethod): ClassDescriptor? { + return if (psiMethod.isConstructor) { + resolveClass(JavaConstructorImpl(psiMethod).containingClass.apply { workaroundForNested() }) + } else { + resolveClass(JavaMethodImpl(psiMethod).containingClass.apply { workaroundForNested() }) + } +} + +internal fun getInstanceForCurrentRound(node: KSNode): KSNode? { + return when (node.origin) { + Origin.KOTLIN_LIB, Origin.JAVA_LIB -> null + else -> when (node) { + is KSClassDeclarationImpl -> KSClassDeclarationImpl.getCached(node.ktClassOrObject) + is KSFileImpl -> KSFileImpl.getCached(node.file) + is KSFunctionDeclarationImpl -> KSFunctionDeclarationImpl.getCached(node.ktFunction) + is KSPropertyDeclarationImpl -> KSPropertyDeclarationImpl.getCached(node.ktProperty) + is KSPropertyGetterImpl -> KSPropertyGetterImpl.getCached(node.ktPropertyAccessor) + is KSPropertySetterImpl -> KSPropertySetterImpl.getCached(node.ktPropertyAccessor) + is KSTypeAliasImpl -> KSTypeAliasImpl.getCached(node.ktTypeAlias) + is KSTypeArgumentLiteImpl -> KSTypeArgumentLiteImpl.getCached(node.type, node.variance) + is KSTypeArgumentKtImpl -> KSTypeArgumentKtImpl.getCached(node.ktTypeArgument) + is KSTypeParameterImpl -> KSTypeParameterImpl.getCached(node.ktTypeParameter) + is KSTypeReferenceImpl -> KSTypeReferenceImpl.getCached(node.ktTypeReference) + is KSValueParameterImpl -> KSValueParameterImpl.getCached(node.ktParameter) + is KSClassDeclarationJavaEnumEntryImpl -> KSClassDeclarationJavaEnumEntryImpl.getCached(node.psi) + is KSClassDeclarationJavaImpl -> KSClassDeclarationJavaImpl.getCached(node.psi) + is KSFileJavaImpl -> KSFileJavaImpl.getCached(node.psi) + is KSFunctionDeclarationJavaImpl -> KSFunctionDeclarationJavaImpl.getCached(node.psi) + is KSPropertyDeclarationJavaImpl -> KSPropertyDeclarationJavaImpl.getCached(node.psi) + is KSTypeArgumentJavaImpl -> KSTypeArgumentJavaImpl.getCached(node.psi, node.parent) + is KSTypeParameterJavaImpl -> KSTypeParameterJavaImpl.getCached(node.psi) + is KSTypeReferenceJavaImpl -> + KSTypeReferenceJavaImpl.getCached(node.psi, (node.parent as? KSAnnotated)?.getInstanceForCurrentRound()) + is KSValueParameterJavaImpl -> KSValueParameterJavaImpl.getCached(node.psi) + is KSPropertyGetterSyntheticImpl -> KSPropertyGetterSyntheticImpl.getCached(node.ksPropertyDeclaration) + is KSPropertySetterSyntheticImpl -> KSPropertySetterSyntheticImpl.getCached(node.ksPropertyDeclaration) + is KSValueParameterSyntheticImpl -> + KSPropertySetterImpl.getCached(node.owner as KtPropertyAccessor).parameter + is KSAnnotationJavaImpl -> KSAnnotationJavaImpl.getCached(node.psi) + is KSAnnotationImpl -> KSAnnotationImpl.getCached(node.ktAnnotationEntry) + is KSClassifierReferenceJavaImpl -> KSClassifierReferenceJavaImpl.getCached(node.psi, node.parent) + is KSValueArgumentJavaImpl -> + KSValueArgumentJavaImpl.getCached(node.name, node.value, getInstanceForCurrentRound(node.parent!!)) + else -> null + } + } +} + +internal fun KSAnnotated.getInstanceForCurrentRound(): KSAnnotated? = getInstanceForCurrentRound(this) as? KSAnnotated + +/** + * Helper class to read the order of fields/methods in a .class file compiled from Kotlin. + * + * When a compiled Kotlin class is read from descriptors, the order of fields / methods do not match + * the order in the original source file (or the .class file). + * This helper class reads the order from the binary class (using the visitor API) and allows + * [KSClassDeclarationDescriptorImpl] to sort its declarations based on the .class file. + * + * Note that the ordering is relevant only for fields and methods. For any other declaration, the + * order that was returned from the descriptor API is kept. + * + * see: https://github.com/google/ksp/issues/250 + */ +@KspExperimental +internal class DeclarationOrdering( + binaryClass: KotlinJvmBinaryClass +) : KotlinJvmBinaryClass.MemberVisitor { + // Map of fieldName -> Order + private val fieldOrdering = mutableMapOf<String, Int>() + // Map of method name to (jvm desc -> Order) map + // multiple methods might have the same name, hence we need to use signature matching for + // methods. That being said, we only do it when we find multiple methods with the same name + // otherwise, there is no reason to compute the jvm signature. + private val methodOrdering = mutableMapOf<String, MutableMap<String, Int>>() + // This map is built while we are sorting to ensure for the same declaration, we return the same + // order, in case it is not found in fields / methods. + private val declOrdering = IdentityHashMap<KSDeclaration, Int>() + // Helper class to generate ids that can be used for comparison. + private val orderProvider = OrderProvider() + + init { + binaryClass.visitMembers(this, null) + orderProvider.seal() + } + + val comparator = Comparator<KSDeclarationDescriptorImpl> { first, second -> + getOrder(first).compareTo(getOrder(second)) + } + + private fun getOrder(decl: KSDeclarationDescriptorImpl): Int { + return declOrdering.getOrPut(decl) { + when (decl) { + is KSPropertyDeclarationDescriptorImpl -> { + fieldOrdering[decl.simpleName.asString()]?.let { + return@getOrPut it + } + // might be a property without backing field. Use method ordering instead + decl.getter?.let { getter -> + return@getOrPut findMethodOrder( + ResolverImpl.instance!!.getJvmName(getter).toString() + ) { + ResolverImpl.instance!!.mapToJvmSignature(getter) + } + } + decl.setter?.let { setter -> + return@getOrPut findMethodOrder( + ResolverImpl.instance!!.getJvmName(setter).toString() + ) { + ResolverImpl.instance!!.mapToJvmSignature(setter) + } + } + orderProvider.next(decl) + } + is KSFunctionDeclarationDescriptorImpl -> { + findMethodOrder( + ResolverImpl.instance!!.getJvmName(decl).toString() + ) { + ResolverImpl.instance!!.mapToJvmSignature(decl).toString() + } + } + else -> orderProvider.nextIgnoreSealed() + } + } + } + + private inline fun findMethodOrder( + jvmName: String, + crossinline getJvmDesc: () -> String + ): Int { + val methods = methodOrdering[jvmName] + // if there is 1 method w/ that name, just return. + // otherwise, we need signature matching + return when { + methods == null -> { + orderProvider.next(jvmName) + } + methods.size == 1 -> { + // only 1 method with this name, return it, no reason to resolve jvm + // signature + methods.values.first() + } + else -> { + // need to match using the jvm signature + val jvmDescriptor = getJvmDesc() + methods.getOrPut(jvmDescriptor) { + orderProvider.next(jvmName) + } + } + } + } + + override fun visitField( + name: Name, + desc: String, + initializer: Any? + ): KotlinJvmBinaryClass.AnnotationVisitor? { + fieldOrdering.getOrPut(name.asString()) { + orderProvider.next(name) + } + return null + } + + override fun visitMethod( + name: Name, + desc: String + ): KotlinJvmBinaryClass.MethodAnnotationVisitor? { + methodOrdering.getOrPut(name.asString()) { + mutableMapOf() + }.put(desc, orderProvider.next(name)) + return null + } + + /** + * Helper class to generate order values for items. + * Each time we see a new declaration, we give it an increasing order. + * + * This provider can also run in STRICT MODE to ensure that if we don't find an expected value + * during sorting, we can crash instead of picking the next ID. For now, it is only used for + * testing. + */ + private class OrderProvider { + private var nextId = 0 + private var sealed = false + + /** + * Seals the provider, preventing it from generating new IDs if [STRICT_MODE] is enabled. + */ + fun seal() { + sealed = true + } + + /** + * Returns the next available order value. + * + * @param ref Used for logging if the data is sealed and we shouldn't provide a new order. + */ + fun next(ref: Any): Int { + check(!sealed || !STRICT_MODE) { + "couldn't find item $ref" + } + return nextId ++ + } + + /** + * Returns the next ID without checking whether the model is sealed or not. This is useful + * for declarations where we don't care about the order (e.g. inner class declarations). + */ + fun nextIgnoreSealed(): Int { + return nextId ++ + } + } + companion object { + /** + * Used in tests to prevent fallback behavior of creating a new ID when we cannot find the + * order. + */ + var STRICT_MODE = false + } +} + +/** + * Same as KSDeclarationContainer.declarations, but sorted by declaration order in the source. + * + * Note that this is SLOW. AVOID IF POSSIBLE. + */ +@KspExperimental +internal val KSDeclarationContainer.declarationsInSourceOrder: Sequence<KSDeclaration> + get() { + // Only Kotlin libs can be out of order. + if (this !is KSClassDeclarationDescriptorImpl || origin != Origin.KOTLIN_LIB) + return declarations + + val declarationOrdering = ( + (descriptor as? DeserializedClassDescriptor)?.source as? KotlinJvmBinarySourceElement + )?.binaryClass?.let { + DeclarationOrdering(it) + } ?: return declarations + + return (declarations as? Sequence<KSDeclarationDescriptorImpl>)?.sortedWith(declarationOrdering.comparator) + ?: declarations + } + +internal val KSPropertyDeclaration.jvmAccessFlag: Int + get() = when (origin) { + Origin.KOTLIN_LIB -> { + val descriptor = (this as KSPropertyDeclarationDescriptorImpl).descriptor + val kotlinBinaryJavaClass = descriptor.getContainingKotlinJvmBinaryClass() + // 0 if no backing field + kotlinBinaryJavaClass?.let { + BinaryClassInfoCache.getCached(it).fieldAccFlags.get(this.simpleName.asString()) ?: 0 + } ?: 0 + } + Origin.JAVA_LIB -> { + val descriptor = (this as KSPropertyDeclarationDescriptorImpl).descriptor + ((descriptor.source as? JavaSourceElement)?.javaElement as? BinaryJavaField)?.access ?: 0 + } + else -> throw IllegalStateException("this function expects only KOTLIN_LIB or JAVA_LIB") + } + +internal val KSFunctionDeclaration.jvmAccessFlag: Int + get() = when (origin) { + Origin.KOTLIN_LIB -> { + val jvmDesc = ResolverImpl.instance!!.mapToJvmSignatureInternal(this) + val descriptor = (this as KSFunctionDeclarationDescriptorImpl).descriptor + // Companion.<init> doesn't have containing KotlinJvmBinaryClass. + val kotlinBinaryJavaClass = descriptor.getContainingKotlinJvmBinaryClass() + kotlinBinaryJavaClass?.let { + BinaryClassInfoCache.getCached(it).methodAccFlags.get(this.simpleName.asString() + jvmDesc) ?: 0 + } ?: 0 + } + Origin.JAVA_LIB -> { + val descriptor = (this as KSFunctionDeclarationDescriptorImpl).descriptor + // Some functions, like `equals` in builtin types, doesn't have source. + ((descriptor.source as? JavaSourceElement)?.javaElement as? BinaryJavaMethodBase)?.access ?: 0 + } + else -> throw IllegalStateException("this function expects only KOTLIN_LIB or JAVA_LIB") + } + +// Compiler subtype checking does not convert Java types to Kotlin types, while getting super types +// from a java type does the conversion, therefore resulting in subtype checking for Java types to fail. +// Check if candidate super type is a Java type, convert to Kotlin type for subtype checking. +// Also, if the type is a generic deserialized type, that actually represents a function type, +// a conversion is also required to yield a type with a correctly recognised descriptor. +internal fun KotlinType.convertKotlinType(): KotlinType { + val declarationDescriptor = this.constructor.declarationDescriptor + val base = if (declarationDescriptor?.shouldMapToKotlinForAssignabilityCheck() == true) { + JavaToKotlinClassMapper + .mapJavaToKotlin(declarationDescriptor.fqNameSafe, ResolverImpl.instance!!.module.builtIns) + ?.defaultType + ?.replace(this.arguments) + ?: this + } else this + val newarguments = + base.arguments.map { if (it !is StarProjectionImpl) it.replaceType(it.type.convertKotlinType()) else it } + val upperBound = if (base.unwrap() is FlexibleType) { + (base.unwrap() as FlexibleType).upperBound.arguments + .map { if (it !is StarProjectionImpl) it.replaceType(it.type.convertKotlinType()) else it } + } else newarguments + return base.replace( + newarguments, + annotations, + upperBound + ) +} + +private fun ClassifierDescriptor.shouldMapToKotlinForAssignabilityCheck(): Boolean { + return when (this) { + is JavaClassDescriptor -> true // All java types need to be mapped to kotlin + is DeserializedDescriptor -> { + // If this is a generic deserialized type descriptor, which actually is a kotlin function type. + // This may be the case if the client explicitly mapped a kotlin function type to the JVM one. + // Such types need to be remapped to be represented by a correct function class descriptor. + fqNameSafe.parent().asString() == "kotlin.jvm.functions" + } + else -> false + } +} + +fun DeclarationDescriptor.findPsi(): PsiElement? { + // For synthetic members. + if ((this is CallableMemberDescriptor) && this.kind != CallableMemberDescriptor.Kind.DECLARATION) return null + val psi = (this as? DeclarationDescriptorWithSource)?.source?.getPsi() ?: return null + if (psi is KtElement) return psi + + // Find Java PSIs loaded by KSP + val containingFile = ResolverImpl.instance!!.findPsiJavaFile(psi.containingFile.virtualFile.path) ?: return null + val leaf = containingFile.findElementAt(psi.textOffset) ?: return null + return leaf.parentsWithSelf.firstOrNull { psi.manager.areElementsEquivalent(it, psi) } +} |