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

org.jetbrains.kotlin.analysis.api.fir.psiUtils.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.analysis.api.fir

import com.intellij.psi.PsiElement
import com.intellij.psi.util.parentOfType
import org.jetbrains.kotlin.KtFakeSourceElement
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtRealPsiSourceElement
import org.jetbrains.kotlin.analysis.api.KaImplementationDetail
import org.jetbrains.kotlin.analysis.api.fir.symbols.KaFirPropertySetterSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KaSymbolLocation
import org.jetbrains.kotlin.analysis.api.symbols.KaSymbolModality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.unwrapFakeOverridesOrDelegated
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.calls.util.isSingleUnderscore
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
import org.jetbrains.kotlin.utils.exceptions.withPsiEntry

private val allowedFakeElementKinds = setOf(
    KtFakeSourceElementKind.FromUseSiteTarget,
    KtFakeSourceElementKind.PropertyFromParameter,
    KtFakeSourceElementKind.ItLambdaParameter,
    KtFakeSourceElementKind.EnumGeneratedDeclaration,
    KtFakeSourceElementKind.DataClassGeneratedMembers,
    KtFakeSourceElementKind.ImplicitConstructor,
    KtFakeSourceElementKind.ImplicitJavaAnnotationConstructor,
)

internal fun FirElement.getAllowedPsi() = when (val source = source) {
    null -> null
    is KtRealPsiSourceElement -> source.psi
    is KtFakeSourceElement -> if (source.kind in allowedFakeElementKinds) psi else null
    else -> null
}

internal fun FirElement.findPsi(): PsiElement? =
    getAllowedPsi()

@KaImplementationDetail
fun FirBasedSymbol<*>.findPsi(): PsiElement? {
    return if (this is FirCallableSymbol<*>) {
        fir.unwrapFakeOverridesOrDelegated().findPsi()
    } else {
        fir.findPsi()
    } ?: FirSyntheticFunctionInterfaceSourceProvider.findPsi(fir)
}

/**
 * Finds [PsiElement] which will be used as go-to referenced element for [KtPsiReference]
 * For data classes & enums generated members like `copy` `componentN`, `values` it will return corresponding enum/data class
 * Otherwise, behaves the same way as [findPsi] returns exact PSI declaration corresponding to passed [FirDeclaration]
 */
internal fun FirDeclaration.findReferencePsi(): PsiElement? {
    return if (this is FirCallableDeclaration) {
        unwrapFakeOverridesOrDelegated().psi
    } else {
        psi
    } ?: FirSyntheticFunctionInterfaceSourceProvider.findPsi(this)
}

/**
 * Not null [CallableId] for functions which are not local and are not a member of a local class.
 */
internal val KtNamedFunction.callableId: CallableId?
    get() = if (isLocal) null else callableIdForName(nameAsSafeName)

internal val KtEnumEntry.callableId: CallableId?
    get() = callableIdForName(nameAsSafeName)

internal val KtProperty.callableId: CallableId?
    get() = if (isLocal) null else callableIdForName(nameAsSafeName)

internal fun KtDeclaration.callableIdForName(callableName: Name): CallableId? {
    val containingClassOrObject = containingClassOrObject
    if (containingClassOrObject != null) {
        return containingClassOrObject.getClassId()?.let { classId ->
            CallableId(classId = classId, callableName = callableName)
        }
    }

    return CallableId(packageName = containingKtFile.packageFqName, callableName = callableName)
}

internal val KtNamedFunction.kaSymbolModality: KaSymbolModality?
    get() = kaSymbolModalityByModifiers ?: when {
        isTopLevel || isLocal -> KaSymbolModality.FINAL

        // Green code cannot have those modifiers with other modalities
        hasModifier(KtTokens.INLINE_KEYWORD) || hasModifier(KtTokens.TAILREC_KEYWORD) -> KaSymbolModality.FINAL

        else -> null
    }

internal val KtDestructuringDeclarationEntry.entryName: Name
    get() = if (isSingleUnderscore) SpecialNames.UNDERSCORE_FOR_UNUSED_VAR else nameAsSafeName

internal val KtParameter.parameterName: Name
    get() = when {
        destructuringDeclaration != null -> SpecialNames.DESTRUCT
        isSingleUnderscore -> SpecialNames.UNDERSCORE_FOR_UNUSED_VAR
        else -> nameAsSafeName
    }

internal val KtClassOrObject.kaSymbolModality: KaSymbolModality?
    get() = kaSymbolModalityByModifiers ?: when {
        this is KtObjectDeclaration || this is KtEnumEntry -> KaSymbolModality.FINAL
        this !is KtClass -> null
        isAnnotation() || isEnum() -> KaSymbolModality.FINAL
        isInterface() -> KaSymbolModality.ABSTRACT

        // Green code cannot have those modifiers with other modalities
        isValue() || isInline() -> KaSymbolModality.FINAL
        else -> null
    }

