[IDE] Move library dependency filtering to dependencies cache
The primary client of LibraryDependenciesCache is LibraryInfo, but it is also used for maintaining modification trackers of source-dependent libraries. Moving dependency filtering to cache allows keeping all client in sync. ^KT-45908 In Progress
This commit is contained in:
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.util.enlargedSearchScope
|
||||
import org.jetbrains.kotlin.idea.caches.trackers.KotlinModuleOutOfCodeBlockModificationTracker
|
||||
import org.jetbrains.kotlin.idea.configuration.BuildSystemType
|
||||
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
|
||||
import org.jetbrains.kotlin.idea.configuration.getBuildSystemType
|
||||
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware
|
||||
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
|
||||
@@ -353,25 +352,7 @@ abstract class LibraryInfo(override val project: Project, val library: Library)
|
||||
val (libraries, sdks) = LibraryDependenciesCache.getInstance(project).getLibrariesAndSdksUsedWith(this)
|
||||
|
||||
result.addAll(sdks)
|
||||
|
||||
/*
|
||||
* When built-ins are created from module dependencies (as opposed to loading them from classloader)
|
||||
* we must resolve Kotlin standard library containing some of the built-ins declarations in the same
|
||||
* resolver for project as JDK. This comes from the following requirements:
|
||||
* - JvmBuiltins need JDK and standard library descriptors -> resolver for project should be able to
|
||||
* resolve them
|
||||
* - Builtins are created in BuiltinsCache -> module descriptors should be resolved under lock of the
|
||||
* SDK resolver to prevent deadlocks
|
||||
* This means we have to maintain dependencies of the standard library manually or effectively drop
|
||||
* resolver for SDK otherwise. Libraries depend on superset of their actual dependencies because of
|
||||
* the inability to get real dependencies from IDEA model. So moving stdlib with all dependencies
|
||||
* down is a questionable option.
|
||||
*/
|
||||
if (!IdeBuiltInsLoadingState.isFromClassLoader && this.isCoreKotlinLibrary(project)) {
|
||||
libraries.filterTo(result) { it.isCoreKotlinLibrary(project) }
|
||||
} else {
|
||||
result.addAll(libraries)
|
||||
}
|
||||
result.addAll(libraries)
|
||||
|
||||
return result.toList()
|
||||
}
|
||||
|
||||
+25
-1
@@ -17,6 +17,7 @@ import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
|
||||
import org.jetbrains.kotlin.idea.core.util.CachedValue
|
||||
import org.jetbrains.kotlin.idea.core.util.getValue
|
||||
import org.jetbrains.kotlin.idea.project.isHMPPEnabled
|
||||
@@ -93,7 +94,9 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
|
||||
}, Unit)
|
||||
}
|
||||
|
||||
return Pair(libraries.toList(), sdks.toList())
|
||||
val filteredLibraries = filterForBuiltins(libraryInfo, libraries)
|
||||
|
||||
return Pair(filteredLibraries.toList(), sdks.toList())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,6 +112,27 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
|
||||
}!!
|
||||
}
|
||||
|
||||
/*
|
||||
* When built-ins are created from module dependencies (as opposed to loading them from classloader)
|
||||
* we must resolve Kotlin standard library containing some of the built-ins declarations in the same
|
||||
* resolver for project as JDK. This comes from the following requirements:
|
||||
* - JvmBuiltins need JDK and standard library descriptors -> resolver for project should be able to
|
||||
* resolve them
|
||||
* - Builtins are created in BuiltinsCache -> module descriptors should be resolved under lock of the
|
||||
* SDK resolver to prevent deadlocks
|
||||
* This means we have to maintain dependencies of the standard library manually or effectively drop
|
||||
* resolver for SDK otherwise. Libraries depend on superset of their actual dependencies because of
|
||||
* the inability to get real dependencies from IDEA model. So moving stdlib with all dependencies
|
||||
* down is a questionable option.
|
||||
*/
|
||||
private fun filterForBuiltins(libraryInfo: LibraryInfo, dependencyLibraries: Set<LibraryInfo>): Set<LibraryInfo> {
|
||||
return if (!IdeBuiltInsLoadingState.isFromClassLoader && libraryInfo.isCoreKotlinLibrary(project)) {
|
||||
dependencyLibraries.filterTo(mutableSetOf()) { it.isCoreKotlinLibrary(project) }
|
||||
} else {
|
||||
dependencyLibraries
|
||||
}
|
||||
}
|
||||
|
||||
private inner class LibraryUsageIndex {
|
||||
private val modulesLibraryIsUsedIn: MultiMap<Library, Module> = MultiMap.createSet()
|
||||
|
||||
|
||||
+6
@@ -13,6 +13,7 @@ import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.caches.project.cacheByClassInvalidatingOnRootModifications
|
||||
import org.jetbrains.kotlin.idea.caches.project.LibraryDependenciesCache
|
||||
@@ -60,6 +61,11 @@ class ResolutionAnchorCacheServiceImpl(val project: Project) :
|
||||
XmlSerializerUtil.copyBean(state, myState)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun setAnchors(mapping: Map<String, String>) {
|
||||
myState = State(mapping)
|
||||
}
|
||||
|
||||
object ResolutionAnchorMappingCacheKey
|
||||
object ResolutionAnchorDependenciesCacheKey
|
||||
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
class LibraryClass
|
||||
+1
@@ -0,0 +1 @@
|
||||
class DependencyClass
|
||||
+1
@@ -0,0 +1 @@
|
||||
class SourceDependentClass
|
||||
+1
@@ -0,0 +1 @@
|
||||
class Test1
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.idea.caches.resolve
|
||||
|
||||
import com.intellij.facet.FacetManager
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
@@ -20,15 +21,13 @@ import org.jetbrains.kotlin.analyzer.ResolverForModuleComputationTracker
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
|
||||
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
|
||||
import org.jetbrains.kotlin.idea.caches.trackers.KotlinModuleOutOfCodeBlockModificationTracker
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings
|
||||
import org.jetbrains.kotlin.idea.completion.test.withServiceRegistered
|
||||
import org.jetbrains.kotlin.idea.core.util.toPsiFile
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetConfiguration
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetType
|
||||
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
||||
@@ -37,12 +36,17 @@ import org.jetbrains.kotlin.idea.test.allKotlinFiles
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.util.projectStructure.sdk
|
||||
import org.jetbrains.kotlin.idea.util.sourceRoots
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.samWithReceiver.SamWithReceiverCommandLineProcessor.Companion.ANNOTATION_OPTION
|
||||
import org.jetbrains.kotlin.samWithReceiver.SamWithReceiverCommandLineProcessor.Companion.PLUGIN_ID
|
||||
import org.jetbrains.kotlin.test.*
|
||||
import org.jetbrains.kotlin.test.TestJdkKind.FULL_JDK
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.concurrent.ThreadLocalRandom
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
@WithMutedInDatabaseRunTest
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
@@ -258,6 +262,49 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
||||
checkHighlightingInProject()
|
||||
}
|
||||
|
||||
fun testResolutionAnchorsAndBuiltins() {
|
||||
val jarForCompositeLibrary = MockLibraryUtilExt.compileJvmLibraryToJar(
|
||||
sourcesPath = "$testDataPath${getTestName(true)}/compositeLibraryPart",
|
||||
jarName = "compositeLibraryPart"
|
||||
)
|
||||
val stdlibJarForCompositeLibrary = ForTestCompileRuntime.runtimeJarForTests()
|
||||
val jarForSourceDependentLibrary = MockLibraryUtilExt.compileJvmLibraryToJar(
|
||||
sourcesPath = "$testDataPath${getTestName(true)}/sourceDependentLibrary",
|
||||
jarName = "sourceDependentLibrary"
|
||||
)
|
||||
|
||||
val dependencyModule = module("dependencyModule")
|
||||
val anchorModule = module("anchor")
|
||||
val sourceModule = module("sourceModule")
|
||||
|
||||
val sourceDependentLibraryName = "sourceDependentLibrary"
|
||||
sourceModule.addMultiJarLibrary(listOf(stdlibJarForCompositeLibrary, jarForCompositeLibrary), "compositeLibrary")
|
||||
sourceModule.addLibrary(jarForSourceDependentLibrary, sourceDependentLibraryName)
|
||||
anchorModule.addDependency(dependencyModule)
|
||||
|
||||
val anchorMapping = mapOf(sourceDependentLibraryName to anchorModule.name)
|
||||
|
||||
withResolutionAnchors(anchorMapping) {
|
||||
checkHighlightingInProject()
|
||||
dependencyModule.modifyTheOnlySourceFile()
|
||||
checkHighlightingInProject()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Module.modifyTheOnlySourceFile() {
|
||||
val sourceRoot = sourceRoots.singleOrNull() ?: error("Expected single source root in a test module")
|
||||
assert(sourceRoot.isDirectory) { "Source root of a test module is not a directory" }
|
||||
val ktFile = sourceRoot.children.singleOrNull()?.toPsiFile(project) as? KtFile
|
||||
?: error("Expected single .kt file in a test source module")
|
||||
|
||||
val stubFunctionName = "fn${System.currentTimeMillis()}_${ThreadLocalRandom.current().nextInt().toLong().absoluteValue}"
|
||||
WriteCommandAction.runWriteCommandAction(project) {
|
||||
ktFile.add(
|
||||
KtPsiFactory(project).createFunction("fun $stubFunctionName() {}")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Module.setupKotlinFacet(configure: KotlinFacetConfiguration.() -> Unit) = apply {
|
||||
runWriteAction {
|
||||
val facet = FacetManager.getInstance(this).addFacet(KotlinFacetType.INSTANCE, KotlinFacetType.NAME, null)
|
||||
|
||||
@@ -27,15 +27,18 @@ import com.intellij.util.ThrowableRunnable
|
||||
import org.jetbrains.kotlin.config.CompilerSettings
|
||||
import org.jetbrains.kotlin.config.KotlinFacetSettings
|
||||
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionAnchorCacheService
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionAnchorCacheServiceImpl
|
||||
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet
|
||||
import org.jetbrains.kotlin.idea.facet.initializeIfNeeded
|
||||
import org.jetbrains.kotlin.idea.project.KotlinLibraryToSourceAnalysisComponent
|
||||
import org.jetbrains.kotlin.idea.test.*
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import org.jetbrains.kotlin.test.WithMutedInDatabaseRunTest
|
||||
import org.jetbrains.kotlin.test.runTest
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.junit.Assert
|
||||
import java.io.File
|
||||
|
||||
@@ -111,10 +114,19 @@ abstract class AbstractMultiModuleTest : DaemonAnalyzerTestCase() {
|
||||
jar: File,
|
||||
name: String = KotlinJdkAndLibraryProjectDescriptor.LIBRARY_NAME,
|
||||
kind: PersistentLibraryKind<*>? = null
|
||||
) = addMultiJarLibrary(listOf(jar), name, kind)
|
||||
|
||||
fun Module.addMultiJarLibrary(
|
||||
jars: Collection<File>,
|
||||
name: String = KotlinJdkAndLibraryProjectDescriptor.LIBRARY_NAME,
|
||||
kind: PersistentLibraryKind<*>? = null,
|
||||
) {
|
||||
assert(jars.isNotEmpty()) { "No JARs passed for a library" }
|
||||
ConfigLibraryUtil.addLibrary(NewLibraryEditor().apply {
|
||||
this.name = name
|
||||
addRoot(VfsUtil.getUrlForLibraryRoot(jar), OrderRootType.CLASSES)
|
||||
for (jar in jars) {
|
||||
addRoot(VfsUtil.getUrlForLibraryRoot(jar), OrderRootType.CLASSES)
|
||||
}
|
||||
}, this, kind)
|
||||
}
|
||||
|
||||
@@ -149,6 +161,26 @@ abstract class AbstractMultiModuleTest : DaemonAnalyzerTestCase() {
|
||||
}
|
||||
Assert.assertTrue(atLeastOneFile)
|
||||
}
|
||||
|
||||
protected fun withResolutionAnchors(
|
||||
anchors: Map<String, String>,
|
||||
block: () -> Unit
|
||||
) {
|
||||
val resolutionAnchorService = ResolutionAnchorCacheService.getInstance(project).safeAs<ResolutionAnchorCacheServiceImpl>()
|
||||
?: error("Anchor service missing")
|
||||
val oldResolutionAnchorMappingState = resolutionAnchorService.state
|
||||
val oldLibraryToSourceAnalysisState = KotlinLibraryToSourceAnalysisComponent.isEnabled(project)
|
||||
|
||||
resolutionAnchorService.setAnchors(anchors)
|
||||
KotlinLibraryToSourceAnalysisComponent.setState(project, isEnabled = true)
|
||||
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
KotlinLibraryToSourceAnalysisComponent.setState(project, isEnabled = oldLibraryToSourceAnalysisState)
|
||||
resolutionAnchorService.loadState(oldResolutionAnchorMappingState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Module.createFacet(
|
||||
|
||||
Reference in New Issue
Block a user