Down-shifting to plain search when operator usage search required

This commit is contained in:
Valentin Kipyatkov
2016-08-29 20:02:19 +03:00
parent 5682c917f9
commit 9cd7e9d519
5 changed files with 58 additions and 2 deletions
@@ -48,6 +48,7 @@ import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
import org.jetbrains.kotlin.idea.util.toFuzzyType
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
@@ -59,7 +60,9 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import java.util.*
enum class DestructuringDeclarationUsageSearch {
ALWAYS_SMART, ALWAYS_PLAIN, PLAIN_WHEN_NEEDED
ALWAYS_SMART,
ALWAYS_PLAIN,
PLAIN_WHEN_NEEDED // use plain search for LocalSearchScope and when unknown type of reference encountered
}
// for tests
@@ -223,12 +226,18 @@ private class Processor(
PROCESS_LAMBDAS
}
//TODO: check if it's operator (too expensive)
/**
* Adds declaration whose type is our data class (or data class used anywhere inside that type)
* or which has parameter of functional type with our data class used inside
*/
private fun addCallableDeclarationToProcess(declaration: PsiElement, kind: CallableToProcessKind) {
if (declaration.isOperatorExpensiveToSearch()) { // cancel all tasks and use plain search
tasks.clear()
scopesToUsePlainSearch.clear()
plainSearchHandler(searchScope)
return
}
data class ProcessCallableUsagesTask(val declaration: PsiElement, val kind: CallableToProcessKind) : Task {
override fun perform() {
// we don't need to search usages of declarations in Java because Java doesn't have implicitly typed declarations so such usages cannot affect Kotlin code
@@ -621,6 +630,24 @@ private class Processor(
return hasModifierProperty(PsiModifier.PRIVATE) || parents.any { it is PsiCodeBlock }
}
private fun PsiElement.isOperatorExpensiveToSearch(): Boolean {
when (this) {
is KtFunction -> {
if (name?.startsWith("component") == true) return false // component functions are not so expensive to search
return hasModifier(KtTokens.OPERATOR_KEYWORD)
|| hasModifier(KtTokens.OVERRIDE_KEYWORD) && (resolveToDescriptorIfAny() as? FunctionDescriptor)?.isOperator == true
}
is KtLightMethod -> {
return kotlinOrigin?.isOperatorExpensiveToSearch() == true
}
else -> {
return false
}
}
}
private fun KotlinType.containsTypeOrDerivedInside(type: FuzzyType): Boolean {
return type.checkIsSuperTypeOf(this) != null || arguments.any { it.type.containsTypeOrDerivedInside(type) }
}
@@ -0,0 +1,16 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtParameter
// OPTIONS: usages
data class A(val <caret>x: Int, val y: Int) {
fun f() {
val (x, y) = this
}
}
class B
operator fun B.plus(other: B): A = TODO()
fun f(b1: B, b2: B) {
val (x, y) = b1 + b2
}
@@ -0,0 +1,5 @@
Searched inheritors of A
Searched references to A
Used plain search in LocalSearchScope:
CLASS:A
Used plain search in whole search scope
@@ -0,0 +1,2 @@
Value read 15 val (x, y) = b1 + b2
Value read 6 val (x, y) = this
@@ -254,6 +254,12 @@ public class FindUsagesTestGenerated extends AbstractFindUsagesTest {
doTest(fileName);
}
@TestMetadata("operators.0.kt")
public void testOperators() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/components/operators.0.kt");
doTest(fileName);
}
@TestMetadata("recursiveDataClass1.0.kt")
public void testRecursiveDataClass1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass1.0.kt");