From be4fcd626c037d147db4a47ae874649a7d2b31f5 Mon Sep 17 00:00:00 2001 From: Igor Yakovlev Date: Tue, 15 Sep 2020 15:19:48 +0300 Subject: [PATCH] [FIR IDE] Separate searchers from descriptor usage KotlinConstructorDelegationCallReferenceSearcher KotlinConventionMethodReferencesSearcher KotlinDefinitionsSearcher KotlinOverridingMethodReferenceSearcher KotlinPropertyAccessorsReferenceSearcher KotlinTodoSearcher --- .../KotlinSearchUsagesSupportFirImpl.kt | 28 ++++ .../kotlin/idea/search/KotlinSearchSupport.kt | 25 ++++ ...structorDelegationCallReferenceSearcher.kt | 4 +- ...otlinConventionMethodReferencesSearcher.kt | 4 +- .../KotlinDefinitionsSearcher.kt | 14 +- ...KotlinOverridingMethodReferenceSearcher.kt | 6 +- ...otlinPropertyAccessorsReferenceSearcher.kt | 0 .../ideaExtensions/KotlinTodoSearcher.kt | 0 .../search/usagesSearch/utilsIndependent.kt | 126 ++++++++++++++++ idea/resources-fir/META-INF/plugin.xml | 15 +- .../search/KotlinSearchUsagesSupportImpl.kt | 14 ++ .../kotlin/idea/search/usagesSearch/utils.kt | 141 +++--------------- 12 files changed, 239 insertions(+), 138 deletions(-) rename idea/{ => idea-frontend-independent}/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConstructorDelegationCallReferenceSearcher.kt (90%) rename idea/{ => idea-frontend-independent}/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConventionMethodReferencesSearcher.kt (90%) rename idea/{ => idea-frontend-independent}/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinDefinitionsSearcher.kt (91%) rename idea/{ => idea-frontend-independent}/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinOverridingMethodReferenceSearcher.kt (96%) rename idea/{ => idea-frontend-independent}/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinPropertyAccessorsReferenceSearcher.kt (100%) rename idea/{ => idea-frontend-independent}/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTodoSearcher.kt (100%) create mode 100644 idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/usagesSearch/utilsIndependent.kt diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportFirImpl.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportFirImpl.kt index 87b65e7bb3b..75a45dfbcaa 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportFirImpl.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportFirImpl.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.idea.search +import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project import com.intellij.psi.* import com.intellij.psi.search.GlobalSearchScope @@ -14,6 +15,10 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.ImportPath class KotlinSearchUsagesSupportFirImpl : KotlinSearchUsagesSupport { + override fun actualsForExpected(declaration: KtDeclaration, module: Module?): Set { + return emptySet() + } + override fun dataClassComponentMethodName(element: KtParameter): String? { return null } @@ -109,4 +114,27 @@ class KotlinSearchUsagesSupportFirImpl : KotlinSearchUsagesSupport { override fun isExpectDeclaration(declaration: KtDeclaration): Boolean { return false } + + override fun canBeResolvedWithFrontEnd(element: PsiElement): Boolean { + //TODO FIR: Is the same as PsiElement.hasJavaResolutionFacade() as for FIR? + return element.originalElement.containingFile != null + } + + override fun createConstructorHandle(ktDeclaration: KtDeclaration): KotlinSearchUsagesSupport.ConstructorCallHandle { + //TODO FIR: This is the stub. Need to implement + return object : KotlinSearchUsagesSupport.ConstructorCallHandle { + override fun referencedTo(element: KtElement): Boolean { + return false + } + } + } + + override fun createConstructorHandle(psiMethod: PsiMethod): KotlinSearchUsagesSupport.ConstructorCallHandle { + //TODO FIR: This is the stub. Need to implement + return object : KotlinSearchUsagesSupport.ConstructorCallHandle { + override fun referencedTo(element: KtElement): Boolean { + return false + } + } + } } \ No newline at end of file diff --git a/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/KotlinSearchSupport.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/KotlinSearchSupport.kt index c4cc0a682e0..f35c2cbe395 100644 --- a/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/KotlinSearchSupport.kt +++ b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/KotlinSearchSupport.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.idea.search import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project import com.intellij.psi.* import com.intellij.psi.search.GlobalSearchScope @@ -17,6 +18,10 @@ import org.jetbrains.kotlin.resolve.ImportPath interface KotlinSearchUsagesSupport { + interface ConstructorCallHandle { + fun referencedTo(element: KtElement): Boolean + } + companion object { fun getInstance(project: Project): KotlinSearchUsagesSupport { return ServiceManager.getService(project, KotlinSearchUsagesSupport::class.java) @@ -95,8 +100,22 @@ interface KotlinSearchUsagesSupport { fun KtDeclaration.isExpectDeclaration(): Boolean = getInstance(project).isExpectDeclaration(this) + + fun KtDeclaration.actualsForExpected(module: Module? = null): Set = + getInstance(project).actualsForExpected(this, module) + + fun PsiElement.canBeResolvedWithFrontEnd(): Boolean = + getInstance(project).canBeResolvedWithFrontEnd(this) + + fun createConstructorHandle(ktDeclaration: KtDeclaration): ConstructorCallHandle = + getInstance(ktDeclaration.project).createConstructorHandle(ktDeclaration) + + fun createConstructorHandle(psiMethod: PsiMethod): ConstructorCallHandle = + getInstance(psiMethod.project).createConstructorHandle(psiMethod) } + fun actualsForExpected(declaration: KtDeclaration, module: Module? = null): Set + fun dataClassComponentMethodName(element: KtParameter): String? fun hasType(element: KtExpression): Boolean @@ -149,4 +168,10 @@ interface KotlinSearchUsagesSupport { fun expectedDeclarationIfAny(declaration: KtDeclaration): KtDeclaration? fun isExpectDeclaration(declaration: KtDeclaration): Boolean + + fun canBeResolvedWithFrontEnd(element: PsiElement): Boolean + + fun createConstructorHandle(ktDeclaration: KtDeclaration): ConstructorCallHandle + + fun createConstructorHandle(psiMethod: PsiMethod): ConstructorCallHandle } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConstructorDelegationCallReferenceSearcher.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConstructorDelegationCallReferenceSearcher.kt similarity index 90% rename from idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConstructorDelegationCallReferenceSearcher.kt rename to idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConstructorDelegationCallReferenceSearcher.kt index f1fbf3c3010..78a97411d0c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConstructorDelegationCallReferenceSearcher.kt +++ b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConstructorDelegationCallReferenceSearcher.kt @@ -20,15 +20,15 @@ import com.intellij.openapi.application.QueryExecutorBase import com.intellij.psi.PsiReference import com.intellij.psi.search.searches.MethodReferencesSearch.SearchParameters import com.intellij.util.Processor -import org.jetbrains.kotlin.idea.caches.resolve.util.hasJavaResolutionFacade import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.search.KotlinSearchUsagesSupport.Companion.canBeResolvedWithFrontEnd import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages class KotlinConstructorDelegationCallReferenceSearcher : QueryExecutorBase(true) { override fun processQuery(queryParameters: SearchParameters, consumer: Processor) { val method = queryParameters.method if (!method.isConstructor) return - if (!method.hasJavaResolutionFacade()) return + if (!method.canBeResolvedWithFrontEnd()) return method.processDelegationCallConstructorUsages(method.useScope.intersectWith(queryParameters.effectiveSearchScope)) { it.calleeExpression?.mainReference?.let { consumer.process(it) } ?: true diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConventionMethodReferencesSearcher.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConventionMethodReferencesSearcher.kt similarity index 90% rename from idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConventionMethodReferencesSearcher.kt rename to idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConventionMethodReferencesSearcher.kt index 7760ed06c00..c61342511ad 100644 --- a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConventionMethodReferencesSearcher.kt +++ b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinConventionMethodReferencesSearcher.kt @@ -20,13 +20,13 @@ import com.intellij.openapi.application.QueryExecutorBase import com.intellij.psi.PsiReference import com.intellij.psi.search.searches.MethodReferencesSearch import com.intellij.util.Processor -import org.jetbrains.kotlin.idea.caches.resolve.util.hasJavaResolutionFacade +import org.jetbrains.kotlin.idea.search.KotlinSearchUsagesSupport.Companion.canBeResolvedWithFrontEnd import org.jetbrains.kotlin.idea.search.usagesSearch.operators.OperatorReferenceSearcher class KotlinConventionMethodReferencesSearcher : QueryExecutorBase(true) { override fun processQuery(queryParameters: MethodReferencesSearch.SearchParameters, consumer: Processor) { val method = queryParameters.method - if (!method.hasJavaResolutionFacade()) return + if (!method.canBeResolvedWithFrontEnd()) return val operatorSearcher = OperatorReferenceSearcher.create( queryParameters.method, diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinDefinitionsSearcher.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinDefinitionsSearcher.kt similarity index 91% rename from idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinDefinitionsSearcher.kt rename to idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinDefinitionsSearcher.kt index 5beb01b7b23..417c17eded1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinDefinitionsSearcher.kt +++ b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinDefinitionsSearcher.kt @@ -28,15 +28,15 @@ import com.intellij.util.QueryExecutor import com.intellij.util.containers.ContainerUtil import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.elements.KtLightMethod -import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.asJava.unwrapped -import org.jetbrains.kotlin.idea.caches.lightClasses.KtFakeLightClass +import org.jetbrains.kotlin.idea.asJava.LightClassProvider.Companion.providedCreateKtFakeLightClass +import org.jetbrains.kotlin.idea.asJava.LightClassProvider.Companion.providedToLightClass +import org.jetbrains.kotlin.idea.search.KotlinSearchUsagesSupport.Companion.actualsForExpected +import org.jetbrains.kotlin.idea.search.KotlinSearchUsagesSupport.Companion.forEachOverridingMethod +import org.jetbrains.kotlin.idea.search.KotlinSearchUsagesSupport.Companion.isExpectDeclaration import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachImplementation -import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingMethod import org.jetbrains.kotlin.idea.search.declarationsSearch.toPossiblyFakeLightMethods -import org.jetbrains.kotlin.idea.util.actualsForExpected import org.jetbrains.kotlin.idea.util.application.runReadAction -import org.jetbrains.kotlin.idea.util.isExpectDeclaration import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.contains import java.util.* @@ -107,7 +107,9 @@ class KotlinDefinitionsSearcher : QueryExecutor): Boolean { - val psiClass = runReadAction { klass.toLightClass() ?: KtFakeLightClass(klass) } + val psiClass = runReadAction { klass.providedToLightClass() ?: providedCreateKtFakeLightClass(klass) } + as? KtLightClass + ?: return false //TODO Implement FIR support for not nullable providedCreateKtFakeLightClass val searchScope = runReadAction { psiClass.useScope } if (searchScope is LocalSearchScope) { diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinOverridingMethodReferenceSearcher.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinOverridingMethodReferenceSearcher.kt similarity index 96% rename from idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinOverridingMethodReferenceSearcher.kt rename to idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinOverridingMethodReferenceSearcher.kt index 641afe43534..93b1b426657 100644 --- a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinOverridingMethodReferenceSearcher.kt +++ b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinOverridingMethodReferenceSearcher.kt @@ -14,12 +14,12 @@ import com.intellij.psi.search.searches.MethodReferencesSearch import com.intellij.psi.util.MethodSignatureUtil import com.intellij.psi.util.TypeConversionUtil import com.intellij.util.Processor -import org.jetbrains.kotlin.asJava.toLightMethods +import org.jetbrains.kotlin.idea.asJava.LightClassProvider.Companion.providedToLightMethods import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.idea.references.SyntheticPropertyAccessorReference import org.jetbrains.kotlin.idea.references.readWriteAccess import org.jetbrains.kotlin.idea.search.restrictToKotlinSources -import org.jetbrains.kotlin.idea.util.runReadActionInSmartMode +import org.jetbrains.kotlin.idea.util.application.runReadActionInSmartMode import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.getPropertyNamesCandidatesByAccessorName import org.jetbrains.kotlin.name.Name @@ -106,7 +106,7 @@ class KotlinOverridingMethodReferenceSearcher : MethodUsagesSearcher() { } fun countNonFinalLightMethods() = refElement - .toLightMethods() + .providedToLightMethods() .filterNot { it.hasModifierProperty(PsiModifier.FINAL) } val lightMethods = when (refElement) { diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinPropertyAccessorsReferenceSearcher.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinPropertyAccessorsReferenceSearcher.kt similarity index 100% rename from idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinPropertyAccessorsReferenceSearcher.kt rename to idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinPropertyAccessorsReferenceSearcher.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTodoSearcher.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTodoSearcher.kt similarity index 100% rename from idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTodoSearcher.kt rename to idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTodoSearcher.kt diff --git a/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/usagesSearch/utilsIndependent.kt b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/usagesSearch/utilsIndependent.kt new file mode 100644 index 00000000000..8d182ec503c --- /dev/null +++ b/idea/idea-frontend-independent/src/org/jetbrains/kotlin/idea/search/usagesSearch/utilsIndependent.kt @@ -0,0 +1,126 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.search.usagesSearch + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.psi.* +import com.intellij.psi.search.SearchScope +import com.intellij.psi.search.searches.MethodReferencesSearch +import com.intellij.psi.search.searches.ReferencesSearch +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.idea.asJava.LightClassProvider.Companion.providedToLightMethods +import org.jetbrains.kotlin.idea.search.KotlinSearchUsagesSupport +import org.jetbrains.kotlin.idea.search.KotlinSearchUsagesSupport.Companion.createConstructorHandle +import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest +import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.contains + +fun PsiElement.processDelegationCallConstructorUsages(scope: SearchScope, process: (KtCallElement) -> Boolean): Boolean { + val task = buildProcessDelegationCallConstructorUsagesTask(scope, process) + return task() +} + +// should be executed under read-action, returns long-running part to be executed outside read-action +fun PsiElement.buildProcessDelegationCallConstructorUsagesTask(scope: SearchScope, process: (KtCallElement) -> Boolean): () -> Boolean { + ApplicationManager.getApplication().assertReadAccessAllowed() + val task1 = buildProcessDelegationCallKotlinConstructorUsagesTask(scope, process) + val task2 = buildProcessDelegationCallJavaConstructorUsagesTask(scope, process) + return { task1() && task2() } +} + +private fun PsiElement.buildProcessDelegationCallKotlinConstructorUsagesTask( + scope: SearchScope, + process: (KtCallElement) -> Boolean +): () -> Boolean { + val element = unwrapped + if (element != null && element !in scope) return { true } + + val klass = when (element) { + is KtConstructor<*> -> element.getContainingClassOrObject() + is KtClass -> element + else -> return { true } + } + + if (klass !is KtClass || element !is KtDeclaration) return { true } + + val constructorHandler = createConstructorHandle(element) + + if (!processClassDelegationCallsToSpecifiedConstructor(klass, constructorHandler, process)) return { false } + + // long-running task, return it to execute outside read-action + return { processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, constructorHandler, process) } +} + +private fun PsiElement.buildProcessDelegationCallJavaConstructorUsagesTask( + scope: SearchScope, + process: (KtCallElement) -> Boolean +): () -> Boolean { + if (this is KtLightElement<*, *>) return { true } + // TODO: Temporary hack to avoid NPE while KotlinNoOriginLightMethod is around + if (this is KtLightMethod && this.kotlinOrigin == null) return { true } + if (!(this is PsiMethod && isConstructor)) return { true } + val klass = containingClass ?: return { true } + + val ctorHandle = createConstructorHandle(this) + return { processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, ctorHandle, process) } +} + + +private fun processInheritorsDelegatingCallToSpecifiedConstructor( + klass: PsiElement, + scope: SearchScope, + constructorCallComparator: KotlinSearchUsagesSupport.ConstructorCallHandle, + process: (KtCallElement) -> Boolean +): Boolean { + return HierarchySearchRequest(klass, scope, false).searchInheritors().all { + runReadAction { + val unwrapped = it.takeIf { it.isValid }?.unwrapped + if (unwrapped is KtClass) + processClassDelegationCallsToSpecifiedConstructor(unwrapped, constructorCallComparator, process) + else + true + } + } +} + +private fun processClassDelegationCallsToSpecifiedConstructor( + klass: KtClass, + constructorCallHadle: KotlinSearchUsagesSupport.ConstructorCallHandle, + process: (KtCallElement) -> Boolean +): Boolean { + for (secondaryConstructor in klass.secondaryConstructors) { + val delegationCall = secondaryConstructor.getDelegationCall() + if (constructorCallHadle.referencedTo(delegationCall)) { + if (!process(delegationCall)) return false + } + } + if (!klass.isEnum()) return true + for (declaration in klass.declarations) { + if (declaration is KtEnumEntry) { + val delegationCall = + declaration.superTypeListEntries.firstOrNull() as? KtSuperTypeCallEntry + ?: continue + + if (constructorCallHadle.referencedTo(delegationCall.calleeExpression)) { + if (!process(delegationCall)) return false + } + } + } + return true +} + +fun PsiElement.searchReferencesOrMethodReferences(): Collection { + val lightMethods = providedToLightMethods() + return if (lightMethods.isNotEmpty()) { + lightMethods.flatMapTo(LinkedHashSet()) { MethodReferencesSearch.search(it) } + } else { + ReferencesSearch.search(this).findAll() + } +} \ No newline at end of file diff --git a/idea/resources-fir/META-INF/plugin.xml b/idea/resources-fir/META-INF/plugin.xml index 44cd228c797..1c9ce5b0644 100644 --- a/idea/resources-fir/META-INF/plugin.xml +++ b/idea/resources-fir/META-INF/plugin.xml @@ -177,6 +177,9 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu + + + @@ -217,15 +220,15 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu - + + + + + + - - - - - diff --git a/idea/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportImpl.kt b/idea/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportImpl.kt index 3d0ee2842b8..dc4531a82da 100644 --- a/idea/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportImpl.kt +++ b/idea/src/org/jetbrains/kotlin/idea/search/KotlinSearchUsagesSupportImpl.kt @@ -5,10 +5,12 @@ package org.jetbrains.kotlin.idea.search +import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project import com.intellij.psi.* import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.SearchScope +import org.jetbrains.kotlin.idea.caches.resolve.util.hasJavaResolutionFacade import org.jetbrains.kotlin.idea.core.isInheritable import org.jetbrains.kotlin.idea.core.isOverridable import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingMethod @@ -16,12 +18,15 @@ import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOpt import org.jetbrains.kotlin.idea.search.usagesSearch.* import org.jetbrains.kotlin.idea.stubindex.KotlinTypeAliasShortNameIndex import org.jetbrains.kotlin.idea.util.ProjectRootsUtil +import org.jetbrains.kotlin.idea.util.actualsForExpected import org.jetbrains.kotlin.idea.util.expectedDeclarationIfAny import org.jetbrains.kotlin.idea.util.isExpectDeclaration import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.ImportPath class KotlinSearchUsagesSupportImpl : KotlinSearchUsagesSupport { + override fun actualsForExpected(declaration: KtDeclaration, module: Module?): Set = + declaration.actualsForExpected(module) override fun dataClassComponentMethodName(element: KtParameter): String? = element.dataClassComponentFunction()?.name?.asString() @@ -101,4 +106,13 @@ class KotlinSearchUsagesSupportImpl : KotlinSearchUsagesSupport { override fun isExpectDeclaration(declaration: KtDeclaration): Boolean = declaration.isExpectDeclaration() + + override fun canBeResolvedWithFrontEnd(element: PsiElement): Boolean = + element.hasJavaResolutionFacade() + + override fun createConstructorHandle(ktDeclaration: KtDeclaration): KotlinSearchUsagesSupport.ConstructorCallHandle = + KotlinConstructorCallLazyDescriptorHandle(ktDeclaration) + + override fun createConstructorHandle(psiMethod: PsiMethod): KotlinSearchUsagesSupport.ConstructorCallHandle = + JavaConstructorCallLazyDescriptorHandle(psiMethod) } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt b/idea/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt index 493af3a5edc..4a5c1e7bd44 100644 --- a/idea/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt @@ -16,27 +16,18 @@ package org.jetbrains.kotlin.idea.search.usagesSearch -import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project import com.intellij.psi.* -import com.intellij.psi.search.SearchScope -import com.intellij.psi.search.searches.MethodReferencesSearch -import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.util.MethodSignatureUtil import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.classes.lazyPub -import org.jetbrains.kotlin.asJava.elements.KtLightElement -import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.asJava.toLightMethods -import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.idea.caches.project.NotUnderContentRootModuleInfo.project import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.idea.caches.resolve.resolveToParameterDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaMemberDescriptor import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaMethodDescriptor import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaOrKotlinMemberDescriptor @@ -46,9 +37,8 @@ import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.idea.project.findAnalyzerServices import org.jetbrains.kotlin.idea.references.unwrappedTargets +import org.jetbrains.kotlin.idea.search.KotlinSearchUsagesSupport import org.jetbrains.kotlin.idea.search.ReceiverTypeSearcherInfo -import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest -import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions import org.jetbrains.kotlin.idea.util.FuzzyType import org.jetbrains.kotlin.idea.util.application.runReadAction @@ -57,7 +47,6 @@ import org.jetbrains.kotlin.idea.util.toFuzzyType import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.contains import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.* @@ -70,6 +59,27 @@ import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.util.isValidOperator +class KotlinConstructorCallLazyDescriptorHandle(ktElement: KtDeclaration) : + KotlinSearchUsagesSupport.ConstructorCallHandle { + private val descriptor: ConstructorDescriptor? by lazyPub { ktElement.constructor } + + override fun referencedTo(element: KtElement): Boolean = + element.getConstructorCallDescriptor().let { + it != null && descriptor != null && it == descriptor + } +} + +class JavaConstructorCallLazyDescriptorHandle(psiMethod: PsiMethod) : + KotlinSearchUsagesSupport.ConstructorCallHandle { + + private val descriptor: ConstructorDescriptor? by lazyPub { psiMethod.getJavaMethodDescriptor() as? ConstructorDescriptor } + + override fun referencedTo(element: KtElement): Boolean = + element.getConstructorCallDescriptor().let { + it != null && descriptor != null && it == descriptor + } +} + fun tryRenderDeclarationCompactStyle(declaration: KtDeclaration): String? = declaration.descriptor?.let { DescriptorRenderer.COMPACT.render(it) } @@ -143,104 +153,6 @@ private fun KtElement.getConstructorCallDescriptor(): DeclarationDescriptor? { return null } -fun PsiElement.processDelegationCallConstructorUsages(scope: SearchScope, process: (KtCallElement) -> Boolean): Boolean { - val task = buildProcessDelegationCallConstructorUsagesTask(scope, process) - return task() -} - -// should be executed under read-action, returns long-running part to be executed outside read-action -fun PsiElement.buildProcessDelegationCallConstructorUsagesTask(scope: SearchScope, process: (KtCallElement) -> Boolean): () -> Boolean { - ApplicationManager.getApplication().assertReadAccessAllowed() - val task1 = buildProcessDelegationCallKotlinConstructorUsagesTask(scope, process) - val task2 = buildProcessDelegationCallJavaConstructorUsagesTask(scope, process) - return { task1() && task2() } -} - -private fun PsiElement.buildProcessDelegationCallKotlinConstructorUsagesTask( - scope: SearchScope, - process: (KtCallElement) -> Boolean -): () -> Boolean { - val element = unwrapped - if (element != null && element !in scope) return { true } - - val klass = when (element) { - is KtConstructor<*> -> element.getContainingClassOrObject() - is KtClass -> element - else -> return { true } - } - - if (klass !is KtClass || element !is KtDeclaration) return { true } - val descriptor = lazyPub { element.constructor } - - if (!processClassDelegationCallsToSpecifiedConstructor(klass, descriptor, process)) return { false } - - // long-running task, return it to execute outside read-action - return { processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process) } -} - -private fun PsiElement.buildProcessDelegationCallJavaConstructorUsagesTask( - scope: SearchScope, - process: (KtCallElement) -> Boolean -): () -> Boolean { - if (this is KtLightElement<*, *>) return { true } - // TODO: Temporary hack to avoid NPE while KotlinNoOriginLightMethod is around - if (this is KtLightMethod && this.kotlinOrigin == null) return { true } - if (!(this is PsiMethod && isConstructor)) return { true } - val klass = containingClass ?: return { true } - val descriptor = lazyPub { getJavaMethodDescriptor() as? ConstructorDescriptor } - return { processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process) } -} - - -private fun processInheritorsDelegatingCallToSpecifiedConstructor( - klass: PsiElement, - scope: SearchScope, - lazyDescriptor: Lazy, - process: (KtCallElement) -> Boolean -): Boolean { - return HierarchySearchRequest(klass, scope, false).searchInheritors().all { - runReadAction { - val unwrapped = it.takeIf { it.isValid }?.unwrapped - if (unwrapped is KtClass) - processClassDelegationCallsToSpecifiedConstructor(unwrapped, lazyDescriptor, process) - else - true - } - } -} - -private fun processClassDelegationCallsToSpecifiedConstructor( - klass: KtClass, - lazyDescriptor: Lazy, - process: (KtCallElement) -> Boolean -): Boolean { - for (secondaryConstructor in klass.secondaryConstructors) { - val delegationCallDescriptor = - secondaryConstructor.getDelegationCall().getConstructorCallDescriptor() - ?: continue - - if (lazyDescriptor.value == delegationCallDescriptor) { - if (!process(secondaryConstructor.getDelegationCall())) return false - } - } - if (!klass.isEnum()) return true - for (declaration in klass.declarations) { - if (declaration is KtEnumEntry) { - val delegationCall = - declaration.superTypeListEntries.firstOrNull() as? KtSuperTypeCallEntry - ?: continue - val constructorCallDescriptor = - delegationCall.calleeExpression.getConstructorCallDescriptor() - ?: continue - - if (lazyDescriptor.value == constructorCallDescriptor) { - 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) // Used in extension search fun PsiReference.isExtensionOfDeclarationClassUsage(declaration: KtNamedDeclaration): Boolean { @@ -304,15 +216,6 @@ fun PsiReference.isCallableOverrideUsage(declaration: KtNamedDeclaration): Boole } } -fun PsiElement.searchReferencesOrMethodReferences(): Collection { - val lightMethods = toLightMethods() - return if (lightMethods.isNotEmpty()) { - lightMethods.flatMapTo(LinkedHashSet()) { MethodReferencesSearch.search(it) } - } else { - ReferencesSearch.search(this).findAll() - } -} - fun List.filterDataClassComponentsIfDisabled(kotlinOptions: KotlinReferencesSearchOptions): List { if (kotlinOptions.searchForComponentConventions) return this