Line Marking: Fix search of overriding methods in platform modules
#KT-19212 Fixed
This commit is contained in:
+66
@@ -17,7 +17,10 @@
|
||||
package org.jetbrains.kotlin.idea.search.declarationsSearch
|
||||
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.searches.AllOverridingMethodsSearch
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
||||
import com.intellij.psi.util.MethodSignatureUtil
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
import com.intellij.psi.util.TypeConversionUtil
|
||||
@@ -25,11 +28,25 @@ 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.toLightMethods
|
||||
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.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.core.isOverridable
|
||||
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.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import java.util.*
|
||||
|
||||
fun PsiElement.isOverridableElement(): Boolean = when (this) {
|
||||
@@ -98,3 +115,52 @@ object PsiMethodOverridingHierarchyTraverser: HierarchyTraverser<PsiMethod> {
|
||||
override fun nextElements(current: PsiMethod): Iterable<PsiMethod> = KotlinPsiMethodOverridersSearch.searchDirectOverriders(current)
|
||||
override fun shouldDescend(element: PsiMethod): Boolean = PsiUtil.canBeOverriden(element)
|
||||
}
|
||||
|
||||
private fun forEachKotlinOverride(
|
||||
ktClass: KtClass,
|
||||
members: List<KtNamedDeclaration>,
|
||||
scope: SearchScope,
|
||||
processor: (superMember: PsiElement, overridingMember: PsiElement) -> Boolean
|
||||
) {
|
||||
val baseClassDescriptor = runReadAction { ktClass.resolveToDescriptor() as ClassDescriptor }
|
||||
val baseDescriptors = runReadAction { members.mapNotNull { it.resolveToDescriptor() as? CallableMemberDescriptor }.filter { it.isOverridable } }
|
||||
if (baseDescriptors.isEmpty()) return
|
||||
|
||||
HierarchySearchRequest(ktClass, scope.restrictToKotlinSources(), true).searchInheritors().forEach {
|
||||
val inheritor = (it as? KtLightClass)?.kotlinOrigin ?: return@forEach
|
||||
val inheritorDescriptor = runReadAction { inheritor.resolveToDescriptor() as ClassDescriptor }
|
||||
val substitutor = getTypeSubstitutor(baseClassDescriptor.defaultType, inheritorDescriptor.defaultType) ?: return@forEach
|
||||
baseDescriptors.forEach {
|
||||
val superMember = it.source.getPsi()!!
|
||||
val overridingDescriptor = inheritorDescriptor.findCallableMemberBySignature(it.substitute(substitutor) as CallableMemberDescriptor)
|
||||
val overridingMember = overridingDescriptor?.source?.getPsi()
|
||||
if (overridingMember != null) {
|
||||
if (!processor(superMember, overridingMember)) return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun PsiMethod.forEachOverridingMethod(processor: (PsiMethod) -> Boolean) {
|
||||
val scope = runReadAction { useScope }
|
||||
|
||||
OverridingMethodsSearch.search(this, scope.excludeKotlinSources(), true).forEach(processor)
|
||||
|
||||
val ktMember = (this as? KtLightMethod)?.kotlinOrigin as? KtNamedDeclaration ?: return
|
||||
val ktClass = runReadAction { ktMember.containingClassOrObject as? KtClass } ?: return
|
||||
forEachKotlinOverride(ktClass, listOf(ktMember), scope) { _, overrider ->
|
||||
val lightMethods = runReadAction { overrider.toLightMethods() }
|
||||
lightMethods.all { processor(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun PsiClass.forEachDeclaredMemberOverride(processor: (superMember: PsiElement, overridingMember: PsiElement) -> Boolean) {
|
||||
val scope = runReadAction { useScope }
|
||||
|
||||
AllOverridingMethodsSearch.search(this, scope.excludeKotlinSources()).all { processor(it.first, it.second) }
|
||||
|
||||
val ktClass = (this as? KtLightClass)?.kotlinOrigin as? KtClass ?: return
|
||||
val members = ktClass.declarations.filterIsInstance<KtNamedDeclaration>() +
|
||||
ktClass.primaryConstructorParameters.filter { it.hasValOrVar() }
|
||||
forEachKotlinOverride(ktClass, members, scope, processor)
|
||||
}
|
||||
@@ -254,18 +254,18 @@ private fun collectInheritedClassMarker(element: KtClass, result: MutableCollect
|
||||
|
||||
private fun collectOverriddenPropertyAccessors(properties: Collection<KtNamedDeclaration>,
|
||||
result: MutableCollection<LineMarkerInfo<*>>) {
|
||||
val mappingToJava = HashMap<PsiMethod, KtNamedDeclaration>()
|
||||
val mappingToJava = HashMap<PsiElement, KtNamedDeclaration>()
|
||||
for (property in properties) {
|
||||
if (property.isOverridable()) {
|
||||
val accessorsPsiMethods = property.getAccessorLightMethods()
|
||||
|
||||
for (psiMethod in accessorsPsiMethods) {
|
||||
mappingToJava.put(psiMethod, property)
|
||||
}
|
||||
mappingToJava[property] = property
|
||||
}
|
||||
}
|
||||
|
||||
val classes = collectContainingClasses(mappingToJava.keys)
|
||||
val classes = collectContainingClasses(mappingToJava.keys.filterIsInstance<PsiMethod>())
|
||||
|
||||
for (property in getOverriddenDeclarations(mappingToJava, classes)) {
|
||||
ProgressManager.checkCanceled()
|
||||
@@ -327,17 +327,18 @@ private fun collectHeaderMarkers(declaration: KtNamedDeclaration,
|
||||
}
|
||||
|
||||
private fun collectOverriddenFunctions(functions: Collection<KtNamedFunction>, result: MutableCollection<LineMarkerInfo<*>>) {
|
||||
val mappingToJava = HashMap<PsiMethod, KtNamedFunction>()
|
||||
val mappingToJava = HashMap<PsiElement, KtNamedFunction>()
|
||||
for (function in functions) {
|
||||
if (function.isOverridable()) {
|
||||
val method = LightClassUtil.getLightClassMethod(function)
|
||||
if (method != null) {
|
||||
mappingToJava.put(method, function)
|
||||
}
|
||||
mappingToJava.put(function, function)
|
||||
}
|
||||
}
|
||||
|
||||
val classes = collectContainingClasses(mappingToJava.keys)
|
||||
val classes = collectContainingClasses(mappingToJava.keys.filterIsInstance<PsiMethod>())
|
||||
|
||||
for (function in getOverriddenDeclarations(mappingToJava, classes)) {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
@@ -24,21 +24,19 @@ import com.intellij.ide.util.PsiElementListCellRenderer
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.util.Pair
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.PsiElementProcessor
|
||||
import com.intellij.psi.search.PsiElementProcessorAdapter
|
||||
import com.intellij.psi.search.searches.AllOverridingMethodsSearch
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
import com.intellij.psi.util.PsiFormatUtil
|
||||
import com.intellij.util.CommonProcessors
|
||||
import com.intellij.util.Processor
|
||||
import gnu.trove.THashSet
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
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.forEachOverridingMethod
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import java.awt.event.MouseEvent
|
||||
import java.util.*
|
||||
import javax.swing.JComponent
|
||||
@@ -47,26 +45,21 @@ private fun PsiMethod.isMethodWithDeclarationInOtherClass(): Boolean {
|
||||
return this is KtLightMethod && this.isTraitFakeOverride()
|
||||
}
|
||||
|
||||
internal fun <T> getOverriddenDeclarations(mappingToJava: MutableMap<PsiMethod, T>, classes: Set<PsiClass>): Set<T> {
|
||||
internal fun <T> getOverriddenDeclarations(mappingToJava: MutableMap<PsiElement, T>, classes: Set<PsiClass>): Set<T> {
|
||||
val overridden = HashSet<T>()
|
||||
for (aClass in classes) {
|
||||
AllOverridingMethodsSearch.search(aClass).forEach(object : Processor<Pair<PsiMethod, PsiMethod>> {
|
||||
override fun process(pair: Pair<PsiMethod, PsiMethod>?): Boolean {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
if (!pair!!.getSecond().isMethodWithDeclarationInOtherClass()) {
|
||||
val superMethod = pair.getFirst()
|
||||
|
||||
val declaration = mappingToJava.get(superMethod)
|
||||
if (declaration != null) {
|
||||
mappingToJava.remove(superMethod)
|
||||
overridden.add(declaration)
|
||||
}
|
||||
aClass.forEachDeclaredMemberOverride { superMember, overridingMember ->
|
||||
ProgressManager.checkCanceled()
|
||||
if (overridingMember.toLightMethods().any { !it.isMethodWithDeclarationInOtherClass() }) {
|
||||
val declaration = mappingToJava[superMember]
|
||||
if (declaration != null) {
|
||||
mappingToJava.remove(superMember)
|
||||
overridden.add(declaration)
|
||||
}
|
||||
|
||||
return !mappingToJava.isEmpty()
|
||||
}
|
||||
})
|
||||
|
||||
!mappingToJava.isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
return overridden
|
||||
@@ -103,7 +96,11 @@ fun buildNavigateToOverriddenMethodPopup(e: MouseEvent?, element: PsiElement?):
|
||||
val processor = PsiElementProcessor.CollectElementsWithLimit<PsiMethod>(2, THashSet<PsiMethod>())
|
||||
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
{
|
||||
OverridingMethodsSearch.search(method, true).forEach(PsiElementProcessorAdapter(processor))
|
||||
method.forEachOverridingMethod {
|
||||
runReadAction {
|
||||
processor.execute(it)
|
||||
}
|
||||
}
|
||||
},
|
||||
"Searching for overriding declarations", true, method.project, e?.component as JComponent?)) {
|
||||
return null
|
||||
@@ -112,7 +109,13 @@ fun buildNavigateToOverriddenMethodPopup(e: MouseEvent?, element: PsiElement?):
|
||||
var overridingJavaMethods = processor.collection.filter { !it.isMethodWithDeclarationInOtherClass() }
|
||||
if (overridingJavaMethods.isEmpty()) return null
|
||||
|
||||
val showMethodNames = !PsiUtil.allMethodsHaveSameSignature(overridingJavaMethods.toTypedArray())
|
||||
val renderedSignatures = overridingJavaMethods.map {
|
||||
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)
|
||||
@@ -138,7 +141,7 @@ private class OverridingMethodsUpdater(
|
||||
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
super.run(indicator)
|
||||
OverridingMethodsSearch.search(myMethod, true).forEach(object : CommonProcessors.CollectProcessor<PsiMethod>() {
|
||||
val processor = object : CommonProcessors.CollectProcessor<PsiMethod>() {
|
||||
override fun process(psiMethod: PsiMethod?): Boolean {
|
||||
if (!updateComponent(psiMethod, myRenderer.comparator)) {
|
||||
indicator.cancel()
|
||||
@@ -146,6 +149,7 @@ private class OverridingMethodsUpdater(
|
||||
indicator.checkCanceled()
|
||||
return super.process(psiMethod)
|
||||
}
|
||||
})
|
||||
}
|
||||
myMethod.forEachOverridingMethod { processor.process(it) }
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
interface <lineMarker>I</lineMarker> {
|
||||
suspend fun <lineMarker descr="<html><body>Is implemented in <br> KJs<br> KJvm</body></html>">foo</lineMarker>(s: String)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// !CHECK_HIGHLIGHTING
|
||||
|
||||
class KJs : I {
|
||||
suspend override fun foo(s: String) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// !CHECK_HIGHLIGHTING
|
||||
|
||||
class KJvm : I {
|
||||
suspend override fun foo(s: String) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -45,4 +45,8 @@ class MultiModuleLineMarkerTest : AbstractMultiModuleHighlightingTest() {
|
||||
fun testWithOverloads() {
|
||||
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
|
||||
}
|
||||
|
||||
fun testSuspendImplInPlatformModules() {
|
||||
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], TargetPlatformKind.JavaScript)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user