Optimize isReferenceTo() when searching for PsiMethod usages

Avoid resolving references when we know from context that a reference
at given location can't resolve to a PsiMethod.
This commit is contained in:
Dmitry Jemerov
2017-03-24 18:39:29 +01:00
parent e3f0a604d1
commit f255f2a1e0
7 changed files with 59 additions and 0 deletions
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.references
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -47,6 +48,18 @@ sealed class SyntheticPropertyAccessorReference(expression: KtNameReferenceExpre
return result
}
override fun isReferenceTo(element: PsiElement?): Boolean {
if (element !is PsiMethod || !isAccessorName(element.name)) return false
return super.isReferenceTo(element)
}
private fun isAccessorName(name: String): Boolean {
if (getter) {
return name.startsWith("get") || name.startsWith("is")
}
return name.startsWith("set")
}
override fun getRangeInElement() = TextRange(0, expression.textLength)
override fun canRename() = true
@@ -80,6 +80,9 @@ fun PsiReference.matchesTarget(candidateTarget: PsiElement): Boolean {
is KtDestructuringDeclarationReference -> {
if (candidateTarget !is KtNamedFunction && candidateTarget !is KtParameter && candidateTarget !is PsiMethod) return false
}
is KtSimpleNameReference -> {
if (unwrappedCandidate is PsiMethod && !canBePsiMethodReference()) return false
}
}
val targets = unwrappedTargets
@@ -124,6 +127,24 @@ fun PsiReference.matchesTarget(candidateTarget: PsiElement): Boolean {
return false
}
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) =
// call to Java constructor
(this is PsiMethod && isConstructor && containingClass == unwrappedCandidate) ||
@@ -0,0 +1,7 @@
// PSI_ELEMENT: com.intellij.psi.PsiMethod
// OPTIONS: usages
public class UnaryNot {
public UnaryNot <caret>not() {
return this;
}
}
@@ -0,0 +1,4 @@
fun unaryClient() {
val u1 = UnaryNot()
val u2 = !u1
}
@@ -0,0 +1,7 @@
Checked type of u1
Checked type of u2
Resolved !u1
Searched references to UnaryNot
Searched references to UnaryNot.not() in non-Java files
Searched references to u1 in non-Java files
Searched references to u2 in non-Java files
@@ -0,0 +1 @@
Function call 3 val u2 = !u1
@@ -1656,6 +1656,12 @@ public class FindUsagesTestGenerated extends AbstractFindUsagesTest {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/java/findJavaMethodUsages/SyntheticProperties.0.java");
doTest(fileName);
}
@TestMetadata("UnaryNot.0.java")
public void testUnaryNot() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/java/findJavaMethodUsages/UnaryNot.0.java");
doTest(fileName);
}
}
@TestMetadata("idea/testData/findUsages/java/findJavaPropertyUsages")