Move frontend independent parts of KtReference.isReferenceTo up to share it.
This commit is contained in:
+3
-7
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -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? {
|
||||
|
||||
@@ -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)
|
||||
|
||||
+8
-1
@@ -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()
|
||||
|
||||
-2
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user