diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/destructuringDeclarationUsages.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/destructuringDeclarationUsages.kt index ae1bfe11a2d..9bd1bd0bf38 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/destructuringDeclarationUsages.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/destructuringDeclarationUsages.kt @@ -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) } } diff --git a/idea/testData/findUsages/kotlin/conventions/components/operators.0.kt b/idea/testData/findUsages/kotlin/conventions/components/operators.0.kt new file mode 100644 index 00000000000..e891ee61110 --- /dev/null +++ b/idea/testData/findUsages/kotlin/conventions/components/operators.0.kt @@ -0,0 +1,16 @@ +// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtParameter +// OPTIONS: usages + +data class A(val 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 +} diff --git a/idea/testData/findUsages/kotlin/conventions/components/operators.log b/idea/testData/findUsages/kotlin/conventions/components/operators.log new file mode 100644 index 00000000000..314d0cecc3a --- /dev/null +++ b/idea/testData/findUsages/kotlin/conventions/components/operators.log @@ -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 diff --git a/idea/testData/findUsages/kotlin/conventions/components/operators.results.txt b/idea/testData/findUsages/kotlin/conventions/components/operators.results.txt new file mode 100644 index 00000000000..2bd789b1d65 --- /dev/null +++ b/idea/testData/findUsages/kotlin/conventions/components/operators.results.txt @@ -0,0 +1,2 @@ +Value read 15 val (x, y) = b1 + b2 +Value read 6 val (x, y) = this \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java b/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java index e86beb65aeb..7f5d6f680a2 100644 --- a/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java @@ -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");