Misc: Support light-methodless members in overrider search

In particular, support line markers for expect-class members
and navigation to expect-class members from corresponding base members
This commit is contained in:
Alexey Sedunov
2017-09-27 13:29:58 +03:00
parent 17abce2811
commit 4cc1e22a5a
22 changed files with 292 additions and 86 deletions
@@ -18,14 +18,19 @@ package org.jetbrains.kotlin.idea.caches.resolve.lightClasses
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElementFactory
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiType
import com.intellij.psi.impl.light.AbstractLightClass
import com.intellij.psi.impl.light.LightMethod
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.LightClassInheritanceHelper
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -58,4 +63,28 @@ class KtFakeLightClass(override val kotlinOrigin: KtClassOrObject) :
val thisDescriptor = kotlinOrigin.resolveToDescriptorIfAny() as? ClassDescriptor ?: return false
return if (checkDeep) DescriptorUtils.isSubclass(thisDescriptor, baseDescriptor) else DescriptorUtils.isDirectSubclass(thisDescriptor, baseDescriptor)
}
}
class KtFakeLightMethod private constructor(
val ktDeclaration: KtNamedDeclaration,
ktClassOrObject : KtClassOrObject
) : LightMethod (
ktDeclaration.manager,
PsiElementFactory.SERVICE.getInstance(ktDeclaration.project).createMethod(ktDeclaration.name ?: "", PsiType.VOID),
KtFakeLightClass(ktClassOrObject),
KotlinLanguage.INSTANCE
), KtLightElement<KtNamedDeclaration, PsiMethod> {
override val kotlinOrigin get() = ktDeclaration
override val clsDelegate get() = myMethod
override fun getNavigationElement() = ktDeclaration
override fun getIcon(flags: Int) = ktDeclaration.getIcon(flags)
override fun getUseScope() = ktDeclaration.useScope
companion object {
fun get(ktDeclaration: KtNamedDeclaration): KtFakeLightMethod? {
val ktClassOrObject = ktDeclaration.containingClassOrObject ?: return null
return KtFakeLightMethod(ktDeclaration, ktClassOrObject)
}
}
}
@@ -23,12 +23,13 @@ import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.util.EmptyQuery
import com.intellij.util.Query
import org.jetbrains.kotlin.asJava.toLightClassWithBuiltinMapping
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightClass
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtClassOrObject
fun HierarchySearchRequest<*>.searchInheritors(): Query<PsiClass> {
val psiClass: PsiClass = when (originalElement) {
is KtClassOrObject -> runReadAction { originalElement.toLightClassWithBuiltinMapping() }
is KtClassOrObject -> runReadAction { originalElement.toLightClassWithBuiltinMapping() ?: KtFakeLightClass(originalElement) }
is PsiClass -> originalElement
else -> null
} ?: return EmptyQuery.getEmptyQuery()
@@ -29,14 +29,14 @@ import com.intellij.util.EmptyQuery
import com.intellij.util.MergeQuery
import com.intellij.util.Processor
import com.intellij.util.Query
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.isOverridable
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightClass
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightMethod
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
@@ -45,10 +45,7 @@ import org.jetbrains.kotlin.idea.search.allScope
import org.jetbrains.kotlin.idea.search.excludeKotlinSources
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
@@ -122,6 +119,17 @@ object PsiMethodOverridingHierarchyTraverser: HierarchyTraverser<PsiMethod> {
override fun shouldDescend(element: PsiMethod): Boolean = PsiUtil.canBeOverriden(element)
}
fun PsiElement.toPossiblyFakeLightMethods(): List<PsiMethod> {
if (this is PsiMethod) return listOf(this)
val element = unwrapped ?: return emptyList()
val lightMethods = element.toLightMethods()
if (lightMethods.isNotEmpty()) return lightMethods
return if (element is KtNamedDeclaration) listOfNotNull(KtFakeLightMethod.get(element)) else emptyList()
}
private fun forEachKotlinOverride(
ktClass: KtClass,
members: List<KtNamedDeclaration>,
@@ -133,7 +141,7 @@ private fun forEachKotlinOverride(
if (baseDescriptors.isEmpty()) return true
HierarchySearchRequest(ktClass, scope.restrictToKotlinSources(), true).searchInheritors().forEach {
val inheritor = (it as? KtLightClass)?.kotlinOrigin ?: return@forEach
val inheritor = it.unwrapped as? KtClassOrObject ?: return@forEach
val inheritorDescriptor = runReadAction { inheritor.unsafeResolveToDescriptor() as ClassDescriptor }
val substitutor = getTypeSubstitutor(baseClassDescriptor.defaultType, inheritorDescriptor.defaultType) ?: return@forEach
baseDescriptors.forEach {
@@ -149,16 +157,31 @@ private fun forEachKotlinOverride(
return true
}
fun KtNamedDeclaration.forEachOverridingElement(
scope: SearchScope = runReadAction { useScope },
processor: (PsiElement) -> Boolean
): Boolean {
val ktClass = runReadAction { containingClassOrObject as? KtClass } ?: return true
toLightMethods().forEach {
if (!OverridingMethodsSearch.search(it, scope.excludeKotlinSources(), true).all(processor)) return false
}
return forEachKotlinOverride(ktClass, listOf(this), scope) { _, overrider -> processor(overrider) }
}
fun PsiMethod.forEachOverridingMethod(
scope: SearchScope = runReadAction { useScope },
processor: (PsiMethod) -> Boolean
): Boolean {
if (!OverridingMethodsSearch.search(this, scope.excludeKotlinSources(), true).forEach(processor)) return false
if (this !is KtFakeLightMethod) {
if (!OverridingMethodsSearch.search(this, scope.excludeKotlinSources(), true).forEach(processor)) return false
}
val ktMember = (this as? KtLightMethod)?.kotlinOrigin as? KtNamedDeclaration ?: return true
val ktMember = this.unwrapped as? KtNamedDeclaration ?: return true
val ktClass = runReadAction { ktMember.containingClassOrObject as? KtClass } ?: return true
return forEachKotlinOverride(ktClass, listOf(ktMember), scope) { _, overrider ->
val lightMethods = runReadAction { overrider.toLightMethods() }
val lightMethods = runReadAction { overrider.toPossiblyFakeLightMethods() }
lightMethods.all { processor(it) }
}
}
@@ -174,9 +197,11 @@ fun PsiMethod.forEachImplementation(
fun PsiClass.forEachDeclaredMemberOverride(processor: (superMember: PsiElement, overridingMember: PsiElement) -> Boolean) {
val scope = runReadAction { useScope }
AllOverridingMethodsSearch.search(this, scope.excludeKotlinSources()).all { processor(it.first, it.second) }
if (this !is KtFakeLightClass) {
AllOverridingMethodsSearch.search(this, scope.excludeKotlinSources()).all { processor(it.first, it.second) }
}
val ktClass = (this as? KtLightClass)?.kotlinOrigin as? KtClass ?: return
val ktClass = unwrapped as? KtClass ?: return
val members = ktClass.declarations.filterIsInstance<KtNamedDeclaration>() +
ktClass.primaryConstructorParameters.filter { it.hasValOrVar() }
forEachKotlinOverride(ktClass, members, scope, processor)