From 2c1d1e84a6c7f7a5752135ebe5da37b6e544b769 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Wed, 3 Feb 2016 20:40:01 +0300 Subject: [PATCH] Allow to use internal declarations from special modules in IDEA (e.g. IDEA 16 gradle tests loaded in separate module) #KT-10595 Fixed --- .../kotlin/analyzer/AnalyzerFacade.kt | 10 ++-- .../org/jetbrains/kotlin/utils/functions.kt | 4 ++ .../idea/caches/resolve/IdeaModuleInfos.kt | 49 +++++++++++++++++-- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt index d3336bce6db..acb91a17cee 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt @@ -98,7 +98,7 @@ class ResolverForProjectImpl( } private fun doGetDescriptorForModule(moduleInfo: M): ModuleDescriptorImpl { - return descriptorByModule[moduleInfo] ?: return delegateResolver.descriptorForModule(moduleInfo) as ModuleDescriptorImpl + return descriptorByModule[moduleInfo] ?: delegateResolver.descriptorForModule(moduleInfo) as ModuleDescriptorImpl } } @@ -114,7 +114,7 @@ interface ModuleInfo { get() = false val name: Name fun dependencies(): List - fun friends(): Collection = listOf() + fun modulesWhichInternalsAreVisible(): Collection = listOf() fun dependencyOnBuiltins(): DependencyOnBuiltins = DependenciesOnBuiltins.LAST val capabilities: Map, Any?> get() = emptyMap() @@ -193,8 +193,8 @@ abstract class AnalyzerFacade { modules.forEach { module -> val descriptor = resolverForProject.descriptorForModule(module) - module.friends().forEach { - descriptor.addFriend(resolverForProject.descriptorForModule(it as M)) + module.modulesWhichInternalsAreVisible().forEach { + resolverForProject.descriptorForModule(it as M).addFriend(descriptor) } } } @@ -249,4 +249,4 @@ private class DelegatingPackageFragmentProvider( override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection { return delegate().getSubPackagesOf(fqName, nameFilter) } -} \ No newline at end of file +} diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/functions.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/functions.kt index 5e08e58459d..2813e2272d8 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/functions.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/functions.kt @@ -25,6 +25,10 @@ private val ALWAYS_TRUE: (Any?) -> Boolean = { true } fun alwaysTrue(): (T) -> Boolean = ALWAYS_TRUE +private val ALWAYS_NULL: (Any?) -> Any? = { null } + +@Suppress("UNCHECKED_CAST") +fun alwaysNull(): (T) -> R? = ALWAYS_NULL as (T) -> R? val DO_NOTHING: (Any?) -> Unit = { } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt index 5cb7432d447..4ad813ec5e8 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IdeaModuleInfos.kt @@ -26,13 +26,44 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager +import com.intellij.util.SmartList import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.alwaysNull import org.jetbrains.kotlin.utils.emptyOrSingletonList +import java.lang.reflect.Method import java.util.* -val LIBRARY_NAME_PREFIX: String = "library " +private val LIBRARY_NAME_PREFIX: String = "library " + +// TODO used reflection to be compatible with IDEA from both 143 and 144 branches, +// TODO switch to directly using when "since-build" will be >= 144.3357.4 +private val getRelatedProductionModule: (Module) -> Module? = run { + val klass = + try { + Class.forName("com.intellij.openapi.roots.TestModuleProperties") + } catch (e: ClassNotFoundException) { + return@run alwaysNull() + } + + + val getInstanceMethod: Method + val getProductionModuleMethod: Method + + try { + getInstanceMethod = klass.getDeclaredMethod("getInstance", Module::class.java) + getProductionModuleMethod = klass.getDeclaredMethod("getProductionModule") + } + catch (e: NoSuchMethodException) { + return@run alwaysNull() + } + + return@run { module -> + val instance = getInstanceMethod(null, module) + getProductionModuleMethod(instance) as Module? + } +} interface IdeaModuleInfo : ModuleInfo { fun contentScope(): GlobalSearchScope @@ -71,7 +102,7 @@ private fun Module.cached(provider: CachedValueProvider): T { return CachedValuesManager.getManager(project).getCachedValue(this, provider) } -fun ideaModelDependencies(module: Module, productionOnly: Boolean): List { +private fun ideaModelDependencies(module: Module, productionOnly: Boolean): List { //NOTE: lib dependencies can be processed several times during recursive traversal val result = LinkedHashSet() val dependencyEnumerator = ModuleRootManager.getInstance(module).orderEntries().compileOnly().recursively().exportedOnly() @@ -102,8 +133,6 @@ data class ModuleProductionSourceInfo(override val module: Module) : ModuleSourc ideaModelDependencies(module, productionOnly = true), ProjectRootModificationTracker.getInstance(module.project)) }) - - override fun friends() = listOf(module.testSourceInfo()) } //TODO: (module refactoring) do not create ModuleTestSourceInfo when there are no test roots for module @@ -117,6 +146,16 @@ data class ModuleTestSourceInfo(override val module: Module) : ModuleSourceInfo ideaModelDependencies(module, productionOnly = false), ProjectRootModificationTracker.getInstance(module.project)) }) + + override fun modulesWhichInternalsAreVisible() = module.cached(CachedValueProvider { + val list = SmartList(module.productionSourceInfo()) + + getRelatedProductionModule(module)?.let { + list.add(it.productionSourceInfo()) + } + + CachedValueProvider.Result(list, ProjectRootModificationTracker.getInstance(module.project)) + }) } internal fun ModuleSourceInfo.isTests() = this is ModuleTestSourceInfo @@ -249,4 +288,4 @@ enum class ModuleOrigin { MODULE, LIBRARY, OTHER -} \ No newline at end of file +}