All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.light.classes.symbol.symbolLightUtils.kt Maven / Gradle / Ivy

/*
 * Copyright 2010-2024 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.light.classes.symbol

import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.TypeConversionUtil
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.analysis.api.base.KaConstantValue
import org.jetbrains.kotlin.analysis.api.platform.modification.createProjectWideOutOfBlockModificationTracker
import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KaSymbolPointer
import org.jetbrains.kotlin.analysis.api.types.*
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.asJava.elements.KtLightMember
import org.jetbrains.kotlin.asJava.elements.psiType
import org.jetbrains.kotlin.light.classes.symbol.annotations.*
import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassBase
import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassForInterface
import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassForInterfaceDefaultImpls
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
import java.util.*

internal fun  L.invalidAccess(): Nothing =
    error("Cls delegate shouldn't be accessed for symbol light classes! Qualified name: ${javaClass.name}")

context(KaSession)
@Suppress("CONTEXT_RECEIVERS_DEPRECATED")
internal fun KaDeclarationSymbol.getContainingSymbolsWithSelf(): Sequence =
    generateSequence(this) { it.containingDeclaration }

internal fun KaSession.mapType(
    type: KaType,
    psiContext: PsiElement,
    mode: KaTypeMappingMode,
): PsiClassType? {
    val psiType = type.asPsiType(
        useSitePosition = psiContext,
        allowErrorTypes = true,
        mode = mode,
        forceValueClassResolution = false,
        allowNonJvmPlatforms = true,
    )

    return psiType as? PsiClassType
}

internal fun KaDeclarationSymbol.computeSimpleModality(): String? = when (modality) {
    KaSymbolModality.SEALED -> PsiModifier.ABSTRACT
    KaSymbolModality.FINAL -> PsiModifier.FINAL
    KaSymbolModality.ABSTRACT -> PsiModifier.ABSTRACT
    KaSymbolModality.OPEN -> null
}

context(KaSession)
@Suppress("CONTEXT_RECEIVERS_DEPRECATED")
internal fun KaClassSymbol.enumClassModality(): String? {
    if (memberScope.callables.any { it.modality == KaSymbolModality.ABSTRACT }) {
        return PsiModifier.ABSTRACT
    }

    if (staticDeclaredMemberScope.callables.none { it is KaEnumEntrySymbol && it.requiresSubClass() }) {
        return PsiModifier.FINAL
    }

    return null
}

context(KaSession)
@Suppress("CONTEXT_RECEIVERS_DEPRECATED")
private fun KaEnumEntrySymbol.requiresSubClass(): Boolean {
    val initializer = enumEntryInitializer ?: return false
    return initializer.combinedDeclaredMemberScope.declarations.any { it !is KaConstructorSymbol }
}

internal fun KaDeclarationSymbol.toPsiVisibilityForMember(): String = visibility.toPsiVisibilityForMember()

internal fun KaDeclarationSymbol.toPsiVisibilityForClass(isNested: Boolean): String = visibility.toPsiVisibilityForClass(isNested)

internal fun KaSymbolVisibility.toPsiVisibilityForMember(): String = when (this) {
    KaSymbolVisibility.PRIVATE -> PsiModifier.PRIVATE
    KaSymbolVisibility.PROTECTED -> PsiModifier.PROTECTED
    else -> PsiModifier.PUBLIC
}

private fun KaSymbolVisibility.toPsiVisibilityForClass(isNested: Boolean): String = when (isNested) {
    false -> when (this) {
        KaSymbolVisibility.PUBLIC,
        KaSymbolVisibility.PROTECTED,
        KaSymbolVisibility.LOCAL,
        KaSymbolVisibility.INTERNAL,
            -> PsiModifier.PUBLIC

        else -> PsiModifier.PACKAGE_LOCAL
    }

    true -> when (this) {
        KaSymbolVisibility.PUBLIC, KaSymbolVisibility.INTERNAL, KaSymbolVisibility.LOCAL -> PsiModifier.PUBLIC
        KaSymbolVisibility.PROTECTED -> PsiModifier.PROTECTED
        KaSymbolVisibility.PRIVATE -> PsiModifier.PRIVATE
        else -> PsiModifier.PACKAGE_LOCAL
    }
}

internal fun basicIsEquivalentTo(`this`: PsiElement?, that: PsiElement?): Boolean {
    if (`this` == null || that == null) return false
    if (`this` == that) return true

    if (`this` !is KtLightElement<*, *>) return false
    if (that !is KtLightElement<*, *>) return false
    if (`this`.kotlinOrigin?.isEquivalentTo(that.kotlinOrigin) == true) return true

    val thisMemberOrigin = (`this` as? KtLightMember<*>)?.lightMemberOrigin ?: return false
    if (thisMemberOrigin.isEquivalentTo(that)) return true

    val thatMemberOrigin = (that as? KtLightMember<*>)?.lightMemberOrigin ?: return false
    return thisMemberOrigin.isEquivalentTo(thatMemberOrigin)
}

internal fun KtLightElement<*, *>.isOriginEquivalentTo(that: PsiElement?): Boolean {
    return kotlinOrigin?.isEquivalentTo(that) == true
}

internal fun KaSession.getTypeNullability(type: KaType): KaTypeNullability {
    if (type is KaClassErrorType) return KaTypeNullability.NON_NULLABLE

    val ktType = type.fullyExpandedType
    if (ktType.nullability != KaTypeNullability.NON_NULLABLE) return ktType.nullability

    if (ktType.isUnitType) return KaTypeNullability.NON_NULLABLE

    if (ktType.isPrimitiveBacked) return KaTypeNullability.UNKNOWN

    if (ktType is KaTypeParameterType) {
        if (ktType.isMarkedNullable) return KaTypeNullability.NULLABLE
        val subtypeOfNullableSuperType = ktType.symbol.upperBounds.all { upperBound -> upperBound.canBeNull }
        return if (!subtypeOfNullableSuperType) KaTypeNullability.NON_NULLABLE else KaTypeNullability.UNKNOWN
    }

    if (ktType !is KaClassType) return KaTypeNullability.NON_NULLABLE
    if (ktType.typeArguments.any { it.type is KaClassErrorType }) return KaTypeNullability.NON_NULLABLE
    if (ktType.classId.shortClassName.asString() == SpecialNames.ANONYMOUS_STRING) return KaTypeNullability.NON_NULLABLE

    return ktType.nullability
}

internal val KaTypeNullability.asAnnotationQualifier: String?
    get() = when (this) {
        KaTypeNullability.NON_NULLABLE -> JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION
        KaTypeNullability.NULLABLE -> JvmAnnotationNames.JETBRAINS_NULLABLE_ANNOTATION
        else -> null
    }?.asString()

private fun escapeString(s: String): String = buildString {
    s.forEach {
        when (it) {
            '\n' -> append("\\n")
            '\r' -> append("\\r")
            '\t' -> append("\\t")
            '"' -> append("\\\"")
            '\\' -> append("\\\\")
            else -> if (it.code in 32..128) {
                append(it)
            } else {
                append("\\u%04X".format(it.code))
            }
        }
    }
}

internal fun AnnotationValue.toAnnotationMemberValue(parent: PsiElement): PsiAnnotationMemberValue? = when (this) {
    is AnnotationValue.Array -> {
        SymbolPsiArrayInitializerMemberValue(sourcePsi, parent) { arrayLiteralParent ->
            values.mapNotNull { element -> element.toAnnotationMemberValue(arrayLiteralParent) }
        }
    }

    is AnnotationValue.Annotation -> {
        SymbolLightSimpleAnnotation(
            fqName = classId?.asFqNameString(),
            parent = parent,
            arguments = normalizedArguments(),
            kotlinOrigin = sourcePsi,
        )
    }

    is AnnotationValue.Constant -> {
        constant.createPsiExpression(parent)?.let {
            when (it) {
                is PsiLiteralExpression -> SymbolPsiLiteral(sourcePsi, parent, it)
                else -> SymbolPsiExpression(sourcePsi, parent, it)
            }
        }
    }

    is AnnotationValue.EnumValue -> asPsiReferenceExpression(parent)

    is AnnotationValue.KClass -> toAnnotationMemberValue(parent)

    is AnnotationValue.Unsupported -> null
}

internal fun AnnotationValue.Annotation.normalizedArguments(): List {
    val args = arguments
    val ctorSymbolPointer = constructorSymbolPointer ?: return args
    val element = sourcePsi ?: return args // May work incorrectly. See KT-63568

    return analyzeForLightClasses(element) {
        val constructorSymbol = ctorSymbolPointer.restoreSymbolOrThrowIfDisposed()
        val params = constructorSymbol.valueParameters
        val missingVarargParameterName =
            params.singleOrNull { it.isVararg && !it.hasDefaultValue }?.name?.takeIf { name -> args.none { it.name == name } }
        if (missingVarargParameterName == null) args
        else args + AnnotationArgument(missingVarargParameterName, AnnotationValue.Array(emptyList(), null))
    }
}


private fun AnnotationValue.EnumValue.asPsiReferenceExpression(parent: PsiElement): SymbolPsiReference? {
    val fqName = this.callableId?.asSingleFqName()?.asString() ?: return null
    val psiReference = parent.project.withElementFactorySafe {
        createExpressionFromText(fqName, parent) as? PsiReferenceExpression
    } ?: return null

    return SymbolPsiReference(sourcePsi, parent, psiReference)
}

private fun AnnotationValue.KClass.toAnnotationMemberValue(parent: PsiElement): SymbolPsiClassObjectAccessExpression? {
    val typeString = classId?.asSingleFqName()?.asString() ?: return null

    val psiType = psiType(
        kotlinFqName = typeString,
        context = parent,
        boxPrimitiveType = false, /* TODO value.arrayNestedness > 0*/
    ).let(TypeConversionUtil::erasure)

    return SymbolPsiClassObjectAccessExpression(sourcePsi, parent, psiType)
}

