Misc: Support light-classless classes in inheritors search
In particular, support line markers for expect-classes and navigation to expect-classes from their base class #KT-20329 Fixed
This commit is contained in:
+3
-3
@@ -91,7 +91,7 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC
|
|||||||
|
|
||||||
private fun getJavaFileStub(): PsiJavaFileStub = getLightClassDataHolder().javaFileStub
|
private fun getJavaFileStub(): PsiJavaFileStub = getLightClassDataHolder().javaFileStub
|
||||||
|
|
||||||
protected fun getDescriptor(): ClassDescriptor? {
|
fun getDescriptor(): ClassDescriptor? {
|
||||||
return LightClassGenerationSupport.getInstance(project).resolveToDescriptor(classOrObject) as? ClassDescriptor
|
return LightClassGenerationSupport.getInstance(project).resolveToDescriptor(classOrObject) as? ClassDescriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,13 +481,13 @@ fun getOutermostClassOrObject(classOrObject: KtClassOrObject): KtClassOrObject {
|
|||||||
|
|
||||||
interface LightClassInheritanceHelper {
|
interface LightClassInheritanceHelper {
|
||||||
fun isInheritor(
|
fun isInheritor(
|
||||||
lightClass: KtLightClassForSourceDeclaration,
|
lightClass: KtLightClass,
|
||||||
baseClass: PsiClass,
|
baseClass: PsiClass,
|
||||||
checkDeep: Boolean
|
checkDeep: Boolean
|
||||||
): ImpreciseResolveResult
|
): ImpreciseResolveResult
|
||||||
|
|
||||||
object NoHelp : LightClassInheritanceHelper {
|
object NoHelp : LightClassInheritanceHelper {
|
||||||
override fun isInheritor(lightClass: KtLightClassForSourceDeclaration, baseClass: PsiClass, checkDeep: Boolean) = UNSURE
|
override fun isInheritor(lightClass: KtLightClass, baseClass: PsiClass, checkDeep: Boolean) = UNSURE
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
+6
-4
@@ -20,19 +20,21 @@ import com.intellij.psi.CommonClassNames
|
|||||||
import com.intellij.psi.PsiClass
|
import com.intellij.psi.PsiClass
|
||||||
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult
|
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult
|
||||||
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.*
|
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.*
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
|
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.classes.defaultJavaAncestorQualifiedName
|
import org.jetbrains.kotlin.asJava.classes.defaultJavaAncestorQualifiedName
|
||||||
import org.jetbrains.kotlin.idea.search.PsiBasedClassResolver
|
import org.jetbrains.kotlin.idea.search.PsiBasedClassResolver
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry
|
||||||
|
import org.jetbrains.kotlin.psi.KtSuperTypeListEntry
|
||||||
|
|
||||||
class IdeLightClassInheritanceHelper : LightClassInheritanceHelper {
|
class IdeLightClassInheritanceHelper : LightClassInheritanceHelper {
|
||||||
override fun isInheritor(
|
override fun isInheritor(
|
||||||
lightClass: KtLightClassForSourceDeclaration,
|
lightClass: KtLightClass,
|
||||||
baseClass: PsiClass,
|
baseClass: PsiClass,
|
||||||
checkDeep: Boolean
|
checkDeep: Boolean
|
||||||
): ImpreciseResolveResult {
|
): ImpreciseResolveResult {
|
||||||
val classOrObject = lightClass.kotlinOrigin
|
val classOrObject = lightClass.kotlinOrigin ?: return UNSURE
|
||||||
val entries = classOrObject.superTypeListEntries
|
val entries = classOrObject.superTypeListEntries
|
||||||
val hasSuperClass = entries.any { it is KtSuperTypeCallEntry }
|
val hasSuperClass = entries.any { it is KtSuperTypeCallEntry }
|
||||||
if (baseClass.qualifiedName == classOrObject.defaultJavaAncestorQualifiedName() && (!hasSuperClass || checkDeep)) {
|
if (baseClass.qualifiedName == classOrObject.defaultJavaAncestorQualifiedName() && (!hasSuperClass || checkDeep)) {
|
||||||
|
|||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.caches.resolve.lightClasses
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiClass
|
||||||
|
import com.intellij.psi.PsiElementFactory
|
||||||
|
import com.intellij.psi.impl.light.AbstractLightClass
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.LightClassInheritanceHelper
|
||||||
|
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.psiUtil.containingClassOrObject
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
|
||||||
|
// Used as a placeholder when actual light class does not exist (expect-classes, for example)
|
||||||
|
// The main purpose is to allow search of inheritors within hierarchies containing such classes
|
||||||
|
class KtFakeLightClass(override val kotlinOrigin: KtClassOrObject) :
|
||||||
|
AbstractLightClass(kotlinOrigin.manager, KotlinLanguage.INSTANCE),
|
||||||
|
KtLightClass {
|
||||||
|
private val _delegate by lazy { PsiElementFactory.SERVICE.getInstance(kotlinOrigin.project).createClass(kotlinOrigin.name ?: "") }
|
||||||
|
private val _containingClass by lazy { kotlinOrigin.containingClassOrObject?.let { KtFakeLightClass(it) } }
|
||||||
|
|
||||||
|
override val clsDelegate get() = _delegate
|
||||||
|
override val originKind get() = LightClassOriginKind.SOURCE
|
||||||
|
|
||||||
|
override fun getDelegate() = _delegate
|
||||||
|
override fun copy() = KtFakeLightClass(kotlinOrigin)
|
||||||
|
|
||||||
|
override fun getQualifiedName() = kotlinOrigin.fqName?.asString()
|
||||||
|
override fun getContainingClass() = _containingClass
|
||||||
|
override fun getNavigationElement() = kotlinOrigin
|
||||||
|
override fun getIcon(flags: Int) = kotlinOrigin.getIcon(flags)
|
||||||
|
override fun getContainingFile() = kotlinOrigin.containingFile
|
||||||
|
override fun getUseScope() = kotlinOrigin.useScope
|
||||||
|
|
||||||
|
override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean {
|
||||||
|
LightClassInheritanceHelper.getService(project).isInheritor(this, baseClass, checkDeep).ifSure { return it }
|
||||||
|
|
||||||
|
val baseKtClass = (baseClass as? KtLightClass)?.kotlinOrigin ?: return false
|
||||||
|
val baseDescriptor = baseKtClass.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ 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.LightClassUtil
|
||||||
import org.jetbrains.kotlin.asJava.toLightClass
|
import org.jetbrains.kotlin.asJava.toLightClass
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightClass
|
||||||
import org.jetbrains.kotlin.psi.KtClass
|
import org.jetbrains.kotlin.psi.KtClass
|
||||||
import org.jetbrains.kotlin.psi.KtFunction
|
import org.jetbrains.kotlin.psi.KtFunction
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
@@ -42,12 +43,12 @@ fun collectContainingClasses(methods: Collection<PsiMethod>): Set<PsiClass> {
|
|||||||
return classes
|
return classes
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getPsiClass(element: PsiElement?): PsiClass? {
|
internal tailrec fun getPsiClass(element: PsiElement?): PsiClass? {
|
||||||
return when {
|
return when {
|
||||||
element == null -> null
|
element == null -> null
|
||||||
element is PsiClass -> element
|
element is PsiClass -> element
|
||||||
element is KtClass -> element.toLightClass()
|
element is KtClass -> element.toLightClass() ?: KtFakeLightClass(element)
|
||||||
element.parent is KtClass -> (element.parent as KtClass).toLightClass()
|
element.parent is KtClass -> getPsiClass(element.parent)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.descriptors.MemberDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.Modality
|
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.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
|
||||||
@@ -248,7 +249,7 @@ private fun collectInheritedClassMarker(element: KtClass, result: MutableCollect
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val lightClass = element.toLightClass() ?: return
|
val lightClass = element.toLightClass() ?: KtFakeLightClass(element)
|
||||||
|
|
||||||
if (ClassInheritorsSearch.search(lightClass, false).findFirst() == null) return
|
if (ClassInheritorsSearch.search(lightClass, false).findFirst() == null) return
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -33,6 +33,7 @@ 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.search.declarationsSearch.forEachImplementation
|
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachImplementation
|
||||||
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.*
|
||||||
@@ -88,7 +89,7 @@ class KotlinDefinitionsSearcher : QueryExecutor<PsiElement, DefinitionsScopedSea
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun processClassImplementations(klass: KtClass, consumer: Processor<PsiElement>): Boolean {
|
private fun processClassImplementations(klass: KtClass, consumer: Processor<PsiElement>): Boolean {
|
||||||
val psiClass = runReadAction { klass.toLightClass() } ?: return true
|
val psiClass = runReadAction { klass.toLightClass() ?: KtFakeLightClass(klass) }
|
||||||
|
|
||||||
val searchScope = runReadAction { psiClass.useScope }
|
val searchScope = runReadAction { psiClass.useScope }
|
||||||
if (searchScope is LocalSearchScope) {
|
if (searchScope is LocalSearchScope) {
|
||||||
|
|||||||
+6
-5
@@ -22,7 +22,8 @@ import com.intellij.psi.PsiClass
|
|||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch
|
import com.intellij.psi.search.searches.DirectClassInheritorsSearch
|
||||||
import com.intellij.util.Processor
|
import com.intellij.util.Processor
|
||||||
import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper
|
import org.jetbrains.kotlin.asJava.toLightClassWithBuiltinMapping
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtFakeLightClass
|
||||||
import org.jetbrains.kotlin.idea.search.fileScope
|
import org.jetbrains.kotlin.idea.search.fileScope
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSuperClassIndex
|
import org.jetbrains.kotlin.idea.stubindex.KotlinSuperClassIndex
|
||||||
@@ -35,10 +36,10 @@ open class KotlinDirectInheritorsSearcher : QueryExecutorBase<PsiClass, DirectCl
|
|||||||
|
|
||||||
val name = baseClass.name ?: return
|
val name = baseClass.name ?: return
|
||||||
|
|
||||||
val originalScope = queryParameters.scope
|
val file = if (baseClass is KtFakeLightClass) baseClass.kotlinOrigin.containingFile else baseClass.containingFile
|
||||||
val scope = originalScope as? GlobalSearchScope ?: baseClass.containingFile?.fileScope() ?: return
|
|
||||||
|
|
||||||
val file = baseClass.containingFile
|
val originalScope = queryParameters.scope
|
||||||
|
val scope = originalScope as? GlobalSearchScope ?: file.fileScope() ?: return
|
||||||
|
|
||||||
val names = mutableSetOf(name)
|
val names = mutableSetOf(name)
|
||||||
val project = file.project
|
val project = file.project
|
||||||
@@ -63,7 +64,7 @@ open class KotlinDirectInheritorsSearcher : QueryExecutorBase<PsiClass, DirectCl
|
|||||||
names.forEach { name ->
|
names.forEach { name ->
|
||||||
KotlinSuperClassIndex.getInstance()
|
KotlinSuperClassIndex.getInstance()
|
||||||
.get(name, baseClass.project, noLibrarySourceScope).asSequence()
|
.get(name, baseClass.project, noLibrarySourceScope).asSequence()
|
||||||
.mapNotNull { candidate -> SourceNavigationHelper.getOriginalPsiClassOrCreateLightClass(candidate) }
|
.mapNotNull { candidate -> candidate.toLightClassWithBuiltinMapping() ?: KtFakeLightClass(candidate) }
|
||||||
.filter { candidate -> candidate.isInheritor(baseClass, false) }
|
.filter { candidate -> candidate.isInheritor(baseClass, false) }
|
||||||
.forEach { candidate -> consumer.process(candidate) }
|
.forEach { candidate -> consumer.process(candidate) }
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
open class <lineMarker>SimpleParent</lineMarker>
|
||||||
|
|
||||||
|
expect open class <lineMarker><lineMarker>ExpectedChild</lineMarker></lineMarker> : SimpleParent
|
||||||
|
|
||||||
|
class ExpectedChildChild : ExpectedChild()
|
||||||
|
|
||||||
|
class SimpleChild : SimpleParent()
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// !CHECK_HIGHLIGHTING
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
actual open class ExpectedChild : SimpleParent()
|
||||||
|
|
||||||
|
class ExpectedChildChildJvm : ExpectedChild()
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// !CHECK_HIGHLIGHTING
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
open class <caret>SimpleParent
|
||||||
|
|
||||||
|
expect open class ExpectedChild : SimpleParent
|
||||||
|
|
||||||
|
class ExpectedChildChild : ExpectedChild()
|
||||||
|
|
||||||
|
class SimpleChild : SimpleParent()
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
actual open class <lineMarker><lineMarker>ExpectedChild</lineMarker></lineMarker> : SimpleParent()
|
||||||
|
|
||||||
|
class ExpectedChildChildJvm : ExpectedChild()
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
open class SimpleParent
|
||||||
|
|
||||||
|
expect open class <caret>ExpectedChild : SimpleParent
|
||||||
|
|
||||||
|
class ExpectedChildChild : ExpectedChild()
|
||||||
|
|
||||||
|
class SimpleChild : SimpleParent()
|
||||||
|
|
||||||
|
// REF: [common] (test).ExpectedChildChild
|
||||||
|
// REF: [jvm] (test).ExpectedChildChildJvm
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
actual open class ExpectedChild : SimpleParent()
|
||||||
|
|
||||||
|
class ExpectedChildChildJvm : ExpectedChild()
|
||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
open class <caret>SimpleParent
|
||||||
|
|
||||||
|
expect open class ExpectedChild : SimpleParent
|
||||||
|
|
||||||
|
class ExpectedChildChild : ExpectedChild()
|
||||||
|
|
||||||
|
class SimpleChild : SimpleParent()
|
||||||
|
|
||||||
|
// REF: [common] (test).ExpectedChild
|
||||||
|
// REF: [common] (test).ExpectedChild
|
||||||
|
// REF: [common] (test).ExpectedChildChild
|
||||||
|
// REF: [common] (test).SimpleChild
|
||||||
|
// REF: [jvm] (test).ExpectedChild
|
||||||
|
// REF: [jvm] (test).ExpectedChildChildJvm
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
actual open class ExpectedChild : SimpleParent()
|
||||||
|
|
||||||
|
class ExpectedChildChildJvm : ExpectedChild()
|
||||||
@@ -86,6 +86,14 @@ class MultiModuleLineMarkerTest : AbstractMultiModuleHighlightingTest() {
|
|||||||
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
|
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testHierarchyWithExpectClassCommonSide() {
|
||||||
|
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testHierarchyWithExpectClassPlatformSide() {
|
||||||
|
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
|
||||||
|
}
|
||||||
|
|
||||||
fun testKotlinTestAnnotations() {
|
fun testKotlinTestAnnotations() {
|
||||||
doMultiPlatformTest(TargetPlatformKind.JavaScript,
|
doMultiPlatformTest(TargetPlatformKind.JavaScript,
|
||||||
configureModule = { module, _ ->
|
configureModule = { module, _ ->
|
||||||
|
|||||||
+8
@@ -67,4 +67,12 @@ class KotlinGotoImplementationMultiModuleTest : AbstractMultiModuleTest() {
|
|||||||
actuals = *arrayOf("jvm" to TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], "js" to TargetPlatformKind.JavaScript)
|
actuals = *arrayOf("jvm" to TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], "js" to TargetPlatformKind.JavaScript)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testExpectClassSuperclass() {
|
||||||
|
doMultiPlatformTest("common.kt")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testExpectClass() {
|
||||||
|
doMultiPlatformTest("common.kt")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user