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 8ca59919b73..ae1bfe11a2d 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 @@ -66,6 +66,10 @@ enum class DestructuringDeclarationUsageSearch { var destructuringDeclarationUsageSearchMode = if (ApplicationManager.getApplication().isUnitTestMode) ALWAYS_SMART else PLAIN_WHEN_NEEDED var destructuringDeclarationUsageSearchLog: MutableList? = null +private object DestructuringDeclarationSearchesInProgress : ThreadLocal>() { + override fun initialValue() = HashSet() +} + //TODO: check if it's too expensive fun findDestructuringDeclarationUsages( @@ -85,35 +89,43 @@ fun findDestructuringDeclarationUsages( consumer: Processor, optimizer: SearchRequestCollector ) { - val usePlainSearch = when (destructuringDeclarationUsageSearchMode) { - ALWAYS_SMART -> false - ALWAYS_PLAIN -> true - PLAIN_WHEN_NEEDED -> scope is LocalSearchScope // for local scope it's faster to use plain search - } - if (usePlainSearch) { - doPlainSearch(ktDeclaration, scope, optimizer) - return - } + val inProgress = DestructuringDeclarationSearchesInProgress.get() + try { + if (!inProgress.add(ktDeclaration)) return - val descriptor = ktDeclaration.resolveToDescriptor() as? CallableDescriptor ?: return + val usePlainSearch = when (destructuringDeclarationUsageSearchMode) { + ALWAYS_SMART -> false + ALWAYS_PLAIN -> true + PLAIN_WHEN_NEEDED -> scope is LocalSearchScope // for local scope it's faster to use plain search + } + if (usePlainSearch) { + doPlainSearch(ktDeclaration, scope, optimizer) + return + } - if (descriptor is FunctionDescriptor && !descriptor.isValidOperator()) return + val descriptor = ktDeclaration.resolveToDescriptor() as? CallableDescriptor ?: return - val dataType = if (descriptor.isExtension) { - descriptor.fuzzyExtensionReceiverType()!! + if (descriptor is FunctionDescriptor && !descriptor.isValidOperator()) return + + val dataType = if (descriptor.isExtension) { + descriptor.fuzzyExtensionReceiverType()!! + } + else { + val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return + classDescriptor.defaultType.toFuzzyType(classDescriptor.typeConstructor.parameters) + } + + Processor(dataType, + ktDeclaration, + scope, + consumer, + plainSearchHandler = { searchScope -> doPlainSearch(ktDeclaration, searchScope, optimizer) }, + testLog = destructuringDeclarationUsageSearchLog + ).run() } - else { - val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return - classDescriptor.defaultType.toFuzzyType(classDescriptor.typeConstructor.parameters) + finally { + inProgress.remove(ktDeclaration) } - - Processor(dataType, - ktDeclaration, - scope, - consumer, - plainSearchHandler = { searchScope -> doPlainSearch(ktDeclaration, searchScope, optimizer) }, - testLog = destructuringDeclarationUsageSearchLog - ).run() } private fun doPlainSearch(ktDeclaration: KtDeclaration, scope: SearchScope, optimizer: SearchRequestCollector) { diff --git a/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass1.0.kt b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass1.0.kt new file mode 100644 index 00000000000..8dafb5970e7 --- /dev/null +++ b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass1.0.kt @@ -0,0 +1,10 @@ +// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtParameter +// OPTIONS: usages + +data class A(val a: A?, val n: Int) + +fun f(a: A) { + val (a1, n1) = a + val (a2, n2) = + a?.a ?: return +} \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass1.log b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass1.log new file mode 100644 index 00000000000..901a8632f1c --- /dev/null +++ b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass1.log @@ -0,0 +1,9 @@ +Checked type of (a1, n1) +Checked type of (a2, n2) +Checked type of (a2, n2) +Searched inheritors of A +Searched references to A +Searched references to parameter a in A() in Kotlin files +Searched references to parameter a in f() in Kotlin files +Used plain search in LocalSearchScope: + CLASS:A diff --git a/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass1.results.txt b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass1.results.txt new file mode 100644 index 00000000000..112e8c1e732 --- /dev/null +++ b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass1.results.txt @@ -0,0 +1,3 @@ +Value read 7 val (a1, n1) = a +Value read 8 val (a2, n2) = +Value read 9 a?.a ?: return \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass2.0.kt b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass2.0.kt new file mode 100644 index 00000000000..3c88c168545 --- /dev/null +++ b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass2.0.kt @@ -0,0 +1,10 @@ +// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtParameter +// OPTIONS: usages + +data class A(val b: B, val n: Int) +data class B(val a: A?, val s: String) + +fun f(a: A) { + val (b, n) = a + val (a1, s) = b +} \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass2.log b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass2.log new file mode 100644 index 00000000000..da05969f203 --- /dev/null +++ b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass2.log @@ -0,0 +1,15 @@ +Checked type of (a1, s) +Checked type of (b, n) +Searched inheritors of A +Searched inheritors of B +Searched references to A +Searched references to B +Searched references to a1 in Kotlin files +Searched references to b in Kotlin files +Searched references to parameter a in B() in Kotlin files +Searched references to parameter a in f() in Kotlin files +Searched references to parameter b in A() in Kotlin files +Used plain search in LocalSearchScope: + CLASS:A +Used plain search in LocalSearchScope: + CLASS:B diff --git a/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass2.results.txt b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass2.results.txt new file mode 100644 index 00000000000..4d72f220479 --- /dev/null +++ b/idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass2.results.txt @@ -0,0 +1 @@ +Value read 8 val (b, n) = a diff --git a/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java b/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java index 7146f20c580..e86beb65aeb 100644 --- a/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java @@ -254,6 +254,18 @@ public class FindUsagesTestGenerated extends AbstractFindUsagesTest { doTest(fileName); } + @TestMetadata("recursiveDataClass1.0.kt") + public void testRecursiveDataClass1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass1.0.kt"); + doTest(fileName); + } + + @TestMetadata("recursiveDataClass2.0.kt") + public void testRecursiveDataClass2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/components/recursiveDataClass2.0.kt"); + doTest(fileName); + } + @TestMetadata("SAM.0.kt") public void testSAM() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/components/SAM.0.kt");