internal val KtProperty.kaSymbolModality: KaSymbolModality?
    get() = kaSymbolModalityByModifiers ?: when {

        // Green code cannot have those modifiers with other modalities
        hasModifier(KtTokens.CONST_KEYWORD) -> KaSymbolModality.FINAL
        else -> null
    }

internal val KtDeclaration.kaSymbolModalityByModifiers: KaSymbolModality?
    get() = when {
        hasModifier(KtTokens.FINAL_KEYWORD) -> KaSymbolModality.FINAL
        hasModifier(KtTokens.ABSTRACT_KEYWORD) -> KaSymbolModality.ABSTRACT
        hasModifier(KtTokens.OPEN_KEYWORD) -> KaSymbolModality.OPEN
        this is KtClassOrObject && hasModifier(KtTokens.SEALED_KEYWORD) -> KaSymbolModality.SEALED
        else -> null
    }

internal val KtNamedFunction.visibility: Visibility?
    get() = when {
        isLocal -> Visibilities.Local
        else -> visibilityByModifiers
    }

internal val KtClassOrObject.visibility: Visibility?
    get() = when {
        isLocal -> Visibilities.Local
        else -> visibilityByModifiers
    }

internal val KtProperty.visibility: Visibility?
    get() = when {
        isLocal -> Visibilities.Local
        else -> visibilityByModifiers
    }

internal val KtDeclaration.visibilityByModifiers: Visibility?
    get() = when {
        hasModifier(KtTokens.PRIVATE_KEYWORD) -> Visibilities.Private
        hasModifier(KtTokens.INTERNAL_KEYWORD) -> Visibilities.Internal
        hasModifier(KtTokens.PROTECTED_KEYWORD) -> Visibilities.Protected
        hasModifier(KtTokens.PUBLIC_KEYWORD) -> Visibilities.Public
        else -> null
    }

internal val KtDeclaration.location: KaSymbolLocation
    get() {
        val parentDeclaration = parentOfType()
        if (this is KtTypeParameter) {
            return if (parentDeclaration is KtClassOrObject) KaSymbolLocation.CLASS else KaSymbolLocation.LOCAL
        }

        return when (parentDeclaration) {
            null, is KtScript -> KaSymbolLocation.TOP_LEVEL
            is KtClassOrObject -> KaSymbolLocation.CLASS
            is KtProperty -> KaSymbolLocation.PROPERTY
            is KtDeclarationWithBody, is KtDeclarationWithInitializer, is KtAnonymousInitializer -> KaSymbolLocation.LOCAL
            else -> errorWithAttachment("Unexpected parent declaration: ${parentDeclaration::class.simpleName}") {
                withPsiEntry("parentDeclaration", parentDeclaration)
                withPsiEntry("psi", this@location)
            }
        }
    }

internal fun KtAnnotated.hasAnnotation(useSiteTarget: AnnotationUseSiteTarget): Boolean = annotationEntries.any {
    it.useSiteTarget?.getAnnotationUseSiteTarget() == useSiteTarget
}

/**
 * **true** if for [this] property should be created [KaFirPropertySetterSymbol]
 * instead of [KaFirDefaultPropertySetterSymbol][org.jetbrains.kotlin.analysis.api.fir.symbols.KaFirDefaultPropertySetterSymbol].
 *
 * The implementation is aligned with [PsiRawFirBuilder][org.jetbrains.kotlin.fir.builder.PsiRawFirBuilder.Visitor.toFirPropertyAccessor]
 */
internal val KtProperty.hasRegularSetter: Boolean
    get() = isVar && hasDelegate() || setter?.isRegularAccessor == true

/**
 * **true** if for [this] property should be created [KaFirPropertyGetterSymbol][org.jetbrains.kotlin.analysis.api.fir.symbols.KaFirPropertyGetterSymbol]
 * instead of [KaFirDefaultPropertyGetterSymbol][org.jetbrains.kotlin.analysis.api.fir.symbols.KaFirDefaultPropertyGetterSymbol].
 *
 * The implementation is aligned with [PsiRawFirBuilder][org.jetbrains.kotlin.fir.builder.PsiRawFirBuilder.Visitor.toFirPropertyAccessor]
 */
internal val KtProperty.hasRegularGetter: Boolean
    get() = hasDelegate() || getter?.isRegularAccessor == true

/**
 * Only [KtPropertyAccessor.hasBody] check should be enough, but we need [KtFile.isCompiled] check
 * to work around the case with [loadProperty][org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization.StubBasedFirMemberDeserializer.loadProperty]
 * as all deserialized properties should have regular accessors, but they don't have bodies.
 *
 * @see hasRegularGetter
 * @see hasRegularSetter
 */
private val KtPropertyAccessor.isRegularAccessor: Boolean
    get() = hasBody() || containingKtFile.isCompiled




© 2015 - 2025 Weber Informatics LLC | Privacy Policy