blob: 01459d6046da99cd4fb6c342413bfdc647099ab7 [file] [log] [blame]
/*
* 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.ExceptionMessage
import com.google.devtools.ksp.MemoizedSequence
import com.google.devtools.ksp.processing.impl.ResolverImpl
import com.google.devtools.ksp.symbol.*
import com.google.devtools.ksp.symbol.ClassKind
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.lang.jvm.JvmModifier
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.impl.source.PsiClassImpl
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
import org.jetbrains.kotlin.load.java.descriptors.JavaClassConstructorDescriptor
import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver
import org.jetbrains.kotlin.load.java.structure.impl.JavaConstructorImpl
import org.jetbrains.kotlin.load.java.structure.impl.JavaMethodImpl
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.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.resolve.descriptorUtil.getOwnerForEffectiveDispatchReceiverParameter
import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.StarProjectionImpl
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.replace
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private val jvmModifierMap = mapOf(
JvmModifier.PUBLIC to Modifier.PUBLIC,
JvmModifier.PRIVATE to Modifier.PRIVATE,
JvmModifier.ABSTRACT to Modifier.ABSTRACT,
JvmModifier.FINAL to Modifier.FINAL,
JvmModifier.PROTECTED to Modifier.PROTECTED,
JvmModifier.STATIC to Modifier.JAVA_STATIC,
JvmModifier.STRICTFP to Modifier.JAVA_STRICT,
JvmModifier.NATIVE to Modifier.JAVA_NATIVE,
JvmModifier.SYNCHRONIZED to Modifier.JAVA_SYNCHRONIZED,
JvmModifier.TRANSIENT to Modifier.JAVA_TRANSIENT,
JvmModifier.VOLATILE to Modifier.JAVA_VOLATILE
)
private val modifierMap = mapOf(
KtTokens.PUBLIC_KEYWORD to Modifier.PUBLIC,
KtTokens.PRIVATE_KEYWORD to Modifier.PRIVATE,
KtTokens.INTERNAL_KEYWORD to Modifier.INTERNAL,
KtTokens.PROTECTED_KEYWORD to Modifier.PROTECTED,
KtTokens.IN_KEYWORD to Modifier.IN,
KtTokens.OUT_KEYWORD to Modifier.OUT,
KtTokens.OVERRIDE_KEYWORD to Modifier.OVERRIDE,
KtTokens.LATEINIT_KEYWORD to Modifier.LATEINIT,
KtTokens.ENUM_KEYWORD to Modifier.ENUM,
KtTokens.SEALED_KEYWORD to Modifier.SEALED,
KtTokens.ANNOTATION_KEYWORD to Modifier.ANNOTATION,
KtTokens.DATA_KEYWORD to Modifier.DATA,
KtTokens.INNER_KEYWORD to Modifier.INNER,
KtTokens.FUN_KEYWORD to Modifier.FUN,
KtTokens.VALUE_KEYWORD to Modifier.VALUE,
KtTokens.SUSPEND_KEYWORD to Modifier.SUSPEND,
KtTokens.TAILREC_KEYWORD to Modifier.TAILREC,
KtTokens.OPERATOR_KEYWORD to Modifier.OPERATOR,
KtTokens.INFIX_KEYWORD to Modifier.INFIX,
KtTokens.INLINE_KEYWORD to Modifier.INLINE,
KtTokens.EXTERNAL_KEYWORD to Modifier.EXTERNAL,
KtTokens.ABSTRACT_KEYWORD to Modifier.ABSTRACT,
KtTokens.FINAL_KEYWORD to Modifier.FINAL,
KtTokens.OPEN_KEYWORD to Modifier.OPEN,
KtTokens.VARARG_KEYWORD to Modifier.VARARG,
KtTokens.NOINLINE_KEYWORD to Modifier.NOINLINE,
KtTokens.CROSSINLINE_KEYWORD to Modifier.CROSSINLINE,
KtTokens.REIFIED_KEYWORD to Modifier.REIFIED,
KtTokens.EXPECT_KEYWORD to Modifier.EXPECT,
KtTokens.ACTUAL_KEYWORD to Modifier.ACTUAL
)
fun KtModifierList?.toKSModifiers(): Set<Modifier> {
if (this == null)
return emptySet()
val modifiers = mutableSetOf<Modifier>()
modifiers.addAll(
modifierMap.entries
.filter { hasModifier(it.key) }
.map { it.value }
)
return modifiers
}
fun KtModifierListOwner.toKSModifiers(): Set<Modifier> {
val modifierList = this.modifierList
return modifierList.toKSModifiers()
}
fun PsiModifierListOwner.toKSModifiers(): Set<Modifier> {
val modifiers = mutableSetOf<Modifier>()
modifiers.addAll(
jvmModifierMap.entries.filter { this.hasModifier(it.key) }
.map { it.value }
.toSet()
)
if (this.modifierList?.hasExplicitModifier("default") == true) {
modifiers.add(Modifier.JAVA_DEFAULT)
}
return modifiers
}
fun MemberDescriptor.toKSModifiers(): Set<Modifier> {
val modifiers = mutableSetOf<Modifier>()
if (this.isActual) {
modifiers.add(Modifier.ACTUAL)
}
if (this.isExpect) {
modifiers.add(Modifier.EXPECT)
}
if (this.isExternal) {
modifiers.add(Modifier.EXTERNAL)
}
// we are not checking for JVM_STATIC annotation here intentionally
// see: https://github.com/google/ksp/issues/378
val isStatic = (this.containingDeclaration as? ClassDescriptor)?.let { containingClass ->
containingClass.staticScope.getContributedDescriptors(
nameFilter = {
it == this.name
}
).any {
it == this
}
} ?: false
if (isStatic) {
modifiers.add(Modifier.JAVA_STATIC)
}
when (this.modality) {
Modality.SEALED -> modifiers.add(Modifier.SEALED)
Modality.FINAL -> modifiers.add(Modifier.FINAL)
Modality.OPEN -> {
if (!isStatic && this.visibility != DescriptorVisibilities.PRIVATE) {
// private methods still show up as OPEN
modifiers.add(Modifier.OPEN)
}
}
Modality.ABSTRACT -> modifiers.add(Modifier.ABSTRACT)
}
when (this.visibility) {
DescriptorVisibilities.PUBLIC -> modifiers.add(Modifier.PUBLIC)
DescriptorVisibilities.PROTECTED,
JavaDescriptorVisibilities.PROTECTED_AND_PACKAGE,
JavaDescriptorVisibilities.PROTECTED_STATIC_VISIBILITY -> modifiers.add(Modifier.PROTECTED)
DescriptorVisibilities.PRIVATE -> modifiers.add(Modifier.PRIVATE)
DescriptorVisibilities.INTERNAL -> modifiers.add(Modifier.INTERNAL)
// Since there is no modifier for package-private, use No modifier to tell if a symbol from binary is package private.
JavaDescriptorVisibilities.PACKAGE_VISIBILITY, JavaDescriptorVisibilities.PROTECTED_STATIC_VISIBILITY -> Unit
else -> throw IllegalStateException("unhandled visibility: ${this.visibility}")
}
return modifiers
}
fun FunctionDescriptor.toFunctionKSModifiers(): Set<Modifier> {
val modifiers = mutableSetOf<Modifier>()
if (this.isSuspend) {
modifiers.add(Modifier.SUSPEND)
}
if (this.isTailrec) {
modifiers.add(Modifier.TAILREC)
}
if (this.isInline) {
modifiers.add(Modifier.INLINE)
}
if (this.isInfix) {
modifiers.add(Modifier.INFIX)
}
if (this.isOperator) {
modifiers.add(Modifier.OPERATOR)
}
if (this.overriddenDescriptors.isNotEmpty()) {
modifiers.add(Modifier.OVERRIDE)
}
return modifiers
}
fun PsiElement.findParentAnnotated(): KSAnnotated? {
var parent = 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 -> null
is KtFunction -> KSFunctionDeclarationImpl.getCached(parent)
is PsiClass -> KSClassDeclarationJavaImpl.getCached(parent)
is PsiJavaFile -> null
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)
}
fun Project.findLocationString(file: PsiFile, offset: Int): String {
val psiDocumentManager = PsiDocumentManager.getInstance(this)
val document = psiDocumentManager.getDocument(file) ?: return "<unknown>"
val lineNumber = document.getLineNumber(offset)
val offsetInLine = offset - document.getLineStartOffset(lineNumber)
return "${file.virtualFile.path}: (${lineNumber + 1}, ${offsetInLine + 1})"
}
private fun parseDocString(raw: String): String? {
val t1 = raw.trim()
if (!t1.startsWith("/**") || !t1.endsWith("*/"))
return null
val lineSep = t1.findAnyOf(listOf("\r\n", "\n", "\r"))?.second ?: ""
return t1.trim('/').trim('*').lines().joinToString(lineSep) {
it.trimStart().trimStart('*')
}
}
fun PsiElement.getDocString(): String? =
this.firstChild.siblings().firstOrNull { it is PsiComment }?.let {
parseDocString(it.text)
}
// 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 KtClassOrObject.getClassType(): ClassKind {
return when (this) {
is KtObjectDeclaration -> ClassKind.OBJECT
is KtEnumEntry -> ClassKind.ENUM_ENTRY
is KtClass -> when {
this.isEnum() -> ClassKind.ENUM_CLASS
this.isInterface() -> ClassKind.INTERFACE
this.isAnnotation() -> ClassKind.ANNOTATION_CLASS
else -> ClassKind.CLASS
}
else -> throw IllegalStateException("Unexpected psi type ${this.javaClass}, $ExceptionMessage")
}
}
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")
}
}
fun KSTypeReference.toKotlinType() = (resolve() as KSTypeImpl).kotlinType
internal fun KotlinType.replaceTypeArguments(newArguments: List<KSTypeArgument>): KotlinType =
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()
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}")
}
}
internal fun DeclarationDescriptor.findPsi(): PsiElement? {
// For synthetic members.
if ((this is CallableMemberDescriptor) && this.kind != CallableMemberDescriptor.Kind.DECLARATION) return null
return (this as? DeclarationDescriptorWithSource)?.source?.getPsi()
}
/**
* @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)
} else {
resolveClass(JavaMethodImpl(psiMethod).containingClass)
}
}
internal inline fun <reified T> PsiElement.findParentOfType(): T? {
var parent = this.parent
while (parent != null && parent !is T) {
parent = parent.parent
}
return parent as? T
}
internal fun getInstanceForCurrentRound(node: KSNode): KSNode? {
when (node.origin) {
Origin.KOTLIN_LIB, Origin.JAVA_LIB -> return null
}
return 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
internal fun <T> Sequence<T>.memoized() = MemoizedSequence(this)
/**
* Custom check for backing fields of descriptors that support properties coming from .class files.
* The compiler API always returns true for them even when they don't have backing fields.
*/
internal fun PropertyDescriptor.hasBackingFieldWithBinaryClassSupport(): Boolean {
return when {
extensionReceiverParameter != null -> false // extension properties do not have backing fields
compileTimeInitializer != null -> true // compile time initialization requires backing field
this is DeserializedPropertyDescriptor -> this.hasBackingFieldInBinaryClass() // kotlin class, check binary
this.source is KotlinSourceElement -> this.declaresDefaultValue // kotlin source
else -> true // Java source or class
}
}
/**
* Lookup cache for field names names for deserialized classes.
* To check if a field has backing field, we need to look for binary field names, hence they are cached here.
*/
internal object BinaryFieldsCache : KSObjectCache<ClassId, Set<Name>>() {
fun getCached(
kotlinJvmBinaryClass: KotlinJvmBinaryClass
) = cache.getOrPut(kotlinJvmBinaryClass.classId) {
val visitor = PropNamesVisitor()
kotlinJvmBinaryClass.visitMembers(visitor, null)
visitor.propNames
}
private class PropNamesVisitor : KotlinJvmBinaryClass.MemberVisitor {
val propNames = mutableSetOf<Name>()
override fun visitField(name: Name, desc: String, initializer: Any?): KotlinJvmBinaryClass.AnnotationVisitor? {
propNames.add(name)
return null
}
override fun visitMethod(name: Name, desc: String): KotlinJvmBinaryClass.MethodAnnotationVisitor? {
return null
}
}
}
/**
* Workaround for backingField in deserialized descriptors.
* They always return non-null for backing field even when they don't have a backing field.
*/
private fun DeserializedPropertyDescriptor.hasBackingFieldInBinaryClass(): Boolean {
val kotlinJvmBinaryClass = if (containingDeclaration.isCompanionObject()) {
// Companion objects have backing fields in containing classes.
// https://kotlinlang.org/docs/java-to-kotlin-interop.html#static-fields
val container = containingDeclaration.containingDeclaration as? DeserializedClassDescriptor
container?.source?.safeAs<KotlinJvmBinarySourceElement>()?.binaryClass
} else {
this.getContainingKotlinJvmBinaryClass()
} ?: return false
return BinaryFieldsCache.getCached(kotlinJvmBinaryClass).contains(name)
}
// from: https://github.com/JetBrains/kotlin/blob/92d200e093c693b3c06e53a39e0b0973b84c7ec5/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt#L45
private val PropertyDescriptor.declaresDefaultValue: Boolean
get() = when (val declaration = this.source.getPsi()) {
is KtDeclarationWithInitializer -> declaration.initializer != null
is KtParameter -> declaration.defaultValue != null
else -> false
}