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.PsiClass
import com.intellij.psi.PsiElementFactory 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.AbstractLightClass
import com.intellij.psi.impl.light.LightMethod
import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.LightClassInheritanceHelper import org.jetbrains.kotlin.asJava.classes.LightClassInheritanceHelper
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -58,4 +63,28 @@ class KtFakeLightClass(override val kotlinOrigin: KtClassOrObject) :
val thisDescriptor = kotlinOrigin.resolveToDescriptorIfAny() as? ClassDescriptor ?: return false val thisDescriptor = kotlinOrigin.resolveToDescriptorIfAny() as? ClassDescriptor ?: return false
return if (checkDeep) DescriptorUtils.isSubclass(thisDescriptor, baseDescriptor) else DescriptorUtils.isDirectSubclass(thisDescriptor, baseDescriptor) 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.EmptyQuery
import com.intellij.util.Query import com.intellij.util.Query
import org.jetbrains.kotlin.asJava.toLightClassWithBuiltinMapping 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.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
fun HierarchySearchRequest<*>.searchInheritors(): Query<PsiClass> { fun HierarchySearchRequest<*>.searchInheritors(): Query<PsiClass> {
val psiClass: PsiClass = when (originalElement) { val psiClass: PsiClass = when (originalElement) {
is KtClassOrObject -> runReadAction { originalElement.toLightClassWithBuiltinMapping() } is KtClassOrObject -> runReadAction { originalElement.toLightClassWithBuiltinMapping() ?: KtFakeLightClass(originalElement) }
is PsiClass -> originalElement is PsiClass -> originalElement
else -> null else -> null
} ?: return EmptyQuery.getEmptyQuery() } ?: return EmptyQuery.getEmptyQuery()
@@ -29,14 +29,14 @@ import com.intellij.util.EmptyQuery
import com.intellij.util.MergeQuery import com.intellij.util.MergeQuery
import com.intellij.util.Processor import com.intellij.util.Processor
import com.intellij.util.Query 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.getRepresentativeLightMethod
import org.jetbrains.kotlin.asJava.toLightMethods import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.isOverridable 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.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations 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.excludeKotlinSources
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
@@ -122,6 +119,17 @@ object PsiMethodOverridingHierarchyTraverser: HierarchyTraverser<PsiMethod> {
override fun shouldDescend(element: PsiMethod): Boolean = PsiUtil.canBeOverriden(element) 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( private fun forEachKotlinOverride(
ktClass: KtClass, ktClass: KtClass,
members: List<KtNamedDeclaration>, members: List<KtNamedDeclaration>,
@@ -133,7 +141,7 @@ private fun forEachKotlinOverride(
if (baseDescriptors.isEmpty()) return true if (baseDescriptors.isEmpty()) return true
HierarchySearchRequest(ktClass, scope.restrictToKotlinSources(), true).searchInheritors().forEach { 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 inheritorDescriptor = runReadAction { inheritor.unsafeResolveToDescriptor() as ClassDescriptor }
val substitutor = getTypeSubstitutor(baseClassDescriptor.defaultType, inheritorDescriptor.defaultType) ?: return@forEach val substitutor = getTypeSubstitutor(baseClassDescriptor.defaultType, inheritorDescriptor.defaultType) ?: return@forEach
baseDescriptors.forEach { baseDescriptors.forEach {
@@ -149,16 +157,31 @@ private fun forEachKotlinOverride(
return true 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( fun PsiMethod.forEachOverridingMethod(
scope: SearchScope = runReadAction { useScope }, scope: SearchScope = runReadAction { useScope },
processor: (PsiMethod) -> Boolean processor: (PsiMethod) -> Boolean
): 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 val ktClass = runReadAction { ktMember.containingClassOrObject as? KtClass } ?: return true
return forEachKotlinOverride(ktClass, listOf(ktMember), scope) { _, overrider -> return forEachKotlinOverride(ktClass, listOf(ktMember), scope) { _, overrider ->
val lightMethods = runReadAction { overrider.toLightMethods() } val lightMethods = runReadAction { overrider.toPossiblyFakeLightMethods() }
lightMethods.all { processor(it) } lightMethods.all { processor(it) }
} }
} }
@@ -174,9 +197,11 @@ fun PsiMethod.forEachImplementation(
fun PsiClass.forEachDeclaredMemberOverride(processor: (superMember: PsiElement, overridingMember: PsiElement) -> Boolean) { fun PsiClass.forEachDeclaredMemberOverride(processor: (superMember: PsiElement, overridingMember: PsiElement) -> Boolean) {
val scope = runReadAction { useScope } 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>() + val members = ktClass.declarations.filterIsInstance<KtNamedDeclaration>() +
ktClass.primaryConstructorParameters.filter { it.hasValOrVar() } ktClass.primaryConstructorParameters.filter { it.hasValOrVar() }
forEachKotlinOverride(ktClass, members, scope, processor) forEachKotlinOverride(ktClass, members, scope, processor)
@@ -21,13 +21,11 @@ import com.intellij.psi.CommonClassNames
import com.intellij.psi.PsiClass import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.*
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightClass import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightClass
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightMethod
import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.KtSecondaryConstructor
import java.util.* import java.util.*
fun collectContainingClasses(methods: Collection<PsiMethod>): Set<PsiClass> { fun collectContainingClasses(methods: Collection<PsiMethod>): Set<PsiClass> {
@@ -58,7 +56,8 @@ internal fun getPsiMethod(element: PsiElement?): PsiMethod? {
return when { return when {
element == null -> null element == null -> null
element is PsiMethod -> element element is PsiMethod -> element
parent is KtNamedFunction || parent is KtSecondaryConstructor -> LightClassUtil.getLightClassMethod(parent as KtFunction) parent is KtNamedFunction || parent is KtSecondaryConstructor ->
LightClassUtil.getLightClassMethod(parent as KtFunction) ?: KtFakeLightMethod.get(parent)
else -> null else -> null
} }
} }
@@ -34,7 +34,6 @@ import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.PsiNameIdentifierOwner
import com.intellij.psi.search.searches.ClassInheritorsSearch import com.intellij.psi.search.searches.ClassInheritorsSearch
import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.getAccessorLightMethods
import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.MemberDescriptor import org.jetbrains.kotlin.descriptors.MemberDescriptor
@@ -42,11 +41,13 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightClass import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightClass
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightMethod
import org.jetbrains.kotlin.idea.core.isInheritable import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.idea.core.isOverridable import org.jetbrains.kotlin.idea.core.isOverridable
import org.jetbrains.kotlin.idea.core.toDescriptor import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.facet.implementedDescriptor import org.jetbrains.kotlin.idea.facet.implementedDescriptor
import org.jetbrains.kotlin.idea.facet.implementingDescriptors import org.jetbrains.kotlin.idea.facet.implementingDescriptors
import org.jetbrains.kotlin.idea.search.declarationsSearch.toPossiblyFakeLightMethods
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -271,10 +272,7 @@ private fun collectOverriddenPropertyAccessors(properties: Collection<KtNamedDec
val mappingToJava = HashMap<PsiElement, KtNamedDeclaration>() val mappingToJava = HashMap<PsiElement, KtNamedDeclaration>()
for (property in properties) { for (property in properties) {
if (property.isOverridable()) { if (property.isOverridable()) {
val accessorsPsiMethods = property.getAccessorLightMethods() property.toPossiblyFakeLightMethods().forEach { mappingToJava.put(it, property) }
for (psiMethod in accessorsPsiMethods) {
mappingToJava.put(psiMethod, property)
}
mappingToJava[property] = property mappingToJava[property] = property
} }
} }
@@ -352,7 +350,7 @@ private fun collectOverriddenFunctions(functions: Collection<KtNamedFunction>, r
val mappingToJava = HashMap<PsiElement, KtNamedFunction>() val mappingToJava = HashMap<PsiElement, KtNamedFunction>()
for (function in functions) { for (function in functions) {
if (function.isOverridable()) { if (function.isOverridable()) {
val method = LightClassUtil.getLightClassMethod(function) val method = LightClassUtil.getLightClassMethod(function) ?: KtFakeLightMethod.get(function)
if (method != null) { if (method != null) {
mappingToJava.put(method, function) mappingToJava.put(method, function)
} }
@@ -24,18 +24,20 @@ import com.intellij.ide.util.PsiElementListCellRenderer
import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.DumbService
import com.intellij.psi.* import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiModifier
import com.intellij.psi.search.PsiElementProcessor import com.intellij.psi.search.PsiElementProcessor
import com.intellij.psi.search.PsiElementProcessorAdapter import com.intellij.psi.search.PsiElementProcessorAdapter
import com.intellij.psi.search.searches.OverridingMethodsSearch import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.psi.util.PsiFormatUtil
import com.intellij.util.CommonProcessors import com.intellij.util.CommonProcessors
import gnu.trove.THashSet import gnu.trove.THashSet
import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.isTraitFakeOverride import org.jetbrains.kotlin.asJava.elements.isTraitFakeOverride
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachDeclaredMemberOverride import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachDeclaredMemberOverride
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingMethod import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingMethod
import org.jetbrains.kotlin.idea.search.declarationsSearch.toPossiblyFakeLightMethods
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import java.awt.event.MouseEvent import java.awt.event.MouseEvent
import java.util.* import java.util.*
@@ -50,7 +52,7 @@ internal fun <T> getOverriddenDeclarations(mappingToJava: MutableMap<PsiElement,
for (aClass in classes) { for (aClass in classes) {
aClass.forEachDeclaredMemberOverride { superMember, overridingMember -> aClass.forEachDeclaredMemberOverride { superMember, overridingMember ->
ProgressManager.checkCanceled() ProgressManager.checkCanceled()
if (overridingMember.toLightMethods().any { !it.isMethodWithDeclarationInOtherClass() }) { if (overridingMember.toPossiblyFakeLightMethods().any { !it.isMethodWithDeclarationInOtherClass() }) {
val declaration = mappingToJava[superMember] val declaration = mappingToJava[superMember]
if (declaration != null) { if (declaration != null) {
mappingToJava.remove(superMember) mappingToJava.remove(superMember)
@@ -67,7 +69,7 @@ internal fun <T> getOverriddenDeclarations(mappingToJava: MutableMap<PsiElement,
fun getOverriddenMethodTooltip(method: PsiMethod): String? { fun getOverriddenMethodTooltip(method: PsiMethod): String? {
val processor = PsiElementProcessor.CollectElementsWithLimit<PsiMethod>(5) val processor = PsiElementProcessor.CollectElementsWithLimit<PsiMethod>(5)
OverridingMethodsSearch.search(method, true).forEach(PsiElementProcessorAdapter(processor)) method.forEachOverridingMethod(processor = PsiElementProcessorAdapter(processor)::process)
val isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT) val isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT)
@@ -109,15 +111,7 @@ fun buildNavigateToOverriddenMethodPopup(e: MouseEvent?, element: PsiElement?):
var overridingJavaMethods = processor.collection.filter { !it.isMethodWithDeclarationInOtherClass() } var overridingJavaMethods = processor.collection.filter { !it.isMethodWithDeclarationInOtherClass() }
if (overridingJavaMethods.isEmpty()) return null if (overridingJavaMethods.isEmpty()) return null
val renderedSignatures = overridingJavaMethods.map { val renderer = MethodCellRenderer(false)
PsiFormatUtil.formatMethod(it,
PsiSubstitutor.EMPTY,
PsiFormatUtil.SHOW_PARAMETERS + PsiFormatUtil.SHOW_FQ_CLASS_NAMES,
PsiFormatUtil.SHOW_TYPE + PsiFormatUtil.SHOW_FQ_CLASS_NAMES)
}
val showMethodNames = renderedSignatures.distinct().size > 1
val renderer = MethodCellRenderer(showMethodNames)
overridingJavaMethods = overridingJavaMethods.sortedWith(renderer.comparator) overridingJavaMethods = overridingJavaMethods.sortedWith(renderer.comparator)
val methodsUpdater = OverridingMethodsUpdater(method, renderer) val methodsUpdater = OverridingMethodsUpdater(method, renderer)
@@ -33,9 +33,10 @@ import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.util.AdapterProcessor import com.intellij.util.AdapterProcessor
import com.intellij.util.CommonProcessors import com.intellij.util.CommonProcessors
import com.intellij.util.Function import com.intellij.util.Function
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.getAccessorLightMethods import org.jetbrains.kotlin.asJava.getAccessorLightMethods
import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingMethod
import org.jetbrains.kotlin.idea.search.declarationsSearch.toPossiblyFakeLightMethods
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDefinitionsSearcher import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDefinitionsSearcher
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -50,9 +51,9 @@ fun getOverriddenPropertyTooltip(property: KtNamedDeclaration): String? {
Function { method: PsiMethod? -> method?.containingClass } Function { method: PsiMethod? -> method?.containingClass }
) )
for (method in property.getAccessorLightMethods()) { for (method in property.toPossiblyFakeLightMethods()) {
if (!overriddenInClassesProcessor.isOverflow) { if (!overriddenInClassesProcessor.isOverflow) {
OverridingMethodsSearch.search(method, true).forEach(consumer) method.forEachOverridingMethod(processor = consumer::process)
} }
} }
@@ -85,11 +86,7 @@ fun buildNavigateToPropertyOverriddenDeclarationsPopup(e: MouseEvent?, element:
return null return null
} }
val psiPropertyMethods = when(propertyOrParameter) { val psiPropertyMethods = propertyOrParameter.toPossiblyFakeLightMethods()
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(propertyOrParameter)
is KtParameter -> LightClassUtil.getLightClassPropertyMethods(propertyOrParameter)
else -> return null
}
val elementProcessor = CommonProcessors.CollectUniquesProcessor<PsiElement>() val elementProcessor = CommonProcessors.CollectUniquesProcessor<PsiElement>()
val ktPsiMethodProcessor = Runnable { val ktPsiMethodProcessor = Runnable {
KotlinDefinitionsSearcher.processPropertyImplementationsMethods( KotlinDefinitionsSearcher.processPropertyImplementationsMethods(
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.idea.search.ideaExtensions package org.jetbrains.kotlin.idea.search.ideaExtensions
import com.intellij.codeInsight.navigation.MethodImplementationsSearch
import com.intellij.psi.PsiClass import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
@@ -28,13 +27,14 @@ import com.intellij.psi.search.searches.DefinitionsScopedSearch
import com.intellij.util.Processor import com.intellij.util.Processor
import com.intellij.util.QueryExecutor import com.intellij.util.QueryExecutor
import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightClass import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightClass
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachImplementation 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.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.contains import org.jetbrains.kotlin.psi.psiUtil.contains
@@ -117,29 +117,21 @@ class KotlinDefinitionsSearcher : QueryExecutor<PsiElement, DefinitionsScopedSea
} }
private fun processFunctionImplementations(function: KtFunction, scope: SearchScope, consumer: Processor<PsiElement>): Boolean { private fun processFunctionImplementations(function: KtFunction, scope: SearchScope, consumer: Processor<PsiElement>): Boolean {
val psiMethod = runReadAction { LightClassUtil.getLightClassMethod(function) } return runReadAction {
return psiMethod?.forEachImplementation(scope, consumer::process) ?: true function.toPossiblyFakeLightMethods().firstOrNull()?.forEachImplementation(scope, consumer::process) ?: true
}
} }
private fun processPropertyImplementations(parameter: KtParameter, scope: SearchScope, consumer: Processor<PsiElement>): Boolean { private fun processPropertyImplementations(declaration: KtNamedDeclaration, scope: SearchScope, consumer: Processor<PsiElement>): Boolean {
val accessorsPsiMethods = runReadAction { LightClassUtil.getLightClassPropertyMethods(parameter) } return runReadAction {
processPropertyImplementationsMethods(declaration.toPossiblyFakeLightMethods(), scope, consumer)
return processPropertyImplementationsMethods(accessorsPsiMethods, scope, consumer) }
} }
private fun processPropertyImplementations(property: KtProperty, scope: SearchScope, consumer: Processor<PsiElement>): Boolean { fun processPropertyImplementationsMethods(accessors: Iterable<PsiMethod>, scope: SearchScope, consumer: Processor<PsiElement>): Boolean {
val accessorsPsiMethods = runReadAction { LightClassUtil.getLightClassPropertyMethods(property) } return accessors.all { method ->
method.forEachOverridingMethod(scope) { implementation ->
return processPropertyImplementationsMethods(accessorsPsiMethods, scope, consumer) if (isDelegated(implementation)) return@forEachOverridingMethod true
}
fun processPropertyImplementationsMethods(accessors: LightClassUtil.PropertyAccessorsPsiMethods, scope: SearchScope, consumer: Processor<PsiElement>): Boolean {
for (method in accessors) {
val implementations = ArrayList<PsiMethod>()
MethodImplementationsSearch.getOverridingMethods(method, implementations, scope)
for (implementation in implementations) {
if (isDelegated(implementation)) continue
val elementToProcess = runReadAction { val elementToProcess = runReadAction {
val mirrorElement = (implementation as? KtLightMethod)?.kotlinOrigin val mirrorElement = (implementation as? KtLightMethod)?.kotlinOrigin
@@ -150,12 +142,9 @@ class KotlinDefinitionsSearcher : QueryExecutor<PsiElement, DefinitionsScopedSea
} }
} }
if (!consumer.process(elementToProcess)) { consumer.process(elementToProcess)
return false
}
} }
} }
return true
} }
} }
} }
@@ -3,5 +3,5 @@ enum class SampleEnum {
override fun <lineMarker descr="Overrides function in 'SampleEnum'">any</lineMarker>() { super.any() } override fun <lineMarker descr="Overrides function in 'SampleEnum'">any</lineMarker>() { super.any() }
}; };
open fun <lineMarker descr="<html><body>Is overridden in <br>&nbsp;&nbsp;&nbsp;&nbsp;Enum constant 'V1' in 'SampleEnum'</body></html>">any</lineMarker>() {} open fun <lineMarker descr="<html><body>Is overridden in <br>&nbsp;&nbsp;&nbsp;&nbsp;SampleEnum.V1</body></html>">any</lineMarker>() {}
} }
@@ -1,9 +1,21 @@
package test package test
open class <lineMarker>SimpleParent</lineMarker> open class <lineMarker>SimpleParent</lineMarker> {
open fun <lineMarker>foo</lineMarker>(n: Int) {}
open val <lineMarker>bar</lineMarker>: Int get() = 1
}
expect open class <lineMarker><lineMarker>ExpectedChild</lineMarker></lineMarker> : SimpleParent expect open class <lineMarker><lineMarker>ExpectedChild</lineMarker></lineMarker> : SimpleParent {
override fun <lineMarker><lineMarker>foo</lineMarker></lineMarker>(n: Int)
override val <lineMarker><lineMarker>bar</lineMarker></lineMarker>: Int
}
class ExpectedChildChild : ExpectedChild() class ExpectedChildChild : ExpectedChild() {
override fun <lineMarker>foo</lineMarker>(n: Int) {}
override val <lineMarker>bar</lineMarker>: Int get() = 1
}
class SimpleChild : SimpleParent() class SimpleChild : SimpleParent() {
override fun <lineMarker>foo</lineMarker>(n: Int) {}
override val <lineMarker>bar</lineMarker>: Int get() = 1
}
@@ -2,6 +2,12 @@
package test package test
actual open class ExpectedChild : SimpleParent() actual open class ExpectedChild : SimpleParent() {
actual override fun foo(n: Int) {}
actual override val bar: Int get() = 1
}
class ExpectedChildChildJvm : ExpectedChild() class ExpectedChildChildJvm : ExpectedChild() {
override fun foo(n: Int) {}
override val bar: Int get() = 1
}
@@ -2,10 +2,22 @@
package test package test
open class <caret>SimpleParent open class SimpleParent {
open fun foo(n: Int) {}
open val bar: Int get() = 1
}
expect open class ExpectedChild : SimpleParent expect open class ExpectedChild : SimpleParent {
override fun foo(n: Int)
override val bar: Int
}
class ExpectedChildChild : ExpectedChild() class ExpectedChildChild : ExpectedChild() {
override fun foo(n: Int) {}
override val bar: Int get() = 1
}
class SimpleChild : SimpleParent() class SimpleChild : SimpleParent() {
override fun foo(n: Int) {}
override val bar: Int get() = 1
}
@@ -1,5 +1,11 @@
package test package test
actual open class <lineMarker><lineMarker>ExpectedChild</lineMarker></lineMarker> : SimpleParent() actual open class <lineMarker><lineMarker>ExpectedChild</lineMarker></lineMarker> : SimpleParent() {
actual override fun <lineMarker><lineMarker><lineMarker>foo</lineMarker></lineMarker></lineMarker>(n: Int) {}
actual override val <lineMarker><lineMarker><lineMarker>bar</lineMarker></lineMarker></lineMarker>: Int get() = 1
}
class ExpectedChildChildJvm : ExpectedChild() class ExpectedChildChildJvm : ExpectedChild() {
override fun <lineMarker>foo</lineMarker>(n: Int) {}
override val <lineMarker>bar</lineMarker>: Int get() = 1
}
@@ -0,0 +1,20 @@
package test
open class SimpleParent {
open fun foo(n: Int) {}
}
expect open class ExpectedChild : SimpleParent {
override fun <caret>foo(n: Int)
}
class ExpectedChildChild : ExpectedChild() {
override fun foo(n: Int) {}
}
class SimpleChild : SimpleParent() {
override fun foo(n: Int) {}
}
// REF: [common] (in test.ExpectedChildChild).foo(Int)
// REF: [jvm] (in test.ExpectedChildChildJvm).foo(Int)
@@ -0,0 +1,9 @@
package test
actual open class ExpectedChild : SimpleParent() {
actual override fun foo(n: Int) {}
}
class ExpectedChildChildJvm : ExpectedChild() {
override fun foo(n: Int) {}
}
@@ -0,0 +1,20 @@
package test
open class SimpleParent {
open val bar: Int get() = 1
}
expect open class ExpectedChild : SimpleParent {
override val <caret>bar: Int
}
class ExpectedChildChild : ExpectedChild() {
override val bar: Int get() = 1
}
class SimpleChild : SimpleParent() {
override val bar: Int get() = 1
}
// REF: [common] (in test.ExpectedChildChild).bar
// REF: [jvm] (in test.ExpectedChildChildJvm).bar
@@ -0,0 +1,9 @@
package test
actual open class ExpectedChild : SimpleParent() {
actual override val bar: Int get() = 1
}
class ExpectedChildChildJvm : ExpectedChild() {
override val bar: Int get() = 1
}
@@ -0,0 +1,23 @@
package test
open class SimpleParent {
open fun <caret>foo(n: Int) {}
}
expect open class ExpectedChild : SimpleParent {
override fun foo(n: Int)
}
class ExpectedChildChild : ExpectedChild() {
override fun foo(n: Int) {}
}
class SimpleChild : SimpleParent() {
override fun foo(n: Int) {}
}
// REF: [common] (in test.ExpectedChild).foo(Int)
// REF: [common] (in test.ExpectedChildChild).foo(Int)
// REF: [common] (in test.SimpleChild).foo(Int)
// REF: [jvm] (in test.ExpectedChild).foo(Int)
// REF: [jvm] (in test.ExpectedChildChildJvm).foo(Int)
@@ -0,0 +1,9 @@
package test
actual open class ExpectedChild : SimpleParent() {
actual override fun foo(n: Int) {}
}
class ExpectedChildChildJvm : ExpectedChild() {
override fun foo(n: Int) {}
}
@@ -0,0 +1,23 @@
package test
open class SimpleParent {
open val <caret>bar: Int get() = 1
}
expect open class ExpectedChild : SimpleParent {
override val bar: Int
}
class ExpectedChildChild : ExpectedChild() {
override val bar: Int get() = 1
}
class SimpleChild : SimpleParent() {
override val bar: Int get() = 1
}
// REF: [common] (in test.ExpectedChild).bar
// REF: [common] (in test.ExpectedChildChild).bar
// REF: [common] (in test.SimpleChild).bar
// REF: [jvm] (in test.ExpectedChild).bar
// REF: [jvm] (in test.ExpectedChildChildJvm).bar
@@ -0,0 +1,9 @@
package test
actual open class ExpectedChild : SimpleParent() {
actual override val bar: Int get() = 1
}
class ExpectedChildChildJvm : ExpectedChild() {
override val bar: Int get() = 1
}
@@ -72,7 +72,23 @@ class KotlinGotoImplementationMultiModuleTest : AbstractMultiModuleTest() {
doMultiPlatformTest("common.kt") doMultiPlatformTest("common.kt")
} }
fun testExpectClassSuperclassFun() {
doMultiPlatformTest("common.kt")
}
fun testExpectClassSuperclassProperty() {
doMultiPlatformTest("common.kt")
}
fun testExpectClass() { fun testExpectClass() {
doMultiPlatformTest("common.kt") doMultiPlatformTest("common.kt")
} }
fun testExpectClassFun() {
doMultiPlatformTest("common.kt")
}
fun testExpectClassProperty() {
doMultiPlatformTest("common.kt")
}
} }