Light classes: fix multifile facades for mpp modules with jvm part
When creating multifile facades respect that facade files can be both
in common and jvm parts
#KT-23942 Fixed
This commit is contained in:
committed by
Pavel V. Talanov
parent
a8a490ea5a
commit
ced3cf0904
@@ -400,9 +400,9 @@ interface SourceForBinaryModuleInfo : IdeaModuleInfo {
|
||||
get() = ModuleOrigin.OTHER
|
||||
}
|
||||
|
||||
class PlatformModuleInfo(
|
||||
data class PlatformModuleInfo(
|
||||
internal val platformModule: ModuleSourceInfo,
|
||||
private val commonModules: List<ModuleSourceInfo>
|
||||
private val commonModules: List<ModuleSourceInfo> // NOTE: usually contains a single element for current implementation
|
||||
) : IdeaModuleInfo, CombinedModuleInfo, TrackableModuleInfo {
|
||||
override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?>
|
||||
get() = platformModule.capabilities
|
||||
|
||||
@@ -11,12 +11,14 @@ import com.intellij.openapi.externalSystem.service.project.IdeModelsProviderImpl
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.roots.ProjectRootModificationTracker
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetType
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetType.Companion.ID
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
|
||||
val Module.implementingModules: List<Module>
|
||||
get() = cached(CachedValueProvider {
|
||||
@@ -72,3 +74,20 @@ val ModuleDescriptor.implementedDescriptors: List<ModuleDescriptor>
|
||||
|
||||
private fun ModuleSourceInfo.toDescriptor() = KotlinCacheService.getInstance(module.project)
|
||||
.getResolutionFacadeByModuleInfo(this, platform)?.moduleDescriptor
|
||||
|
||||
fun PsiElement.getPlatformModuleInfo(desiredPlatform: TargetPlatform): PlatformModuleInfo? {
|
||||
assert(desiredPlatform != TargetPlatform.Common) { "Platform module cannot have Common platform" }
|
||||
val moduleInfo = getModuleInfo() as? ModuleSourceInfo ?: return null
|
||||
return when (moduleInfo.platform) {
|
||||
TargetPlatform.Common -> {
|
||||
val correspondingImplementingModule = moduleInfo.module.implementingModules.map { it.toInfo(moduleInfo.isTests()) }
|
||||
.firstOrNull { it?.platform == desiredPlatform } ?: return null
|
||||
PlatformModuleInfo(correspondingImplementingModule, correspondingImplementingModule.expectedBy)
|
||||
}
|
||||
desiredPlatform -> {
|
||||
val expectedBy = moduleInfo.expectedBy.takeIf { it.isNotEmpty() } ?: return null
|
||||
PlatformModuleInfo(moduleInfo, expectedBy)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
+38
-18
@@ -19,16 +19,17 @@ import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
|
||||
import org.jetbrains.kotlin.idea.caches.lightClasses.ClsJavaStubByVirtualFileCache
|
||||
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
|
||||
import org.jetbrains.kotlin.idea.caches.lightClasses.platformMutabilityWrapper
|
||||
import org.jetbrains.kotlin.idea.caches.project.*
|
||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
|
||||
import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper
|
||||
import org.jetbrains.kotlin.idea.project.platform
|
||||
import org.jetbrains.kotlin.idea.stubindex.*
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import java.util.*
|
||||
@@ -47,10 +48,10 @@ class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport(
|
||||
override fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
|
||||
val facadeFilesInPackage = runReadAction {
|
||||
KotlinFileFacadeClassByPackageIndex.getInstance()
|
||||
.get(packageFqName.asString(), project, scope)
|
||||
.get(packageFqName.asString(), project, scope).platformSourcesFirst()
|
||||
}
|
||||
val groupedByFqNameAndModuleInfo = facadeFilesInPackage.groupBy {
|
||||
Pair(it.javaFileFacadeFqName, it.getModuleInfo())
|
||||
Pair(it.javaFileFacadeFqName, it.getModuleInfoPreferringJvmPlatform())
|
||||
}
|
||||
|
||||
return groupedByFqNameAndModuleInfo.flatMap {
|
||||
@@ -141,11 +142,9 @@ class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport(
|
||||
KtLightClassForScript.create(script)
|
||||
|
||||
private fun withFakeLightClasses(
|
||||
lightClassForFacade: KtLightClassForFacade?,
|
||||
lightClassForFacade: KtLightClassForFacade,
|
||||
facadeFiles: List<KtFile>
|
||||
): List<PsiClass> {
|
||||
if (lightClassForFacade == null) return emptyList()
|
||||
|
||||
val lightClasses = ArrayList<PsiClass>()
|
||||
lightClasses.add(lightClassForFacade)
|
||||
if (facadeFiles.size > 1) {
|
||||
@@ -157,7 +156,7 @@ class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport(
|
||||
}
|
||||
|
||||
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
|
||||
val filesByModule = findFilesForFacade(facadeFqName, scope).groupBy(KtFile::getModuleInfo)
|
||||
val filesByModule = findFilesForFacade(facadeFqName, scope).groupBy(PsiElement::getModuleInfoPreferringJvmPlatform)
|
||||
|
||||
return filesByModule.flatMap {
|
||||
createLightClassForFileFacade(facadeFqName, it.value, it.key)
|
||||
@@ -209,23 +208,40 @@ class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport(
|
||||
moduleInfo: IdeaModuleInfo
|
||||
): List<PsiClass> {
|
||||
val (clsFiles, sourceFiles) = facadeFiles.partition { it is KtClsFile }
|
||||
val lightClassesForClsFacades = clsFiles.mapNotNull { createLightClassForDecompiledKotlinFile(it as KtClsFile) }
|
||||
if (moduleInfo is ModuleSourceInfo && sourceFiles.isNotEmpty()) {
|
||||
val lightClassForFacade = KtLightClassForFacade.createForFacade(
|
||||
psiManager, facadeFqName, moduleInfo.contentScope(), sourceFiles
|
||||
)
|
||||
return withFakeLightClasses(lightClassForFacade, sourceFiles) + lightClassesForClsFacades
|
||||
} else {
|
||||
return lightClassesForClsFacades
|
||||
}
|
||||
val facadesFromCls = clsFiles.mapNotNull { createLightClassForDecompiledKotlinFile(it as KtClsFile) }
|
||||
val facadesFromSources = createFacadesForSourceFiles(moduleInfo, sourceFiles, facadeFqName)
|
||||
return facadesFromSources + facadesFromCls
|
||||
}
|
||||
|
||||
private fun createFacadesForSourceFiles(
|
||||
moduleInfo: IdeaModuleInfo,
|
||||
sourceFiles: List<KtFile>,
|
||||
facadeFqName: FqName
|
||||
): List<PsiClass> {
|
||||
if (sourceFiles.isEmpty()) return listOf()
|
||||
if (moduleInfo !is ModuleSourceInfo && moduleInfo !is PlatformModuleInfo) return listOf()
|
||||
|
||||
val lightClassForFacade = KtLightClassForFacade.createForFacade(
|
||||
psiManager, facadeFqName, moduleInfo.contentScope(), sourceFiles
|
||||
)
|
||||
return withFakeLightClasses(lightClassForFacade, sourceFiles)
|
||||
}
|
||||
|
||||
override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile> {
|
||||
return runReadAction {
|
||||
KotlinFileFacadeFqNameIndex.INSTANCE.get(facadeFqName.asString(), project, scope)
|
||||
KotlinFileFacadeFqNameIndex.INSTANCE.get(facadeFqName.asString(), project, scope).platformSourcesFirst()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NOTE: this is a hacky solution to the following problem:
|
||||
// when building this light class resolver will be built by the first file in the list
|
||||
// (we could assume that files are in the same module before)
|
||||
// thus we need to ensure that resolver will be built by the file from platform part of the module
|
||||
// (resolver built by a file from the common part will have no knowledge of the platform part)
|
||||
// the actual of order of files that resolver receives is controlled by *findFilesForFacade* method
|
||||
private fun Collection<KtFile>.platformSourcesFirst() = sortedByDescending { it.platform == JvmPlatform }
|
||||
|
||||
private fun getLightClassForDecompiledClassOrObject(decompiledClassOrObject: KtClassOrObject): KtLightClassForDecompiledDeclaration? {
|
||||
if (decompiledClassOrObject is KtEnumEntry) {
|
||||
return null
|
||||
@@ -307,4 +323,8 @@ class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport(
|
||||
javaFileStub.psi = fakeFile
|
||||
return fakeFile.classes.single() as ClsClassImpl
|
||||
}
|
||||
}
|
||||
|
||||
internal fun PsiElement.getModuleInfoPreferringJvmPlatform(): IdeaModuleInfo {
|
||||
return getPlatformModuleInfo(JvmPlatform) ?: getModuleInfo()
|
||||
}
|
||||
+1
-1
@@ -29,7 +29,7 @@ class KtFileClassProviderImpl(val kotlinAsJavaSupport: KotlinAsJavaSupport) :
|
||||
val result = arrayListOf<PsiClass>()
|
||||
file.declarations.filterIsInstance<KtClassOrObject>().map { it.toLightClass() }.filterNotNullTo(result)
|
||||
|
||||
val moduleInfo = file.getModuleInfo()
|
||||
val moduleInfo = file.getModuleInfoPreferringJvmPlatform()
|
||||
val jvmClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(file)
|
||||
val fileClassFqName = file.javaFileFacadeFqName
|
||||
|
||||
|
||||
Reference in New Issue
Block a user