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
|
get() = ModuleOrigin.OTHER
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlatformModuleInfo(
|
data class PlatformModuleInfo(
|
||||||
internal val platformModule: ModuleSourceInfo,
|
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 {
|
) : IdeaModuleInfo, CombinedModuleInfo, TrackableModuleInfo {
|
||||||
override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?>
|
override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?>
|
||||||
get() = platformModule.capabilities
|
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.Module
|
||||||
import com.intellij.openapi.module.ModuleManager
|
import com.intellij.openapi.module.ModuleManager
|
||||||
import com.intellij.openapi.roots.ProjectRootModificationTracker
|
import com.intellij.openapi.roots.ProjectRootModificationTracker
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.util.CachedValueProvider
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetType
|
import org.jetbrains.kotlin.idea.facet.KotlinFacetType
|
||||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetType.Companion.ID
|
import org.jetbrains.kotlin.idea.facet.KotlinFacetType.Companion.ID
|
||||||
|
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||||
|
|
||||||
val Module.implementingModules: List<Module>
|
val Module.implementingModules: List<Module>
|
||||||
get() = cached(CachedValueProvider {
|
get() = cached(CachedValueProvider {
|
||||||
@@ -72,3 +74,20 @@ val ModuleDescriptor.implementedDescriptors: List<ModuleDescriptor>
|
|||||||
|
|
||||||
private fun ModuleSourceInfo.toDescriptor() = KotlinCacheService.getInstance(module.project)
|
private fun ModuleSourceInfo.toDescriptor() = KotlinCacheService.getInstance(module.project)
|
||||||
.getResolutionFacadeByModuleInfo(this, platform)?.moduleDescriptor
|
.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.ClsJavaStubByVirtualFileCache
|
||||||
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
|
import org.jetbrains.kotlin.idea.caches.lightClasses.KtLightClassForDecompiledDeclaration
|
||||||
import org.jetbrains.kotlin.idea.caches.lightClasses.platformMutabilityWrapper
|
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.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.classFile.KtClsFile
|
||||||
import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper
|
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.stubindex.*
|
||||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
import org.jetbrains.kotlin.utils.sure
|
import org.jetbrains.kotlin.utils.sure
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -47,10 +48,10 @@ class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport(
|
|||||||
override fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
|
override fun getFacadeClassesInPackage(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
|
||||||
val facadeFilesInPackage = runReadAction {
|
val facadeFilesInPackage = runReadAction {
|
||||||
KotlinFileFacadeClassByPackageIndex.getInstance()
|
KotlinFileFacadeClassByPackageIndex.getInstance()
|
||||||
.get(packageFqName.asString(), project, scope)
|
.get(packageFqName.asString(), project, scope).platformSourcesFirst()
|
||||||
}
|
}
|
||||||
val groupedByFqNameAndModuleInfo = facadeFilesInPackage.groupBy {
|
val groupedByFqNameAndModuleInfo = facadeFilesInPackage.groupBy {
|
||||||
Pair(it.javaFileFacadeFqName, it.getModuleInfo())
|
Pair(it.javaFileFacadeFqName, it.getModuleInfoPreferringJvmPlatform())
|
||||||
}
|
}
|
||||||
|
|
||||||
return groupedByFqNameAndModuleInfo.flatMap {
|
return groupedByFqNameAndModuleInfo.flatMap {
|
||||||
@@ -141,11 +142,9 @@ class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport(
|
|||||||
KtLightClassForScript.create(script)
|
KtLightClassForScript.create(script)
|
||||||
|
|
||||||
private fun withFakeLightClasses(
|
private fun withFakeLightClasses(
|
||||||
lightClassForFacade: KtLightClassForFacade?,
|
lightClassForFacade: KtLightClassForFacade,
|
||||||
facadeFiles: List<KtFile>
|
facadeFiles: List<KtFile>
|
||||||
): List<PsiClass> {
|
): List<PsiClass> {
|
||||||
if (lightClassForFacade == null) return emptyList()
|
|
||||||
|
|
||||||
val lightClasses = ArrayList<PsiClass>()
|
val lightClasses = ArrayList<PsiClass>()
|
||||||
lightClasses.add(lightClassForFacade)
|
lightClasses.add(lightClassForFacade)
|
||||||
if (facadeFiles.size > 1) {
|
if (facadeFiles.size > 1) {
|
||||||
@@ -157,7 +156,7 @@ class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
|
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 {
|
return filesByModule.flatMap {
|
||||||
createLightClassForFileFacade(facadeFqName, it.value, it.key)
|
createLightClassForFileFacade(facadeFqName, it.value, it.key)
|
||||||
@@ -209,23 +208,40 @@ class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport(
|
|||||||
moduleInfo: IdeaModuleInfo
|
moduleInfo: IdeaModuleInfo
|
||||||
): List<PsiClass> {
|
): List<PsiClass> {
|
||||||
val (clsFiles, sourceFiles) = facadeFiles.partition { it is KtClsFile }
|
val (clsFiles, sourceFiles) = facadeFiles.partition { it is KtClsFile }
|
||||||
val lightClassesForClsFacades = clsFiles.mapNotNull { createLightClassForDecompiledKotlinFile(it as KtClsFile) }
|
val facadesFromCls = clsFiles.mapNotNull { createLightClassForDecompiledKotlinFile(it as KtClsFile) }
|
||||||
if (moduleInfo is ModuleSourceInfo && sourceFiles.isNotEmpty()) {
|
val facadesFromSources = createFacadesForSourceFiles(moduleInfo, sourceFiles, facadeFqName)
|
||||||
val lightClassForFacade = KtLightClassForFacade.createForFacade(
|
return facadesFromSources + facadesFromCls
|
||||||
psiManager, facadeFqName, moduleInfo.contentScope(), sourceFiles
|
}
|
||||||
)
|
|
||||||
return withFakeLightClasses(lightClassForFacade, sourceFiles) + lightClassesForClsFacades
|
private fun createFacadesForSourceFiles(
|
||||||
} else {
|
moduleInfo: IdeaModuleInfo,
|
||||||
return lightClassesForClsFacades
|
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> {
|
override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile> {
|
||||||
return runReadAction {
|
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? {
|
private fun getLightClassForDecompiledClassOrObject(decompiledClassOrObject: KtClassOrObject): KtLightClassForDecompiledDeclaration? {
|
||||||
if (decompiledClassOrObject is KtEnumEntry) {
|
if (decompiledClassOrObject is KtEnumEntry) {
|
||||||
return null
|
return null
|
||||||
@@ -307,4 +323,8 @@ class IDEKotlinAsJavaSupport(private val project: Project): KotlinAsJavaSupport(
|
|||||||
javaFileStub.psi = fakeFile
|
javaFileStub.psi = fakeFile
|
||||||
return fakeFile.classes.single() as ClsClassImpl
|
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>()
|
val result = arrayListOf<PsiClass>()
|
||||||
file.declarations.filterIsInstance<KtClassOrObject>().map { it.toLightClass() }.filterNotNullTo(result)
|
file.declarations.filterIsInstance<KtClassOrObject>().map { it.toLightClass() }.filterNotNullTo(result)
|
||||||
|
|
||||||
val moduleInfo = file.getModuleInfo()
|
val moduleInfo = file.getModuleInfoPreferringJvmPlatform()
|
||||||
val jvmClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(file)
|
val jvmClassInfo = JvmFileClassUtil.getFileClassInfoNoResolve(file)
|
||||||
val fileClassFqName = file.javaFileFacadeFqName
|
val fileClassFqName = file.javaFileFacadeFqName
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user