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:
+13
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.references
|
|||||||
|
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiMethod
|
||||||
import com.intellij.util.SmartList
|
import com.intellij.util.SmartList
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
@@ -47,6 +48,18 @@ sealed class SyntheticPropertyAccessorReference(expression: KtNameReferenceExpre
|
|||||||
return result
|
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 getRangeInElement() = TextRange(0, expression.textLength)
|
||||||
|
|
||||||
override fun canRename() = true
|
override fun canRename() = true
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ fun PsiReference.matchesTarget(candidateTarget: PsiElement): Boolean {
|
|||||||
is KtDestructuringDeclarationReference -> {
|
is KtDestructuringDeclarationReference -> {
|
||||||
if (candidateTarget !is KtNamedFunction && candidateTarget !is KtParameter && candidateTarget !is PsiMethod) return false
|
if (candidateTarget !is KtNamedFunction && candidateTarget !is KtParameter && candidateTarget !is PsiMethod) return false
|
||||||
}
|
}
|
||||||
|
is KtSimpleNameReference -> {
|
||||||
|
if (unwrappedCandidate is PsiMethod && !canBePsiMethodReference()) return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val targets = unwrappedTargets
|
val targets = unwrappedTargets
|
||||||
@@ -124,6 +127,24 @@ fun PsiReference.matchesTarget(candidateTarget: PsiElement): Boolean {
|
|||||||
return false
|
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) =
|
private fun PsiElement.isConstructorOf(unwrappedCandidate: PsiElement) =
|
||||||
// call to Java constructor
|
// call to Java constructor
|
||||||
(this is PsiMethod && isConstructor && containingClass == unwrappedCandidate) ||
|
(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");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/java/findJavaMethodUsages/SyntheticProperties.0.java");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("idea/testData/findUsages/java/findJavaPropertyUsages")
|
||||||
|
|||||||
Reference in New Issue
Block a user