Move frontend independent parts of KtReference.isReferenceTo up to share it.

This commit is contained in:
Mads Ager
2022-02-03 13:16:37 +01:00
committed by Ilya Kirillov
parent 6f0bb0eda5
commit 17aee1e944
7 changed files with 152 additions and 20 deletions
@@ -36,12 +36,8 @@ abstract class KtFe10SimpleNameReference(expression: KtSimpleNameExpression) : K
internal class CliKtFe10SimpleNameReference(
expression: KtSimpleNameExpression
) : KtFe10SimpleNameReference(expression), CliKtFe10Reference {
override fun doCanBeReferenceTo(candidateTarget: PsiElement): Boolean {
return true
}
override fun isReferenceTo(element: PsiElement): Boolean {
return resolve() == element
override fun isReferenceToViaExtension(element: PsiElement): Boolean {
return false
}
override fun handleElementRename(newElementName: String): PsiElement? {
@@ -59,4 +55,4 @@ internal class CliKtFe10SimpleNameReference(
override fun getImportAlias(): KtImportAlias? {
return null
}
}
}
@@ -43,13 +43,13 @@ internal class KtFirSimpleNameReference(
return fixUpAnnotationCallResolveToCtor(results)
}
override fun doCanBeReferenceTo(candidateTarget: PsiElement): Boolean {
override fun canBeReferenceTo(candidateTarget: PsiElement): Boolean {
return true // TODO
}
override fun isReferenceTo(element: PsiElement): Boolean {
return resolve() == element //todo
// Extension point used for deprecated Android Extensions. Not going to implement for FIR.
override fun isReferenceToViaExtension(element: PsiElement): Boolean {
return false
}
override fun handleElementRename(newElementName: String): PsiElement? {
+1
View File
@@ -5,6 +5,7 @@ plugins {
dependencies {
implementation(project(":compiler:psi"))
implementation(project(":compiler:light-classes"))
implementation(intellijCore())
compileOnly(commonDependency("com.google.guava:guava"))
@@ -8,9 +8,13 @@ package org.jetbrains.kotlin.idea.references
import com.intellij.psi.*
import com.intellij.psi.impl.source.resolve.ResolveCache
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtReferenceExpression
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<KtReference>
@@ -40,8 +44,105 @@ abstract class AbstractKtReference<T : KtElement>(element: T) : PsiPolyVariantRe
override fun isSoft(): Boolean = false
override fun toString() = this::class.java.simpleName + ": " + expression.text
@Suppress("UNUSED_PARAMETER")
protected open fun canBeReferenceTo(candidateTarget: PsiElement): Boolean = true
@Suppress("UNUSED_PARAMETER")
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<KtClass>() == 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<KtCallableReferenceExpression> { callableReference }
if (callableReference != null) return true
val binaryOperator = element.getParentOfTypeAndBranch<KtBinaryExpression> { operationReference }
if (binaryOperator != null) return true
val unaryOperator = element.getParentOfTypeAndBranch<KtUnaryExpression> { operationReference }
if (unaryOperator != null) return true
if (element.getNonStrictParentOfType<KtImportDirective>() != 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<T : KtReferenceExpression>(expression: T) : AbstractKtReference<T>(expression)
abstract class KtMultiReference<T : KtElement>(expression: T) : AbstractKtReference<T>(expression)
abstract class KtMultiReference<T : KtElement>(expression: T) : AbstractKtReference<T>(expression)
@@ -16,7 +16,14 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.types.expressions.OperatorConventions
abstract class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleReference<KtSimpleNameExpression>(expression) {
protected abstract fun doCanBeReferenceTo(candidateTarget: PsiElement): Boolean
// Extension point used by deprecated android extensions.
abstract fun isReferenceToViaExtension(element: PsiElement): Boolean
override fun isReferenceTo(candidateTarget: PsiElement): Boolean {
if (!canBeReferenceTo(candidateTarget)) return false
if (isReferenceToViaExtension(candidateTarget)) return true
return super.isReferenceTo(candidateTarget)
}
override fun getRangeInElement(): TextRange {
val element = element.getReferencedNameElement()
@@ -14,8 +14,6 @@ abstract class SyntheticPropertyAccessorReference(
expression: KtNameReferenceExpression,
val getter: Boolean
) : KtSimpleReference<KtNameReferenceExpression>(expression) {
protected abstract fun additionalIsReferenceToChecker(element: PsiElement): Boolean
protected fun isAccessorName(name: String): Boolean {
if (getter) {
return name.startsWith("get") || name.startsWith("is")
@@ -5,10 +5,15 @@
package org.jetbrains.kotlin.idea.references
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiPolyVariantReference
import com.intellij.psi.PsiReference
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
@@ -27,3 +32,27 @@ val KtElement.mainReference: KtReference?
is KDocName -> mainReference
else -> references.firstIsInstanceOrNull()
}
// Navigation element of the resolved reference
// For property accessor return enclosing property
val PsiReference.unwrappedTargets: Set<PsiElement>
get() {
fun PsiElement.adjust(): PsiElement? = when (val target = unwrapped?.originalElement) {
is KtPropertyAccessor -> target.getNonStrictParentOfType<KtProperty>()
else -> target
}
return when (this) {
is PsiPolyVariantReference -> multiResolve(false).mapNotNullTo(HashSet()) { it.element?.adjust() }
else -> listOfNotNull(resolve()?.adjust()).toSet()
}
}
fun KtFunction.getCalleeByLambdaArgument(): KtSimpleNameExpression? {
val argument = getParentOfTypeAndBranch<KtValueArgument> { getArgumentExpression() } ?: return null
val callExpression = when (argument) {
is KtLambdaArgument -> argument.parent as? KtCallExpression
else -> (argument.parent as? KtValueArgumentList)?.parent as? KtCallExpression
} ?: return null
return callExpression.calleeExpression as? KtSimpleNameExpression
}