diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt index fec47a62023..1d0fe32c902 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt @@ -24,6 +24,7 @@ import com.intellij.psi.search.* import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.util.Processor import org.jetbrains.kotlin.asJava.* +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.JetFileType import org.jetbrains.kotlin.idea.references.JetSimpleNameReference import org.jetbrains.kotlin.idea.search.KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT @@ -47,7 +48,7 @@ data class KotlinReferencesSearchOptions(val acceptCallableOverrides: Boolean = } public class KotlinReferencesSearchParameters(elementToSearch: PsiElement, - scope: SearchScope = elementToSearch.project.allScope(), + scope: SearchScope = runReadAction { elementToSearch.project.allScope() }, ignoreAccessScope: Boolean = false, optimizer: SearchRequestCollector? = null, val kotlinOptions: KotlinReferencesSearchOptions = KotlinReferencesSearchOptions.Empty) @@ -61,8 +62,8 @@ public class KotlinReferencesSearcher : QueryExecutorBase() - val originLightClass = LightClassUtil.getPsiClass(originClass) - if (originLightClass != null) { - val lightDeclarations: List?> = - originLightClass.methods.map { it as? KotlinLightMethod } + - originLightClass.fields.map { it as? KotlinLightFieldForDeclaration } + runReadAction { + val originClass = element.getStrictParentOfType() + val originLightClass = LightClassUtil.getPsiClass(originClass) + if (originLightClass != null) { + val lightDeclarations: List?> = + originLightClass.methods.map { it as? KotlinLightMethod } + + originLightClass.fields.map { it as? KotlinLightFieldForDeclaration } - for (declaration in element.declarations) { - val lightDeclaration = lightDeclarations.find { it?.getOrigin() == declaration } - if (lightDeclaration != null) { - searchNamedElement(queryParameters, lightDeclaration) + for (declaration in element.declarations) { + val lightDeclaration = lightDeclarations.find { it?.getOrigin() == declaration } + if (lightDeclaration != null) { + searchNamedElement(queryParameters, lightDeclaration) + } } } } @@ -224,6 +227,17 @@ public class KotlinReferencesSearcher : QueryExecutorBase processJetClassOrObject(element, queryParameters) @@ -235,7 +249,7 @@ public class KotlinReferencesSearcher : QueryExecutorBase { searchPropertyMethods(queryParameters, element) + runReadAction { + val componentFunctionDescriptor = element.dataClassComponentFunction() + if (componentFunctionDescriptor != null) { + val containingClass = LightClassUtil.getPsiClass(element.getStrictParentOfType()) + searchDataClassComponentUsages(queryParameters, containingClass, componentFunctionDescriptor) + } + } } is KotlinLightMethod -> { @@ -262,7 +284,7 @@ public class KotlinReferencesSearcher : QueryExecutorBase { val origin = element.getOrigin() ?: return - val componentFunctionDescriptor = origin.dataClassComponentFunction() - if (componentFunctionDescriptor != null) { - val containingClass = element.method.containingClass - val componentFunction = containingClass?.methods?.find { - it.name == componentFunctionDescriptor.name.asString() && it.parameterList.parametersCount == 0 - } - if (componentFunction != null) { - searchNamedElement(queryParameters, componentFunction) + runReadAction { + val componentFunctionDescriptor = origin.dataClassComponentFunction() + if (componentFunctionDescriptor != null) { + searchDataClassComponentUsages(queryParameters, element.method.containingClass, componentFunctionDescriptor) } } - searchPropertyMethods(queryParameters, origin) } } } private fun isOnlyKotlinSearch(searchScope: SearchScope) = - searchScope is LocalSearchScope && searchScope.getScope().all { it.getContainingFile().getFileType() == JetFileType.INSTANCE } + searchScope is LocalSearchScope && runReadAction { + searchScope.getScope().all { it.getContainingFile().getFileType() == JetFileType.INSTANCE } + } private fun searchNamedElement(queryParameters: ReferencesSearch.SearchParameters, element: PsiNamedElement?, diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt index 6544c80caf8..e5a28fa08e8 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt @@ -106,34 +106,34 @@ private fun JetElement.getConstructorCallDescriptor(): DeclarationDescriptor? { return null } -public fun PsiElement.processDelegationCallConstructorUsages(scope: SearchScope, process: (JetCallElement) -> Unit) { - processDelegationCallKotlinConstructorUsages(scope, process) - processDelegationCallJavaConstructorUsages(scope, process) +public fun PsiElement.processDelegationCallConstructorUsages(scope: SearchScope, process: (JetCallElement) -> Boolean): Boolean { + if (!processDelegationCallKotlinConstructorUsages(scope, process)) return false + return processDelegationCallJavaConstructorUsages(scope, process) } -private fun PsiElement.processDelegationCallKotlinConstructorUsages(scope: SearchScope, process: (JetCallElement) -> Unit) { +private fun PsiElement.processDelegationCallKotlinConstructorUsages(scope: SearchScope, process: (JetCallElement) -> Boolean): Boolean { val element = unwrapped val klass = when (element) { is JetConstructor<*> -> element.getContainingClassOrObject() is JetClass -> element - else -> return + else -> return true } - if (klass !is JetClass || element !is JetDeclaration) return - val descriptor = element.constructor ?: return + if (klass !is JetClass || element !is JetDeclaration) return true + val descriptor = element.constructor ?: return true - processClassDelegationCallsToSpecifiedConstructor(klass, descriptor, process) - processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process) + if (!processClassDelegationCallsToSpecifiedConstructor(klass, descriptor, process)) return false + return processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process) } -private fun PsiElement.processDelegationCallJavaConstructorUsages(scope: SearchScope, process: (JetCallElement) -> Unit) { - if (this is KotlinLightElement<*, *>) return +private fun PsiElement.processDelegationCallJavaConstructorUsages(scope: SearchScope, process: (JetCallElement) -> Boolean): Boolean { + if (this is KotlinLightElement<*, *>) return true // TODO: Temporary hack to avoid NPE while KotlinNoOriginLightMethod is around - if (this is KotlinNoOriginLightMethod) return - if (!(this is PsiMethod && isConstructor())) return - val klass = getContainingClass() ?: return - val descriptor = getJavaMethodDescriptor() as? ConstructorDescriptor ?: return - processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process) + if (this is KotlinNoOriginLightMethod) return true + if (!(this is PsiMethod && isConstructor())) return true + val klass = getContainingClass() ?: return true + val descriptor = getJavaMethodDescriptor() as? ConstructorDescriptor ?: return true + return processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process) } @@ -141,34 +141,36 @@ private fun processInheritorsDelegatingCallToSpecifiedConstructor( klass: PsiElement, scope: SearchScope, descriptor: ConstructorDescriptor, - process: (JetCallElement) -> Unit -) { - HierarchySearchRequest(klass, scope, false).searchInheritors().forEach() { + process: (JetCallElement) -> Boolean +): Boolean { + return HierarchySearchRequest(klass, scope, false).searchInheritors().all { val unwrapped = it.unwrapped if (unwrapped is JetClass) { processClassDelegationCallsToSpecifiedConstructor(unwrapped, descriptor, process) - } + } else + true } } private fun processClassDelegationCallsToSpecifiedConstructor( - klass: JetClass, constructor: DeclarationDescriptor, process: (JetCallElement) -> Unit -) { + klass: JetClass, constructor: DeclarationDescriptor, process: (JetCallElement) -> Boolean +): Boolean { for (secondaryConstructor in klass.getSecondaryConstructors()) { val delegationCallDescriptor = secondaryConstructor.getDelegationCall().getConstructorCallDescriptor() if (constructor == delegationCallDescriptor) { - process(secondaryConstructor.getDelegationCall()) + if (!process(secondaryConstructor.getDelegationCall())) return false } } - if (!klass.isEnum()) return + if (!klass.isEnum()) return true for (declaration in klass.declarations) { if (declaration is JetEnumEntry) { val delegationCall = declaration.getDelegationSpecifiers().firstOrNull() if (delegationCall is JetDelegatorToSuperCall && constructor == delegationCall.calleeExpression.getConstructorCallDescriptor()) { - process(delegationCall) + if (!process(delegationCall)) return false } } } + return true } // Check if reference resolves to extension function whose receiver is the same as declaration's parent (or its superclass) diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindClassUsagesHandler.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindClassUsagesHandler.kt index 44a7f6b6380..739615b5f83 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindClassUsagesHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindClassUsagesHandler.kt @@ -16,10 +16,7 @@ package org.jetbrains.kotlin.idea.findUsages.handlers -import com.intellij.find.findUsages.AbstractFindUsagesDialog -import com.intellij.find.findUsages.FindUsagesOptions -import com.intellij.find.findUsages.JavaFindUsagesHandler -import com.intellij.find.findUsages.JavaFindUsagesHandlerFactory +import com.intellij.find.findUsages.* import com.intellij.openapi.actionSystem.DataContext import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement @@ -47,6 +44,7 @@ import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor import org.jetbrains.kotlin.idea.search.usagesSearch.isConstructorUsage import org.jetbrains.kotlin.idea.search.usagesSearch.isImportUsage import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages +import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.effectiveDeclarations import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType @@ -110,13 +108,15 @@ public class KotlinFindClassUsagesHandler( } if (kotlinOptions.searchConstructorUsages) { - val constructors = classOrObject.toLightClass()?.getConstructors() ?: PsiMethod.EMPTY_ARRAY - for (constructor in constructors) { - if (constructor !is KotlinLightMethod) continue - constructor.processDelegationCallConstructorUsages(constructor.getUseScope()) { - it.getCalleeExpression()?.mainReference?.let { referenceProcessor.process(it) } + val result = runReadAction { + val constructors = classOrObject.toLightClass()?.getConstructors() ?: PsiMethod.EMPTY_ARRAY + constructors.filterIsInstance().all { constructor -> + constructor.processDelegationCallConstructorUsages(constructor.getUseScope()) { + it.getCalleeExpression()?.mainReference?.let { referenceProcessor.process(it) } ?: false + } } } + if (!result) return false } if (options.isDerivedClasses || options.isDerivedInterfaces) { @@ -148,27 +148,29 @@ public class KotlinFindClassUsagesHandler( private fun processCompanionObjectInternalReferences(companionObject: JetObjectDeclaration, processor: Processor): Boolean { - val klass = companionObject.getStrictParentOfType() ?: return true - val companionObjectDescriptor = companionObject.descriptor var stop: Boolean = false - klass.acceptChildren(object : JetVisitorVoid() { - override fun visitJetElement(element: JetElement) { - if (element == companionObject) return // skip companion object itself - if (stop) return - element.acceptChildren(this) + runReadAction { + val klass = companionObject.getStrictParentOfType() ?: return@runReadAction + val companionObjectDescriptor = companionObject.descriptor + klass.acceptChildren(object : JetVisitorVoid() { + override fun visitJetElement(element: JetElement) { + if (element == companionObject) return // skip companion object itself + if (stop) return + element.acceptChildren(this) - val bindingContext = element.analyze() - val resolvedCall = bindingContext[BindingContext.CALL, element]?.getResolvedCall(bindingContext) ?: return - if ((resolvedCall.getDispatchReceiver() as? ClassReceiver)?.getDeclarationDescriptor() == companionObjectDescriptor - || (resolvedCall.getExtensionReceiver() as? ClassReceiver)?.getDeclarationDescriptor() == companionObjectDescriptor) { - element.getReferences().forEach { - if (!stop && !processor.process(it)) { - stop = true + val bindingContext = element.analyze() + val resolvedCall = bindingContext[BindingContext.CALL, element]?.getResolvedCall(bindingContext) ?: return + if ((resolvedCall.getDispatchReceiver() as? ClassReceiver)?.getDeclarationDescriptor() == companionObjectDescriptor + || (resolvedCall.getExtensionReceiver() as? ClassReceiver)?.getDeclarationDescriptor() == companionObjectDescriptor) { + element.getReferences().forEach { + if (!stop && !processor.process(it)) { + stop = true + } } } } - } - }) + }) + } return !stop } @@ -191,15 +193,7 @@ public class KotlinFindClassUsagesHandler( else -> null } ?: return Collections.emptyList() - // Work around the protected method in JavaFindUsagesHandler - // todo: Use JavaFindUsagesHelper.getElementNames() when it becomes public in IDEA - var stringsToSearch: Collection - object: JavaFindUsagesHandler(psiClass, JavaFindUsagesHandlerFactory.getInstance(element.getProject())) { - init { - stringsToSearch = getStringsToSearch(psiClass)!! - } - } - return stringsToSearch + return JavaFindUsagesHelper.getElementNames(psiClass) } protected override fun isSearchForTextOccurencesAvailable(psiElement: PsiElement, isSingleFile: Boolean): Boolean { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java index 9501ec169c8..6c4481e16c2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java @@ -39,7 +39,6 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import com.intellij.util.containers.MultiMap; import kotlin.KotlinPackage; -import kotlin.Unit; import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -330,9 +329,9 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro UsagesSearchPackage.processDelegationCallConstructorUsages( functionPsi, functionPsi.getUseScope(), - new Function1() { + new Function1() { @Override - public Unit invoke(JetCallElement element) { + public Boolean invoke(JetCallElement element) { if (element instanceof JetConstructorDelegationCall) { result.add(new JetConstructorDelegationCallUsage((JetConstructorDelegationCall) element, changeInfo)); } @@ -340,7 +339,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro result.add(new JetFunctionCallUsage(element, functionUsageInfo)); } - return null; + return true; } } ); @@ -481,13 +480,13 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro UsagesSearchPackage.processDelegationCallConstructorUsages( psiMethod, psiMethod.getUseScope(), - new Function1() { + new Function1() { @Override - public Unit invoke(JetCallElement element) { + public Boolean invoke(JetCallElement element) { if (element instanceof JetConstructorDelegationCall) { result.add(new JavaConstructorDeferredUsageInDelegationCall((JetConstructorDelegationCall) element)); } - return null; + return true; } } ); diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConstructorDelegationCallReferenceSearcher.kt b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConstructorDelegationCallReferenceSearcher.kt index 81119a2ed45..775a671aaaf 100644 --- a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConstructorDelegationCallReferenceSearcher.kt +++ b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConstructorDelegationCallReferenceSearcher.kt @@ -28,7 +28,7 @@ public class KotlinConstructorDelegationCallReferenceSearcher() : QueryExecutorB if (!method.isConstructor()) return method.processDelegationCallConstructorUsages(method.getUseScope()) { - it.getCalleeExpression()?.getReference()?.let { consumer.process(it) } + it.getCalleeExpression()?.getReference()?.let { consumer.process(it) } ?: true } } } \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/findParameterUsages/kotlinComponentFunctionParameterUsages.0.kt b/idea/testData/findUsages/kotlin/findParameterUsages/kotlinComponentFunctionParameterUsages.0.kt new file mode 100644 index 00000000000..599a94134d7 --- /dev/null +++ b/idea/testData/findUsages/kotlin/findParameterUsages/kotlinComponentFunctionParameterUsages.0.kt @@ -0,0 +1,6 @@ +// PSI_ELEMENT: org.jetbrains.kotlin.psi.JetParameter +// OPTIONS: usages +package test + +public data class KotlinDataClass(val foo: Int, val bar: String) { +} \ No newline at end of file diff --git a/idea/testData/findUsages/kotlin/findParameterUsages/kotlinComponentFunctionParameterUsages.1.java b/idea/testData/findUsages/kotlin/findParameterUsages/kotlinComponentFunctionParameterUsages.1.java new file mode 100644 index 00000000000..dc283857368 --- /dev/null +++ b/idea/testData/findUsages/kotlin/findParameterUsages/kotlinComponentFunctionParameterUsages.1.java @@ -0,0 +1,7 @@ +package test; + +public class JavaUser { + int use(KotlinDataClass dataClass) { + return dataClass.component1(); + } +} diff --git a/idea/testData/findUsages/kotlin/findParameterUsages/kotlinComponentFunctionParameterUsages.results.txt b/idea/testData/findUsages/kotlin/findParameterUsages/kotlinComponentFunctionParameterUsages.results.txt new file mode 100644 index 00000000000..3a2c991b116 --- /dev/null +++ b/idea/testData/findUsages/kotlin/findParameterUsages/kotlinComponentFunctionParameterUsages.results.txt @@ -0,0 +1 @@ +Unclassified usage (5: 26) return dataClass.component1(); diff --git a/idea/tests/org/jetbrains/kotlin/findUsages/JetFindUsagesTestGenerated.java b/idea/tests/org/jetbrains/kotlin/findUsages/JetFindUsagesTestGenerated.java index ef9421e8aaa..ae1f6378238 100644 --- a/idea/tests/org/jetbrains/kotlin/findUsages/JetFindUsagesTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/findUsages/JetFindUsagesTestGenerated.java @@ -735,6 +735,12 @@ public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/findUsages/kotlin/findParameterUsages"), Pattern.compile("^(.+)\\.0\\.kt$"), true); } + @TestMetadata("kotlinComponentFunctionParameterUsages.0.kt") + public void testKotlinComponentFunctionParameterUsages() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/findParameterUsages/kotlinComponentFunctionParameterUsages.0.kt"); + doTest(fileName); + } + @TestMetadata("kotlinConstructorParameterUsages.0.kt") public void testKotlinConstructorParameterUsages() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/findParameterUsages/kotlinConstructorParameterUsages.0.kt");