private fun KaConstantValue.asStringForPsiExpression(): String =
    when (val value = value) {
        Double.NEGATIVE_INFINITY -> "-1.0 / 0.0"
        Double.NaN -> "0.0 / 0.0"
        Double.POSITIVE_INFINITY -> "1.0 / 0.0"
        Float.NEGATIVE_INFINITY -> "-1.0F / 0.0F"
        Float.NaN -> "0.0F / 0.0F"
        Float.POSITIVE_INFINITY -> "1.0F / 0.0F"
        '\'' -> "'\\''"
        is Char -> "'${escapeString(value.toString())}'"
        is String -> "\"${escapeString(value)}\""
        is Long -> "${value}L"
        is Float -> "${value}f"
        else -> value.toString()
    }

internal fun KaConstantValue.createPsiExpression(parent: PsiElement): PsiExpression? {
    val asString = asStringForPsiExpression()
    return parent.project.withElementFactorySafe {
        createExpressionFromText(asString, parent)
    }
}

internal inline fun  Project.withElementFactorySafe(crossinline action: PsiElementFactory.() -> T): T? {
    val instance = PsiElementFactory.getInstance(this)
    return try {
        instance.action()
    } catch (_: IncorrectOperationException) {
        null
    }
}

internal fun BitSet.copy(): BitSet = clone() as BitSet

