Avoid creating ModuleSourceInfo instances with empty content

This is overall desirable because it means less ModuleDescriptor
    instances which is good for IDE analysis performance
This commit is contained in:
Pavel V. Talanov
2017-08-28 20:25:44 +03:00
parent a623fb5312
commit 352071dc4a
6 changed files with 75 additions and 18 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.impl.scopes.LibraryScopeBase
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.rootManager
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.*
import com.intellij.openapi.roots.impl.libraries.LibraryEx
@@ -30,6 +31,7 @@ import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.util.PathUtil
import com.intellij.util.SmartList
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.TrackableModuleInfo
import org.jetbrains.kotlin.caches.resolve.LibraryModuleInfo
@@ -42,6 +44,7 @@ import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.GlobalSearchScopeWithModuleSources
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
interface IdeaModuleInfo : ModuleInfo {
@@ -56,7 +59,7 @@ interface IdeaModuleInfo : ModuleInfo {
}
private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry, productionOnly: Boolean): List<IdeaModuleInfo> {
fun Module.toInfos() = if (productionOnly) listOf(productionSourceInfo()) else listOf(testSourceInfo(), productionSourceInfo())
fun Module.toInfos() = correspondingModuleInfos().filter { !productionOnly || it is ModuleProductionSourceInfo }
if (!orderEntry.isValid) return emptyList()
@@ -115,7 +118,7 @@ interface ModuleSourceInfo : IdeaModuleInfo, TrackableModuleInfo {
KotlinModuleModificationTracker(module)
}
data class ModuleProductionSourceInfo(override val module: Module) : ModuleSourceInfo {
data class ModuleProductionSourceInfo internal constructor(override val module: Module) : ModuleSourceInfo {
override val name = Name.special("<production sources for module ${module.name}>")
override fun contentScope(): GlobalSearchScope = ModuleProductionSourceScope(module)
@@ -128,7 +131,7 @@ data class ModuleProductionSourceInfo(override val module: Module) : ModuleSourc
}
//TODO: (module refactoring) do not create ModuleTestSourceInfo when there are no test roots for module
data class ModuleTestSourceInfo(override val module: Module) : ModuleSourceInfo {
data class ModuleTestSourceInfo internal constructor(override val module: Module) : ModuleSourceInfo {
override val name = Name.special("<test sources for module ${module.name}>")
override val displayedName get() = module.name + " (test)"
@@ -142,7 +145,9 @@ data class ModuleTestSourceInfo(override val module: Module) : ModuleSourceInfo
})
override fun modulesWhoseInternalsAreVisible() = module.cached(CachedValueProvider {
val list = SmartList<ModuleInfo>(module.productionSourceInfo())
val list = SmartList<ModuleInfo>()
list.addIfNotNull(module.productionSourceInfo())
TestModuleProperties.getInstance(module).productionModule?.let {
list.add(it.productionSourceInfo())
@@ -154,8 +159,16 @@ data class ModuleTestSourceInfo(override val module: Module) : ModuleSourceInfo
internal fun ModuleSourceInfo.isTests() = this is ModuleTestSourceInfo
fun Module.productionSourceInfo(): ModuleProductionSourceInfo = ModuleProductionSourceInfo(this)
fun Module.testSourceInfo(): ModuleTestSourceInfo = ModuleTestSourceInfo(this)
fun Module.productionSourceInfo(): ModuleProductionSourceInfo? = if (hasProductionRoots()) ModuleProductionSourceInfo(this) else null
fun Module.testSourceInfo(): ModuleTestSourceInfo? = if (hasTestRoots()) ModuleTestSourceInfo(this) else null
internal fun Module.correspondingModuleInfos(): List<ModuleSourceInfo> = listOf(testSourceInfo(), productionSourceInfo()).filterNotNull()
private fun Module.hasProductionRoots() = hasRootsOfType(JavaSourceRootType.SOURCE)
private fun Module.hasTestRoots() = hasRootsOfType(JavaSourceRootType.TEST_SOURCE)
private fun Module.hasRootsOfType(sourceRootType: JavaSourceRootType): Boolean =
rootManager.contentEntries.any { it.getSourceFolders(sourceRootType).isNotEmpty() }
private abstract class ModuleSourceScope(val module: Module) : GlobalSearchScope(module.project), GlobalSearchScopeWithModuleSources {
override fun compare(file1: VirtualFile, file2: VirtualFile) = 0
@@ -266,7 +279,7 @@ data class SdkInfo(val project: Project, val sdk: Sdk) : IdeaModuleInfo {
override fun dependencies(): List<IdeaModuleInfo> = listOf(this)
}
internal object NotUnderContentRootModuleInfo : IdeaModuleInfo {
object NotUnderContentRootModuleInfo : IdeaModuleInfo {
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.components.service
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.ProjectJdkTable
@@ -102,7 +103,7 @@ fun createModuleResolverProvider(
fun collectAllModuleInfosFromIdeaModel(project: Project): List<IdeaModuleInfo> {
val ideaModules = ModuleManager.getInstance(project).modules.toList()
val modulesSourcesInfos = ideaModules.flatMap { listOf(it.productionSourceInfo(), it.testSourceInfo()) }
val modulesSourcesInfos = ideaModules.flatMap(Module::correspondingModuleInfos)
//TODO: (module refactoring) include libraries that are not among dependencies of any module
val ideaLibraries = ideaModules.flatMap {
@@ -33,10 +33,10 @@ import java.util.HashSet
fun ModuleSourceInfo.getDependentModules(): Set<ModuleSourceInfo> {
val dependents = getDependents(module)
return if (isTests()) {
dependents.mapTo(HashSet<ModuleSourceInfo>(), Module::testSourceInfo)
dependents.mapNotNullTo(HashSet<ModuleSourceInfo>(), Module::testSourceInfo)
}
else {
dependents.flatMapTo(HashSet<ModuleSourceInfo>()) { listOf(it.productionSourceInfo(), it.testSourceInfo()) }
dependents.flatMapTo(HashSet<ModuleSourceInfo>()) { it.correspondingModuleInfos() }
}
}
@@ -54,8 +54,7 @@ import org.jetbrains.kotlin.console.gutter.ConsoleIndicatorRenderer
import org.jetbrains.kotlin.console.gutter.IconWithTooltip
import org.jetbrains.kotlin.console.gutter.ReplIcons
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.caches.resolve.ModuleTestSourceInfo
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.*
import org.jetbrains.kotlin.idea.project.KOTLIN_CONSOLE_KEY
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.name.Name
@@ -160,7 +159,7 @@ class KotlinConsoleRunner(
private fun enableCompletion(consoleView: LanguageConsoleView) {
val consoleKtFile = PsiManager.getInstance(project).findFile(consoleView.virtualFile) as? KtFile ?: return
consoleKtFile.moduleInfo = ModuleTestSourceInfo(module)
configureFileDependencies(consoleKtFile)
}
override fun createProcessHandler(process: Process): OSProcessHandler {
@@ -268,7 +267,7 @@ class KotlinConsoleRunner(
?: error("Failed to setup PSI for file:\n$text")
replState.submitLine(psiFile)
psiFile.moduleInfo = ModuleTestSourceInfo(module)
configureFileDependencies(psiFile)
val scriptDescriptor = psiFile.script!!.resolveToDescriptor() as? LazyScriptDescriptor ?: error("Failed to analyze line:\n$text")
ForceResolveUtil.forceResolveAllContents(scriptDescriptor)
replState.lineSuccess(psiFile, scriptDescriptor)
@@ -282,4 +281,8 @@ class KotlinConsoleRunner(
val consoleFile = consoleView.virtualFile
return PsiManager.getInstance(project).findFile(consoleFile) as KtFile
}
private fun configureFileDependencies(psiFile: KtFile) {
psiFile.moduleInfo = module.testSourceInfo() ?: module.productionSourceInfo() ?: NotUnderContentRootModuleInfo
}
}
@@ -21,7 +21,9 @@ import com.intellij.openapi.module.StdModuleTypes
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.testFramework.ModuleTestCase
import com.intellij.testFramework.PsiTestUtil
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
@@ -271,6 +273,33 @@ class IdeaModuleInfoTest : ModuleTestCase() {
lib3.classes.assertAdditionalLibraryDependencies(lib1.classes, lib2.classes)
}
fun testRoots() {
val a = module("a", hasProductionRoot = true, hasTestRoot = false)
val empty = module("empty", hasProductionRoot = false, hasTestRoot = false)
a.addDependency(empty)
val b = module("b", hasProductionRoot = false, hasTestRoot = true)
b.addDependency(a)
val c = module("c")
c.addDependency(b)
c.addDependency(a)
assertNotNull(a.productionSourceInfo())
assertNull(a.testSourceInfo())
assertNull(empty.productionSourceInfo())
assertNull(empty.testSourceInfo())
assertNull(b.productionSourceInfo())
assertNotNull(b.testSourceInfo())
b.test.assertDependenciesEqual(b.test, a.production)
c.test.assertDependenciesEqual(c.test, c.production, b.test, a.production)
c.production.assertDependenciesEqual(c.production, a.production)
}
fun testCommonLibraryDoesNotDependOnPlatform() {
val stdlibCommon = stdlibCommon()
val stdlibJvm = stdlibJvm()
@@ -296,18 +325,25 @@ class IdeaModuleInfoTest : ModuleTestCase() {
) = ModuleRootModificationUtil.addDependency(this, other, dependencyScope, exported)
private val Module.production: ModuleProductionSourceInfo
get() = productionSourceInfo()
get() = productionSourceInfo()!!
private val Module.test: ModuleTestSourceInfo
get() = testSourceInfo()
get() = testSourceInfo()!!
private val Library.classes: LibraryInfo
get() = LibraryInfo(project!!, this)
private fun module(name: String): Module {
return createModuleFromTestData(createTempDirectory()!!.absolutePath, name, StdModuleTypes.JAVA, false)!!
private fun module(name: String, hasProductionRoot: Boolean = true, hasTestRoot: Boolean = true): Module {
return createModuleFromTestData(createTempDirectory()!!.absolutePath, name, StdModuleTypes.JAVA, false)!!.apply {
if (hasProductionRoot)
PsiTestUtil.addSourceContentToRoots(this, dir(), false)
if (hasTestRoot)
PsiTestUtil.addSourceContentToRoots(this, dir(), true)
}
}
private fun dir() = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(createTempDirectory())!!
private fun modules(name1: String = "a", name2: String = "b", name3: String = "c") = Triple(module(name1), module(name2), module(name3))
private fun IdeaModuleInfo.assertDependenciesEqual(vararg expected: IdeaModuleInfo) {
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.repl
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
import com.intellij.psi.PsiDocumentManager
import com.intellij.testFramework.LightProjectDescriptor
import org.jetbrains.kotlin.console.KotlinConsoleKeeper
@@ -28,6 +29,7 @@ import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescrip
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractIdeReplCompletionTest : KotlinFixtureCompletionBaseTestCase() {
@@ -36,9 +38,11 @@ abstract class AbstractIdeReplCompletionTest : KotlinFixtureCompletionBaseTestCa
override fun setUp() {
super.setUp()
consoleRunner = KotlinConsoleKeeper.getInstance(project).run(myModule)!!
VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory())
}
override fun tearDown() {
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory())
consoleRunner?.dispose()
consoleRunner = null
super.tearDown()