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

org.jetbrains.kotlin.idea.references.KtReference.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2021 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.idea.references

import com.intellij.openapi.application.ApplicationManager
import com.intellij.psi.*
import com.intellij.psi.impl.source.resolve.ResolveCache
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getLabeledParent
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.util.OperatorNameConventions

interface KtReference : PsiPolyVariantReference {
    val resolver: ResolveCache.PolyVariantResolver

    override fun getElement(): KtElement

    val resolvesByNames: Collection
}

abstract class AbstractKtReference(element: T) : PsiPolyVariantReferenceBase(element), KtReference {
    val expression: T
        get() = element

    override fun multiResolve(incompleteCode: Boolean): Array =
        ResolveCache.getInstance(expression.project).resolveWithCaching(this, resolver, false, incompleteCode)

    override fun getCanonicalText(): String = expression.text

    open fun canRename(): Boolean = false
    override fun handleElementRename(newElementName: String): PsiElement? =
        if (canRename())
            getKtReferenceMutateService().handleElementRename(this, newElementName)
        else
            null

    override fun bindToElement(element: PsiElement): PsiElement =
        getKtReferenceMutateService().bindToElement(this, element)

    protected fun getKtReferenceMutateService(): KtReferenceMutateService =
        ApplicationManager.getApplication().getService(KtReferenceMutateService::class.java)
            ?: throw IllegalStateException("Cannot handle element rename because KtReferenceMutateService is missing")

    @Suppress("UNCHECKED_CAST")
    override fun getVariants(): Array = PsiReference.EMPTY_ARRAY as Array

    override fun isSoft(): Boolean = false

    override fun toString() = this::class.java.simpleName + ": " + expression.text

    protected open fun canBeReferenceTo(candidateTarget: PsiElement): Boolean = true

    protected open fun isReferenceToImportAlias(alias: KtImportAlias): Boolean = false

    override fun isReferenceTo(candidateTarget: PsiElement): Boolean {
        if (!canBeReferenceTo(candidateTarget)) return false

        val unwrappedCandidate = candidateTarget.unwrapped?.originalElement ?: return false

        // Optimizations to return early for cases where this reference cannot
        // refer to the candidate target.
        when (this) {
            is KtInvokeFunctionReference -> {
                if (candidateTarget !is KtNamedFunction && candidateTarget !is PsiMethod) return false
                if ((candidateTarget as PsiNamedElement).name != OperatorNameConventions.INVOKE.asString()) {
                    return false
                }
            }
            is KtDestructuringDeclarationReference -> {
                if (candidateTarget !is KtNamedFunction && candidateTarget !is KtParameter && candidateTarget !is PsiMethod) return false
            }
            is KtSimpleNameReference -> {
                if (unwrappedCandidate is PsiMethod && !canBePsiMethodReference()) return false
            }
        }

        val element = element

        if (candidateTarget is KtImportAlias &&
            (element is KtSimpleNameExpression && element.getReferencedName() == candidateTarget.name ||
                    this is KDocReference && this.canonicalText == candidateTarget.name)
        ) {
            return isReferenceToImportAlias(candidateTarget)
        }

        if (element is KtLabelReferenceExpression) {
            when ((element.parent as? KtContainerNode)?.parent) {
                is KtReturnExpression -> unwrappedTargets.forEach {
                    if (it !is KtFunctionLiteral && !(it is KtNamedFunction && it.name.isNullOrEmpty())) return@forEach
                    it as KtFunction

                    val labeledExpression = it.getLabeledParent(element.getReferencedName())
                    if (labeledExpression != null) {
                        if (candidateTarget == labeledExpression) return true else return@forEach
                    }
                    val calleeReference = it.getCalleeByLambdaArgument()?.mainReference ?: return@forEach
                    if (calleeReference.isReferenceTo(candidateTarget)) return true
                }
                is KtBreakExpression, is KtContinueExpression -> unwrappedTargets.forEach {
                    val labeledExpression = (it as? KtExpression)?.getLabeledParent(element.getReferencedName()) ?: return@forEach
                    if (candidateTarget == labeledExpression) return true
                }
            }
        }

        val targets = unwrappedTargets
        val manager = candidateTarget.manager

        if (targets.any { manager.areElementsEquivalent(unwrappedCandidate, it) }) {
            return true
        }

        return targets.any {
            it.isConstructorOf(unwrappedCandidate) ||
                    it is KtObjectDeclaration && it.isCompanion() && it.getNonStrictParentOfType() == unwrappedCandidate
        }
    }

    private fun KtSimpleNameReference.canBePsiMethodReference(): Boolean {
        // NOTE: Accessor references are handled separately, see SyntheticPropertyAccessorReference
        if (element == (element.parent as? KtCallExpression)?.calleeExpression) return true

        val callableReference = element.getParentOfTypeAndBranch { callableReference }
        if (callableReference != null) return true

        val binaryOperator = element.getParentOfTypeAndBranch { operationReference }
        if (binaryOperator != null) return true

        val unaryOperator = element.getParentOfTypeAndBranch { operationReference }
        if (unaryOperator != null) return true

        if (element.getNonStrictParentOfType() != null) return true

        return false
    }

    private fun PsiElement.isConstructorOf(unwrappedCandidate: PsiElement) =
        when {
            // call to Java constructor
            this is PsiMethod && isConstructor && containingClass == unwrappedCandidate -> true
            // call to Kotlin constructor
            this is KtConstructor<*> && getContainingClassOrObject().isEquivalentTo(unwrappedCandidate) -> true
            else -> false
        }
}

abstract class KtSimpleReference(expression: T) : AbstractKtReference(expression)

abstract class KtMultiReference(expression: T) : AbstractKtReference(expression)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy