Line Marking: Fix search of overriding methods in platform modules

#KT-19212 Fixed
This commit is contained in:
Alexey Sedunov
2017-08-03 18:34:27 +03:00
parent 98eb990f30
commit 386b1fc002
7 changed files with 125 additions and 33 deletions
@@ -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) }
}
}
@@ -0,0 +1,3 @@
interface <lineMarker>I</lineMarker> {
suspend fun <lineMarker descr="<html><body>Is implemented in <br>&nbsp;&nbsp;&nbsp;&nbsp;KJs<br>&nbsp;&nbsp;&nbsp;&nbsp;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)
}
}