Light classes support for declarations in multipart classes.

relates to #KT-39196
This commit is contained in:
Vladimir Ilmov
2020-09-07 15:03:45 +02:00
parent a5d91339b5
commit 3ea51a982c
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.asJava package org.jetbrains.kotlin.asJava
import com.intellij.openapi.project.Project
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.impl.java.stubs.PsiClassStub import com.intellij.psi.impl.java.stubs.PsiClassStub
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
@@ -27,11 +28,19 @@ import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.utils.checkWithAttachment import org.jetbrains.kotlin.utils.checkWithAttachment
object LightClassUtil { object LightClassUtil {
/**
* {@link kotlin.MetadataKt#kind} 4 = Multi-file class facade
*/
const val MULTI_FILE_CLASS_FACADE = 4
const val KOTLIN_METADATA_ANNOTATION = "kotlin.Metadata"
const val METADATA_KIND = "k"
const val METADATA_DATA1 = "d1"
fun findClass(stub: StubElement<*>, predicate: (PsiClassStub<*>) -> Boolean): PsiClass? { fun findClass(stub: StubElement<*>, predicate: (PsiClassStub<*>) -> Boolean): PsiClass? {
if (stub is PsiClassStub<*> && predicate(stub)) { if (stub is PsiClassStub<*> && predicate(stub)) {
@@ -170,20 +179,46 @@ object LightClassUtil {
private fun findFileFacade(ktFile: KtFile): PsiClass? { private fun findFileFacade(ktFile: KtFile): PsiClass? {
val fqName = ktFile.javaFileFacadeFqName val fqName = ktFile.javaFileFacadeFqName
val project = ktFile.project val project = ktFile.project
val classesWithMatchingFqName = val classesWithMatchingFqName = findPsiClass(project, fqName)
JavaElementFinder.getInstance(project).findClasses(fqName.asString(), GlobalSearchScope.allScope(project))
return classesWithMatchingFqName.singleOrNull() ?: classesWithMatchingFqName.find { return classesWithMatchingFqName.singleOrNull() ?: classesWithMatchingFqName.find {
it.containingFile?.virtualFile == ktFile.virtualFile it.containingFile?.virtualFile == ktFile.virtualFile
} }
} }
private fun findPsiClass(
project: Project,
fqName: FqName
): Array<PsiClass> =
JavaElementFinder.getInstance(project).findClasses(fqName.asString(), GlobalSearchScope.allScope(project))
private fun findMultifileClassMetadata(psiClass: PsiClass): List<String>? {
val metadata = psiClass.getAnnotation(KOTLIN_METADATA_ANNOTATION)
if (metadata != null) {
val k = (metadata.findAttributeValue(METADATA_KIND) as? PsiLiteralValue)?.value as Int
if (k == MULTI_FILE_CLASS_FACADE) {
val d1 = (metadata.findAttributeValue(METADATA_DATA1) as? PsiArrayInitializerMemberValue)?.initializers?.mapNotNull {
(it as? PsiLiteralValue)?.value as? String
} ?: emptyList()
return d1
}
}
return null
}
private fun getWrappingClasses(declaration: KtDeclaration): Sequence<PsiClass> { private fun getWrappingClasses(declaration: KtDeclaration): Sequence<PsiClass> {
val wrapperClass = getWrappingClass(declaration) ?: return emptySequence() val wrapperClass = getWrappingClass(declaration) ?: return emptySequence()
val wrapperClassOrigin = (wrapperClass as KtLightClass).kotlinOrigin val wrapperClassOrigin = (wrapperClass as KtLightClass).kotlinOrigin
if (wrapperClassOrigin is KtObjectDeclaration && wrapperClassOrigin.isCompanion() && wrapperClass.parent is PsiClass) { if (wrapperClassOrigin is KtObjectDeclaration && wrapperClassOrigin.isCompanion() && wrapperClass.parent is PsiClass) {
return sequenceOf(wrapperClass, wrapperClass.parent as PsiClass) return sequenceOf(wrapperClass, wrapperClass.parent as PsiClass)
} }
return sequenceOf(wrapperClass) val wrappers = mutableListOf<PsiClass>(wrapperClass);
// for multipart classes we need to add parts to wrappingClasses, otherwise the declarations there wouldn't be found in stubs
findMultifileClassMetadata(wrapperClass)?.let {
it.forEach {
wrappers.addAll(findPsiClass(declaration.project, FqName.fromSegments(it.split("/"))).toList())
}
}
return wrappers.asSequence()
} }
fun canGenerateLightClass(declaration: KtDeclaration): Boolean { fun canGenerateLightClass(declaration: KtDeclaration): Boolean {