diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/declarationsSearch/overridersSearch.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/declarationsSearch/overridersSearch.kt index 27bcf6c6ea5..a12c7056a95 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/declarationsSearch/overridersSearch.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/declarationsSearch/overridersSearch.kt @@ -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 { override fun nextElements(current: PsiMethod): Iterable = KotlinPsiMethodOverridersSearch.searchDirectOverriders(current) override fun shouldDescend(element: PsiMethod): Boolean = PsiUtil.canBeOverriden(element) } + +private fun forEachKotlinOverride( + ktClass: KtClass, + members: List, + 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() + + ktClass.primaryConstructorParameters.filter { it.hasValOrVar() } + forEachKotlinOverride(ktClass, members, scope, processor) +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt index 0b63ce06cda..18dbfc079e6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt @@ -254,18 +254,18 @@ private fun collectInheritedClassMarker(element: KtClass, result: MutableCollect private fun collectOverriddenPropertyAccessors(properties: Collection, result: MutableCollection>) { - val mappingToJava = HashMap() + val mappingToJava = HashMap() 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()) for (property in getOverriddenDeclarations(mappingToJava, classes)) { ProgressManager.checkCanceled() @@ -327,17 +327,18 @@ private fun collectHeaderMarkers(declaration: KtNamedDeclaration, } private fun collectOverriddenFunctions(functions: Collection, result: MutableCollection>) { - val mappingToJava = HashMap() + val mappingToJava = HashMap() 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()) for (function in getOverriddenDeclarations(mappingToJava, classes)) { ProgressManager.checkCanceled() diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/OverridenFunctionMarker.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/OverridenFunctionMarker.kt index 6386b5d0446..2f614b851da 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/OverridenFunctionMarker.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/OverridenFunctionMarker.kt @@ -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 getOverriddenDeclarations(mappingToJava: MutableMap, classes: Set): Set { +internal fun getOverriddenDeclarations(mappingToJava: MutableMap, classes: Set): Set { val overridden = HashSet() for (aClass in classes) { - AllOverridingMethodsSearch.search(aClass).forEach(object : Processor> { - override fun process(pair: Pair?): 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(2, THashSet()) 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() { + val processor = object : CommonProcessors.CollectProcessor() { 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) } } } diff --git a/idea/testData/multiModuleLineMarker/suspendImplInPlatformModules/common/common.kt b/idea/testData/multiModuleLineMarker/suspendImplInPlatformModules/common/common.kt new file mode 100644 index 00000000000..2103943d8e5 --- /dev/null +++ b/idea/testData/multiModuleLineMarker/suspendImplInPlatformModules/common/common.kt @@ -0,0 +1,3 @@ +interface I { + suspend fun foo(s: String) +} \ No newline at end of file diff --git a/idea/testData/multiModuleLineMarker/suspendImplInPlatformModules/js/js.kt b/idea/testData/multiModuleLineMarker/suspendImplInPlatformModules/js/js.kt new file mode 100644 index 00000000000..c459df7d0ea --- /dev/null +++ b/idea/testData/multiModuleLineMarker/suspendImplInPlatformModules/js/js.kt @@ -0,0 +1,7 @@ +// !CHECK_HIGHLIGHTING + +class KJs : I { + suspend override fun foo(s: String) { + + } +} \ No newline at end of file diff --git a/idea/testData/multiModuleLineMarker/suspendImplInPlatformModules/jvm/jvm.kt b/idea/testData/multiModuleLineMarker/suspendImplInPlatformModules/jvm/jvm.kt new file mode 100644 index 00000000000..fb674c18e46 --- /dev/null +++ b/idea/testData/multiModuleLineMarker/suspendImplInPlatformModules/jvm/jvm.kt @@ -0,0 +1,7 @@ +// !CHECK_HIGHLIGHTING + +class KJvm : I { + suspend override fun foo(s: String) { + + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTest.kt index 0008d6bcadf..6d186a24016 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTest.kt @@ -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) + } } \ No newline at end of file