context(KaSession)
@Suppress("CONTEXT_RECEIVERS_DEPRECATED")
internal fun  KaSymbolPointer.restoreSymbolOrThrowIfDisposed(): T =
    restoreSymbol()
        ?: errorWithAttachment("${this::class} pointer already disposed") {
            withEntry("pointer", this@restoreSymbolOrThrowIfDisposed) { it.toString() }
        }

internal fun hasTypeParameters(
    ktModule: KaModule,
    declaration: KtTypeParameterListOwner?,
    declarationPointer: KaSymbolPointer,
): Boolean = declaration?.typeParameters?.isNotEmpty() ?: declarationPointer.withSymbol(ktModule) {
    it.typeParameters.isNotEmpty()
}

internal val SymbolLightClassBase.interfaceIfDefaultImpls: SymbolLightClassForInterface?
    get() = (this as? SymbolLightClassForInterfaceDefaultImpls)?.containingClass

internal val SymbolLightClassBase.isDefaultImplsForInterfaceWithTypeParameters: Boolean
    get() = interfaceIfDefaultImpls?.hasTypeParameters() ?: false

internal fun KaSymbolPointer<*>.isValid(ktModule: KaModule): Boolean = analyzeForLightClasses(ktModule) {
    restoreSymbol() != null
}

@Suppress("NOTHING_TO_INLINE")
internal inline fun  compareSymbolPointers(
    left: KaSymbolPointer,
    right: KaSymbolPointer,
): Boolean = left === right || left.pointsToTheSameSymbolAs(right)

internal inline fun  KaSymbolPointer.withSymbol(
    ktModule: KaModule,
    crossinline action: KaSession.(T) -> R,
): R = analyzeForLightClasses(ktModule) { action(this, restoreSymbolOrThrowIfDisposed()) }

internal val KaPropertySymbol.isConstOrJvmField: Boolean get() = isConst || isJvmField
internal val KaPropertySymbol.isJvmField: Boolean get() = backingFieldSymbol?.hasJvmFieldAnnotation() == true
internal val KaPropertySymbol.isConst: Boolean get() = (this as? KaKotlinPropertySymbol)?.isConst == true
internal val KaPropertySymbol.isLateInit: Boolean get() = (this as? KaKotlinPropertySymbol)?.isLateInit == true
internal val KaPropertySymbol.canHaveNonPrivateField: Boolean get() = isConstOrJvmField || isLateInit

internal inline fun  Collection.toArrayIfNotEmptyOrDefault(default: Array): Array {
    return if (isNotEmpty()) toTypedArray() else default
}

internal inline fun  R.cachedValue(
    crossinline computer: () -> T,
): T = CachedValuesManager.getCachedValue(this) {
    val value = computer()
    val specialTrackers = (this as? SymbolLightClassBase)?.contentModificationTrackers()
    if (specialTrackers != null) {
        CachedValueProvider.Result.create(value, specialTrackers)
    } else {
        CachedValueProvider.Result.createSingleDependency(value, project.createProjectWideOutOfBlockModificationTracker())
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy