diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/ExpressionsOfTypeProcessor.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/ExpressionsOfTypeProcessor.kt index 54d5437ea45..fb23407d988 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/ExpressionsOfTypeProcessor.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/ExpressionsOfTypeProcessor.kt @@ -21,13 +21,14 @@ import com.intellij.ide.highlighter.XmlFileType import com.intellij.lang.java.JavaLanguage import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project import com.intellij.openapi.util.TextRange import com.intellij.psi.* -import com.intellij.psi.search.FileTypeIndex -import com.intellij.psi.search.GlobalSearchScope -import com.intellij.psi.search.LocalSearchScope -import com.intellij.psi.search.SearchScope +import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference +import com.intellij.psi.search.* +import com.intellij.psi.search.searches.ClassInheritorsSearch import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.util.Processor import org.jetbrains.annotations.TestOnly @@ -271,10 +272,108 @@ class ExpressionsOfTypeProcessor( }) } - private fun addCallableDeclarationToProcess(declaration: PsiElement, scope: SearchScope, processor: ReferenceProcessor) { - data class ProcessCallableUsagesTask(val declaration: PsiElement, val processor: ReferenceProcessor) : Task { + private class StaticMemberRequestResultProcessor(val psiMember: PsiMember) : RequestResultProcessor(psiMember) { + override fun processTextOccurrence(element: PsiElement, offsetInElement: Int, consumer: Processor): Boolean { + if (element !is KtQualifiedExpression) return true + + val selectorExpression = element.selectorExpression ?: return true + val selectorReference = element.findReferenceAt(selectorExpression.startOffsetInParent) + + val references = when (selectorReference) { + is PsiMultiReference -> selectorReference.references.toList() + else -> listOf(selectorReference) + }.filterNotNull() + + for (ref in references) { + ProgressManager.checkCanceled() + + if (ref.isReferenceTo(psiMember)) { + consumer.process(ref) + } + } + + return true + } + } + + private fun classUseScope(psiClass: PsiClass) = runReadAction { + if (!psiClass.isValid) { + throw ProcessCanceledException() + } + + val file = psiClass.containingFile + (file ?: psiClass).useScope + } + + private fun addStaticMemberToProcess(psiMember: PsiMember, scope: SearchScope, processor: ReferenceProcessor) { + val declarationClass = runReadAction { psiMember.containingClass } ?: return + val declarationName = runReadAction { psiMember.name } ?: return + if (declarationName.isEmpty()) return + + data class ProcessStaticCallableUsagesTask(val member: PsiMember, val memberScope: SearchScope, val taskProcessor: ReferenceProcessor) : Task { override fun perform() { - testLog?.add("Searched references to ${logPresentation(declaration)} in non-Java files") + // This class will look through the whole hierarchy anyway, so shouldn't be a big overhead here + val inheritanceClasses = ClassInheritorsSearch.search( + declarationClass, + classUseScope(declarationClass), + true, true, false).findAll() + + val classes = (inheritanceClasses + declarationClass).filter { + it !is KtLightClass + } + + val searchRequestCollector = SearchRequestCollector(SearchSession()) + val resultProcessor = StaticMemberRequestResultProcessor(member) + + for (klass in classes) { + val request = klass.name + "." + declarationName + + testLog?.add("Searched references to static member in non-Java files by request $request") + + searchRequestCollector.searchWord( + request, classUseScope(klass).intersectWith(memberScope), UsageSearchContext.IN_CODE, true, member, resultProcessor) + } + + PsiSearchHelper.SERVICE.getInstance(project).processRequests(searchRequestCollector) { reference -> + if (reference.element.parents.any { it is KtImportDirective }) { + // Found declaration in import - process all file with an ordinal reference search + val containingFile = reference.element.containingFile + addCallableDeclarationToProcess(member, LocalSearchScope(containingFile), taskProcessor) + + true + } + else { + val processed = taskProcessor.handler(this@ExpressionsOfTypeProcessor, reference) + if (!processed) { // we don't know how to handle this reference and down-shift to plain search + downShiftToPlainSearch(reference) + } + + true + } + } + } + } + + addTask(ProcessStaticCallableUsagesTask(psiMember, scope, processor)) + return + } + + private fun addCallableDeclarationToProcess(declaration: PsiElement, scope: SearchScope, processor: ReferenceProcessor) { + if (scope !is LocalSearchScope && declaration is PsiMember && (declaration.modifierList?.hasModifierProperty(PsiModifier.STATIC) ?: false)) { + addStaticMemberToProcess(declaration, scope, processor) + return + } + + @Suppress("NAME_SHADOWING") + data class ProcessCallableUsagesTask(val declaration: PsiElement, val processor: ReferenceProcessor, val scope: SearchScope) : Task { + override fun perform() { + if (scope is LocalSearchScope) { + testLog?.add("Searched imported static member $declaration in ${scope.scope.toList()}") + } + else { + testLog?.add("Searched references to ${logPresentation(declaration)} in non-Java files") + } + val searchParameters = KotlinReferencesSearchParameters( declaration, scope, kotlinOptions = KotlinReferencesSearchOptions(searchNamedArguments = false)) searchReferences(searchParameters) { reference -> @@ -286,7 +385,7 @@ class ExpressionsOfTypeProcessor( } } } - addTask(ProcessCallableUsagesTask(declaration, processor)) + addTask(ProcessCallableUsagesTask(declaration, processor, scope)) } private fun addPsiMemberTask(member: PsiMember) { @@ -542,7 +641,7 @@ class ExpressionsOfTypeProcessor( break@ParentsLoop } - //TODO: if Java parameter has Kotlin functional type then we should process method usages + //TODO: if Java parameter has Kotlin functional type then we should process method usages is PsiParameter -> { if (prev == parent.typeElement) { // usage in parameter type - check if the method is in SAM interface processParameterInSamClass(parent) diff --git a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.0.java b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.0.java index 22515e6364e..b0bdcdfe7f7 100644 --- a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.0.java +++ b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.0.java @@ -1,11 +1,23 @@ // PSI_ELEMENT: com.intellij.psi.PsiMethod // OPTIONS: usages -public class JavaClass { +public class JavaClassInvoke { public void invoke() { } - public static class OtherJavaClass extends JavaClass { + public static JavaClassInvoke INSTANCE = new JavaClassInvoke(); + + public static class Other extends JavaClassInvoke {} + public static class AnotherOther extends Other {} + + public static class JavaOther { + public void invoke() { + } + + public static JavaOther INSTANCE = new JavaOther(); + } + + public static class OtherJavaClass extends JavaClassInvoke { public static OtherJavaClass OJC = new OtherJavaClass(); } } \ No newline at end of file diff --git a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.0.kt b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.0.kt index b6970379402..0f6e09db07a 100644 --- a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.0.kt +++ b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.0.kt @@ -1,8 +1,14 @@ -fun f(c: JavaClass) { +fun f(c: JavaClassInvoke) { c() } -fun foo(o: JavaClass.OtherJavaClass) { +fun foo(o: JavaClassInvoke.OtherJavaClass) { o() - JavaClass.OtherJavaClass.OJC() -} \ No newline at end of file + JavaClassInvoke.OtherJavaClass.OJC() +} + +fun foo() { + JavaClassInvoke.INSTANCE() + JavaClassInvoke.AnotherOther.INSTANCE() + JavaClassInvoke.JavaOther.INSTANCE() +} diff --git a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.log b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.log index bcfeb6300b3..f1a1b67220c 100644 --- a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.log +++ b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.log @@ -1,8 +1,18 @@ +Resolved INSTANCE() +Resolved INSTANCE() +Resolved INSTANCE() +Resolved INSTANCE() Resolved OJC() Resolved c() Resolved o() -Searched references to JavaClass -Searched references to JavaClass.OtherJavaClass -Searched references to JavaClass.OtherJavaClass.OJC in non-Java files -Searched references to parameter c of f(c: JavaClass) in non-Java files -Searched references to parameter o of foo(o: JavaClass.OtherJavaClass) in non-Java files \ No newline at end of file +Searched references to JavaClassInvoke +Searched references to JavaClassInvoke.AnotherOther +Searched references to JavaClassInvoke.Other +Searched references to JavaClassInvoke.OtherJavaClass +Searched references to parameter c of f(c: JavaClassInvoke) in non-Java files +Searched references to parameter o of foo(o: JavaClassInvoke.OtherJavaClass) in non-Java files +Searched references to static member in non-Java files by request AnotherOther.INSTANCE +Searched references to static member in non-Java files by request JavaClassInvoke.INSTANCE +Searched references to static member in non-Java files by request Other.INSTANCE +Searched references to static member in non-Java files by request OtherJavaClass.INSTANCE +Searched references to static member in non-Java files by request OtherJavaClass.OJC \ No newline at end of file diff --git a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.results.txt b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.results.txt index 386c7872825..8980788c9a1 100644 --- a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.results.txt +++ b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvoke.results.txt @@ -1,3 +1,5 @@ +Implicit 'invoke' 11 JavaClassInvoke.INSTANCE() +Implicit 'invoke' 12 JavaClassInvoke.AnotherOther.INSTANCE() Implicit 'invoke' 2 c() Implicit 'invoke' 6 o() -Implicit 'invoke' 7 JavaClass.OtherJavaClass.OJC() \ No newline at end of file +Implicit 'invoke' 7 JavaClassInvoke.OtherJavaClass.OJC() \ No newline at end of file diff --git a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.0.java b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.0.java new file mode 100644 index 00000000000..d99cb4ec827 --- /dev/null +++ b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.0.java @@ -0,0 +1,11 @@ +// PSI_ELEMENT: com.intellij.psi.PsiMethod +// OPTIONS: usages + +public class JavaClassWI { + public void invoke() { + } + + public static JavaClassWI INSTANCE = new JavaClass(); + + public static class Other extends JavaClassWI {} +} \ No newline at end of file diff --git a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.0.kt b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.0.kt new file mode 100644 index 00000000000..4024c315c8e --- /dev/null +++ b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.0.kt @@ -0,0 +1,5 @@ +import JavaClassWI.Other.INSTANCE + +fun foo() { + INSTANCE() +} diff --git a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.log b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.log new file mode 100644 index 00000000000..ef5f3ce40b5 --- /dev/null +++ b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.log @@ -0,0 +1,6 @@ +Resolved INSTANCE() +Searched imported static member PsiField:INSTANCE in [JetFile: javaInvokeWithStaticImport.0.kt] +Searched references to JavaClassWI +Searched references to JavaClassWI.Other +Searched references to static member in non-Java files by request JavaClassWI.INSTANCE +Searched references to static member in non-Java files by request Other.INSTANCE \ No newline at end of file diff --git a/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.results.txt b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.results.txt new file mode 100644 index 00000000000..0f28189c1d2 --- /dev/null +++ b/idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.results.txt @@ -0,0 +1 @@ +Implicit 'invoke' 4 INSTANCE() \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/conventions/components/dataClass.log b/idea/testData/findUsages/kotlin/conventions/components/dataClass.log index 55d7938758f..a18030600cb 100644 --- a/idea/testData/findUsages/kotlin/conventions/components/dataClass.log +++ b/idea/testData/findUsages/kotlin/conventions/components/dataClass.log @@ -41,7 +41,6 @@ Searched references to JavaClass2 Searched references to JavaClass3 Searched references to JavaClass4.NestedPrivate Searched references to JavaClass4.NestedPublic -Searched references to JavaClass4.getNested() in non-Java files Searched references to a in non-Java files Searched references to f() in non-Java files Searched references to g() in non-Java files @@ -51,6 +50,7 @@ Searched references to pack.A Searched references to pack.X Searched references to parameter p1 of test2(p1: JavaClass2, p2: JavaClass3) in non-Java files Searched references to parameter p2 of test2(p1: JavaClass2, p2: JavaClass3) in non-Java files +Searched references to static member in non-Java files by request JavaClass4.getNested Used plain search of parameter n of A(val n: Int, val s: String, val o: Any) in LocalSearchScope: CLASS:A CLASS:X diff --git a/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java b/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java index c1ecaf25276..a1b923bc32e 100644 --- a/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesTestGenerated.java @@ -1639,6 +1639,12 @@ public class FindUsagesTestGenerated extends AbstractFindUsagesTest { doTest(fileName); } + @TestMetadata("javaInvokeWithStaticImport.0.java") + public void testJavaInvokeWithStaticImport() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/java/findJavaMethodUsages/javaInvokeWithStaticImport.0.java"); + doTest(fileName); + } + @TestMetadata("JavaWithGroovyInvoke.0.java") public void testJavaWithGroovyInvoke() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/java/findJavaMethodUsages/JavaWithGroovyInvoke.0.java");