findDecompiledDeclaration: narrow decompiled declaration search scope

Search scope of library that descriptor originated from
Introduce API that matches binaries and its' sources via IdeaModuleInfos
This commit is contained in:
Pavel V. Talanov
2017-03-18 19:19:02 +03:00
parent 569e7ac593
commit 4c94d931fa
3 changed files with 59 additions and 26 deletions
@@ -127,7 +127,7 @@ interface ModuleInfo {
fun modulesWhoseInternalsAreVisible(): Collection<ModuleInfo> = listOf()
fun dependencyOnBuiltIns(): DependencyOnBuiltIns = DependenciesOnBuiltIns.LAST
val capabilities: Map<ModuleDescriptor.Capability<*>, Any?>
get() = emptyMap()
get() = mapOf(Capability to this)
//TODO: (module refactoring) provide dependency on builtins after runtime in IDEA
interface DependencyOnBuiltIns {
@@ -146,6 +146,10 @@ interface ModuleInfo {
}
};
}
companion object {
val Capability = ModuleDescriptor.Capability<ModuleInfo>("ModuleInfo")
}
}
abstract class AnalyzerFacade<in P : PlatformAnalysisParameters> {
@@ -271,4 +275,4 @@ interface LanguageSettingsProvider {
companion object {
fun getInstance(project: Project) = ServiceManager.getService(project, LanguageSettingsProvider::class.java) ?: Default
}
}
}
@@ -44,7 +44,7 @@ interface IdeaModuleInfo : ModuleInfo {
val moduleOrigin: ModuleOrigin
override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?>
get() = mapOf(OriginCapability to moduleOrigin)
get() = super.capabilities + mapOf(OriginCapability to moduleOrigin)
override fun dependencies(): List<IdeaModuleInfo>
}
@@ -176,7 +176,7 @@ private class ModuleTestSourceScope(module: Module) : ModuleSourceScope(module)
override fun toString() = "ModuleTestSourceScope($module)"
}
open class LibraryInfo(val project: Project, val library: Library) : IdeaModuleInfo, LibraryModuleInfo {
class LibraryInfo(val project: Project, val library: Library) : IdeaModuleInfo, LibraryModuleInfo, BinaryModuleInfo {
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.LIBRARY
@@ -201,6 +201,9 @@ open class LibraryInfo(val project: Project, val library: Library) : IdeaModuleI
override fun isJsLibrary(): Boolean = KotlinJavaScriptLibraryDetectionUtil.isKotlinJavaScriptLibrary(library)
override val sourcesModuleInfo: SourceForBinaryModuleInfo
get() = LibrarySourceInfo(project, library)
override fun getLibraryRoots(): Collection<String> =
library.getFiles(OrderRootType.CLASSES).map(PathUtil::getLocalPath).filterNotNull()
@@ -214,13 +217,15 @@ open class LibraryInfo(val project: Project, val library: Library) : IdeaModuleI
override fun hashCode(): Int = 43 * library.hashCode()
}
data class LibrarySourceInfo(val project: Project, val library: Library) : IdeaModuleInfo {
data class LibrarySourceInfo(val project: Project, val library: Library) : IdeaModuleInfo, SourceForBinaryModuleInfo {
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER
override val name: Name = Name.special("<sources for library ${library.name}>")
override fun contentScope() = GlobalSearchScope.EMPTY_SCOPE
override fun contentScope(): GlobalSearchScope = GlobalSearchScope.EMPTY_SCOPE
override fun sourceScope(): GlobalSearchScope = LibrarySourceScope(project, library)
override val isLibrary: Boolean
get() = true
@@ -233,6 +238,9 @@ data class LibrarySourceInfo(val project: Project, val library: Library) : IdeaM
return listOf(LibraryInfo(project, library))
}
override val binariesModuleInfo: BinaryModuleInfo
get() = LibraryInfo(project, library)
override fun toString() = "LibrarySourceInfo(libraryName=${library.name})"
}
@@ -263,6 +271,8 @@ internal object NotUnderContentRootModuleInfo : IdeaModuleInfo {
private class LibraryWithoutSourceScope(project: Project, private val library: Library) :
LibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getClassRootForFile(file)
override fun equals(other: Any?) = other is LibraryWithoutSourceScope && library == other.library
override fun hashCode() = library.hashCode()
@@ -270,6 +280,18 @@ private class LibraryWithoutSourceScope(project: Project, private val library: L
override fun toString() = "LibraryWithoutSourceScope($library)"
}
private class LibrarySourceScope(project: Project, private val library: Library) :
LibraryScopeBase(project, arrayOf<VirtualFile>(), library.getFiles(OrderRootType.SOURCES)) {
override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getSourceRootForFile(file)
override fun equals(other: Any?) = other is LibrarySourceScope && library == other.library
override fun hashCode() = library.hashCode()
override fun toString() = "LibrarySourceScope($library)"
}
//TODO: (module refactoring) android sdk has modified scope
private class SdkScope(project: Project, private val sdk: Sdk) :
LibraryScopeBase(project, sdk.rootProvider.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
@@ -290,3 +312,13 @@ enum class ModuleOrigin {
LIBRARY,
OTHER
}
interface BinaryModuleInfo : IdeaModuleInfo {
val sourcesModuleInfo: SourceForBinaryModuleInfo?
fun binariesScope(): GlobalSearchScope = contentScope()
}
interface SourceForBinaryModuleInfo : IdeaModuleInfo {
val binariesModuleInfo: BinaryModuleInfo
fun sourceScope(): GlobalSearchScope
}
@@ -18,11 +18,13 @@ package org.jetbrains.kotlin.idea.decompiler.navigation
import com.intellij.openapi.project.Project
import com.intellij.psi.search.EverythingGlobalScope
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.*
import org.jetbrains.kotlin.idea.caches.resolve.BinaryModuleInfo
import org.jetbrains.kotlin.idea.caches.resolve.LOG
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledTextIndexer
import org.jetbrains.kotlin.idea.stubindex.*
@@ -50,21 +52,16 @@ fun findDecompiledDeclaration(
if (isLocal(referencedDescriptor)) return null
if (referencedDescriptor is PackageFragmentDescriptor || referencedDescriptor is PackageViewDescriptor) return null
val decompiledFiles = findDecompiledFilesForDescriptor(project, referencedDescriptor)
val binaryInfo = referencedDescriptor.module.getCapability(ModuleInfo.Capability) as? BinaryModuleInfo
val referencedModule = referencedDescriptor.module
return decompiledFiles.asSequence().mapNotNull { file ->
val moduleInfo = file.getNullableModuleInfo()
val libraryInfo = when (moduleInfo) {
is LibraryInfo -> moduleInfo
is LibrarySourceInfo -> LibraryInfo(project, moduleInfo.library)
else -> null
}
val libraryModule = libraryInfo?.let { file.getResolutionFacade().findModuleDescriptor(it) }
if (libraryModule != null
&& referencedModule.name != KotlinBuiltIns.BUILTINS_MODULE_NAME
&& referencedModule.name != libraryModule.name) return@mapNotNull null
val scope = binaryInfo?.binariesScope() ?:
// TODO: can't return null right now, have to deal with builtins
KotlinSourceFilterScope.libraryClassFiles(EverythingGlobalScope(project), project)
val decompiledFiles = findDecompiledFilesForDescriptor(project, referencedDescriptor, scope)
return decompiledFiles.asSequence().mapNotNull {
file ->
ByDescriptorIndexer.getDeclarationForDescriptor(referencedDescriptor, file)
}.firstOrNull()
}
@@ -80,19 +77,19 @@ private fun isLocal(descriptor: DeclarationDescriptor): Boolean {
private fun findDecompiledFilesForDescriptor(
project: Project,
referencedDescriptor: DeclarationDescriptor
referencedDescriptor: DeclarationDescriptor,
scope: GlobalSearchScope
): Collection<KtDecompiledFile> {
return findCandidateDeclarationsInIndex(project, referencedDescriptor).mapNotNullTo(LinkedHashSet()) {
return findCandidateDeclarationsInIndex(project, referencedDescriptor, scope).mapNotNullTo(LinkedHashSet()) {
it?.containingFile as? KtDecompiledFile
}
}
private fun findCandidateDeclarationsInIndex(
project: Project,
referencedDescriptor: DeclarationDescriptor
referencedDescriptor: DeclarationDescriptor,
scope: GlobalSearchScope
): Collection<KtDeclaration?> {
val scope = KotlinSourceFilterScope.libraryClassFiles(EverythingGlobalScope(project), project)
val containingClass = DescriptorUtils.getParentOfType(referencedDescriptor, ClassDescriptor::class.java, false)
if (containingClass != null) {
return KotlinFullClassNameIndex.getInstance().get(containingClass.fqNameSafe.asString(), project, scope)