[FIR IDE] Separate searchers from descriptor usage

KotlinConstructorDelegationCallReferenceSearcher
KotlinConventionMethodReferencesSearcher
KotlinDefinitionsSearcher
KotlinOverridingMethodReferenceSearcher
KotlinPropertyAccessorsReferenceSearcher
KotlinTodoSearcher
This commit is contained in:
Igor Yakovlev
2020-09-15 15:19:48 +03:00
parent 36e6247125
commit be4fcd626c
12 changed files with 239 additions and 138 deletions
@@ -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<KtDeclaration> {
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
}
}
}
}
@@ -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<KtDeclaration> =
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<KtDeclaration>
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
}
@@ -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<PsiReference, SearchParameters>(true) {
override fun processQuery(queryParameters: SearchParameters, consumer: Processor<in PsiReference>) {
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
@@ -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<PsiReference, MethodReferencesSearch.SearchParameters>(true) {
override fun processQuery(queryParameters: MethodReferencesSearch.SearchParameters, consumer: Processor<in PsiReference>) {
val method = queryParameters.method
if (!method.hasJavaResolutionFacade()) return
if (!method.canBeResolvedWithFrontEnd()) return
val operatorSearcher = OperatorReferenceSearcher.create(
queryParameters.method,
@@ -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<PsiElement, DefinitionsScopedSea
}
private fun processClassImplementations(klass: KtClass, consumer: Processor<PsiElement>): 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) {
@@ -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) {
@@ -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<PsiReference> {
val lightMethods = providedToLightMethods()
return if (lightMethods.isNotEmpty()) {
lightMethods.flatMapTo(LinkedHashSet()) { MethodReferencesSearch.search(it) }
} else {
ReferencesSearch.search(this).findAll()
}
}
+9 -6
View File
@@ -177,6 +177,9 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
<extensions defaultExtensionNs="com.intellij">
<postStartupActivity implementation="org.jetbrains.kotlin.idea.PluginStartupActivity"/>
<projectService serviceImplementation="org.jetbrains.kotlin.idea.PluginStartupService"/>
<iconProvider implementation="org.jetbrains.kotlin.idea.KotlinIconProviderBase"/>
<projectService serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.IdePackageOracleFactory"/>
@@ -217,15 +220,15 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
<gotoDeclarationHandler implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinImportAliasGotoDeclarationHandler"/>
<referencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearcher"/>
<referencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinAliasedImportedElementSearcher"/>
<!-- <directClassInheritorsSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDirectInheritorsSearcher"/>-->
<methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinConstructorDelegationCallReferenceSearcher"/>
<methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinConventionMethodReferencesSearcher"/>
<definitionsScopedSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDefinitionsSearcher"/>
<methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinOverridingMethodReferenceSearcher"/>
<methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinPropertyAccessorsReferenceSearcher"/>
<!-- <directClassInheritorsSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDirectInheritorsSearcher"/>-->
<!-- <overridingMethodsSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinOverridingMethodsWithGenericsSearcher"/>-->
<!-- <definitionsScopedSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDefinitionsSearcher"/>-->
<!-- <annotatedElementsSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinAnnotatedElementsSearcher"/>-->
<!-- <classesWithAnnotatedMembersSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinClassesWithAnnotatedMembersSearcher"/>-->
<!-- <methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinPropertyAccessorsReferenceSearcher"/>-->
<!-- <methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinConstructorDelegationCallReferenceSearcher"/>-->
<!-- <methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinOverridingMethodReferenceSearcher"/>-->
<!-- <methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinConventionMethodReferencesSearcher"/>-->
<!-- <methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.DefaultAnnotationMethodKotlinImplicitReferenceSearcher"/>-->
<readWriteAccessDetector implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector" id="kotlin"/>
@@ -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<KtDeclaration> =
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)
}
@@ -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<ConstructorDescriptor?>,
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<ConstructorDescriptor?>,
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<PsiReference> {
val lightMethods = toLightMethods()
return if (lightMethods.isNotEmpty()) {
lightMethods.flatMapTo(LinkedHashSet()) { MethodReferencesSearch.search(it) }
} else {
ReferencesSearch.search(this).findAll()
}
}
fun <T : PsiNamedElement> List<T>.filterDataClassComponentsIfDisabled(kotlinOptions: KotlinReferencesSearchOptions): List<T> {
if (kotlinOptions.searchForComponentConventions) return this