diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/KtFe10SimpleNameReference.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/KtFe10SimpleNameReference.kt index 7cc9cca4d3e..e1501d0a21c 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/KtFe10SimpleNameReference.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/KtFe10SimpleNameReference.kt @@ -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 } -} \ No newline at end of file +} diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/FirSimpleNameReference.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/FirSimpleNameReference.kt index 04c76335180..4519a24ebb8 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/FirSimpleNameReference.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/references/FirSimpleNameReference.kt @@ -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? { diff --git a/analysis/kt-reference/build.gradle.kts b/analysis/kt-reference/build.gradle.kts index 0536680e2de..a7505b19557 100644 --- a/analysis/kt-reference/build.gradle.kts +++ b/analysis/kt-reference/build.gradle.kts @@ -5,6 +5,7 @@ plugins { dependencies { implementation(project(":compiler:psi")) + implementation(project(":compiler:light-classes")) implementation(intellijCore()) compileOnly(commonDependency("com.google.guava:guava")) diff --git a/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/KtReference.kt b/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/KtReference.kt index f21f08ce9db..ac4a2e97229 100644 --- a/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/KtReference.kt +++ b/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/KtReference.kt @@ -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 @@ -40,8 +44,105 @@ abstract class AbstractKtReference(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() == 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) \ No newline at end of file +abstract class KtMultiReference(expression: T) : AbstractKtReference(expression) diff --git a/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/KtSimpleNameReference.kt b/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/KtSimpleNameReference.kt index 12cfb26ed9c..afa864578d7 100644 --- a/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/KtSimpleNameReference.kt +++ b/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/KtSimpleNameReference.kt @@ -16,7 +16,14 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.types.expressions.OperatorConventions abstract class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleReference(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() diff --git a/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt b/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt index 781882b78e7..ddfa36022ab 100644 --- a/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt +++ b/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt @@ -14,8 +14,6 @@ abstract class SyntheticPropertyAccessorReference( expression: KtNameReferenceExpression, val getter: Boolean ) : KtSimpleReference(expression) { - protected abstract fun additionalIsReferenceToChecker(element: PsiElement): Boolean - protected fun isAccessorName(name: String): Boolean { if (getter) { return name.startsWith("get") || name.startsWith("is") diff --git a/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/referenceUtils.kt b/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/referenceUtils.kt index 8aacfc0ced1..8947d44f90a 100644 --- a/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/referenceUtils.kt +++ b/analysis/kt-reference/src/org/jetbrains/kotlin/idea/references/referenceUtils.kt @@ -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 + get() { + fun PsiElement.adjust(): PsiElement? = when (val target = unwrapped?.originalElement) { + is KtPropertyAccessor -> target.getNonStrictParentOfType() + 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 { 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 +}