From d6d385913f02929a88aec0ba77d5e62630c31a89 Mon Sep 17 00:00:00 2001 From: "Aleksei.Cherepanov" Date: Fri, 22 Mar 2024 17:46:39 +0000 Subject: [PATCH] Use script context to resolve SDK for a library that no modules depend on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: The initial issues KTIJ-27480 [AE "Can't find built-in class kotlin.Cloneable" on opening build.gradle.kts with broken syntax highlighting] and KTIJ-25236 ["AssertionError: Can't find built-in class kotlin.Cloneable" after reload build.gradle.kts configuration] arose due to the following chain of events: we attempt to resolve a KTS script ⇒ in the script dependencies, we found a symbol for which Kotlin built-ins need to be created ⇒ creating of built-ins depends on the SDK and stdlib ⇒ (1) we find the SDK from the module to which this library is bound ⇒ (2) in our case, buildSrc module has no source set, so the library ends up hanging and not bound to any module ⇒ sdk=null ⇒ built-ins=Default (instead of JVM ones) ⇒ the required built-in is not found ⇒ internal resolve error. Such a situation (2) can occur if, for example, the buildSrc module does not have source folders and the Kotlin plugin is not applied. In this case, its list of source roots will be empty, and all libraries needed to resolve the script are not bound to any modules. Important notice: here we determine the SDK from the module. This behavior appears only when using the Composite resolver (only in the case of MPP projects). In the case of SEPARATE mode - the SDK is provided for the entire project, and such an error does not occur, while in SEPARATE it is prohibited and the SDK needs to be computed depending on the module. Motivation: The situation where a library is not associated with any module because it is only used in a script remains unhandled. In this fix, we take into account this possible scenario and handle it to ensure correctness. Solution: at the moment of building the mapping (1) between libraries and modules that use them, we also use information from `ScriptConfigurationManager` (if the search by direct dependencies does not yield results), to find the script that depends on this library. From this script, we add SDK information. Note: in general, the architectural problem lies in the lack of context - in resolving, we can come to the same library from different contexts - scripts have their classpath, which requires the use of the Gradle JDK, the same library can be in the dependencies of buildSrc - there user can set any JDK using the toolchain. We can also find this library through symbol search. Thus, it turns out that the same library can have different environments, about which nothing is known at the moment. ^KTIJ-27480 Fixed Merge-request: IJ-MR-129468 Merged-by: Aleksei Cherepanov GitOrigin-RevId: 151c0bc82278ad379a8c091bcfc46850c9d83e9e --- .../idea/base/analysis/LibraryDependenciesCache.kt | 8 ++++++++ .../idea/core/script/ScriptConfigurationManager.kt | 1 + .../CompositeScriptConfigurationManager.kt | 12 +++++++++++- .../idea/core/script/ucache/ScriptClassRootsCache.kt | 5 ++++- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/plugins/kotlin/base/analysis/src/org/jetbrains/kotlin/idea/base/analysis/LibraryDependenciesCache.kt b/plugins/kotlin/base/analysis/src/org/jetbrains/kotlin/idea/base/analysis/LibraryDependenciesCache.kt index 8f2db939efbf..14ac61a5ac08 100644 --- a/plugins/kotlin/base/analysis/src/org/jetbrains/kotlin/idea/base/analysis/LibraryDependenciesCache.kt +++ b/plugins/kotlin/base/analysis/src/org/jetbrains/kotlin/idea/base/analysis/LibraryDependenciesCache.kt @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.idea.base.util.caching.findSdkBridge import org.jetbrains.kotlin.idea.base.util.caching.getChanges import org.jetbrains.kotlin.idea.caches.project.* import org.jetbrains.kotlin.idea.configuration.isMavenized +import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -177,6 +178,13 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend val filteredLibraries = filterForBuiltins(libraryInfo, libraryDependencyCandidatesAndSdkInfos.libraryDependencyCandidates) + libraryDependencyCandidatesAndSdkInfos.sdkInfos.takeIf { it.isEmpty() }?.apply { + val scriptConfigurationManager = ScriptConfigurationManager.getInstance(project) + scriptConfigurationManager.getScriptDependingOn(libraryInfo.getLibraryRoots()) + ?.let { script -> scriptConfigurationManager.getScriptSdk(script) } + ?.let { sdk -> add(SdkInfo(project, sdk)) } + } + return LibraryDependencyCandidatesAndSdkInfos(filteredLibraries, libraryDependencyCandidatesAndSdkInfos.sdkInfos) } diff --git a/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/ScriptConfigurationManager.kt b/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/ScriptConfigurationManager.kt index c56ec2f65e5e..f2d5c6fbfc91 100644 --- a/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/ScriptConfigurationManager.kt +++ b/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/ScriptConfigurationManager.kt @@ -119,6 +119,7 @@ interface ScriptConfigurationManager { fun getAllScriptDependenciesSources(): Collection fun getAllScriptsSdkDependenciesClassFiles(): Collection fun getAllScriptSdkDependenciesSources(): Collection + fun getScriptDependingOn(dependencies: Collection): VirtualFile? companion object { fun getServiceIfCreated(project: Project): ScriptConfigurationManager? = project.serviceIfCreated() diff --git a/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt b/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt index abb9796942c8..6620d3e61125 100644 --- a/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt +++ b/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/configuration/CompositeScriptConfigurationManager.kt @@ -4,11 +4,11 @@ package org.jetbrains.kotlin.idea.core.script.configuration import com.intellij.codeInsight.daemon.OutsidersPsiFileSupport import com.intellij.ide.scratch.ScratchUtil -import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.roots.OrderRootType import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.platform.backend.workspace.WorkspaceModelChangeListener import com.intellij.platform.backend.workspace.WorkspaceModelTopics @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.core.script.ucache.* import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper +import java.nio.file.Path /** * The [CompositeScriptConfigurationManager] will provide redirection of [ScriptConfigurationManager] calls to the @@ -183,6 +184,15 @@ class CompositeScriptConfigurationManager(val project: Project, val scope: Corou override fun getAllScriptSdkDependenciesSources(): Collection = classpathRoots.sdks.nonIndexedSourceRoots + override fun getScriptDependingOn(dependencies: Collection): VirtualFile? = + classpathRoots.scriptsPaths().firstNotNullOfOrNull { scriptPath -> + VfsUtil.findFile(Path.of(scriptPath), true)?.takeIf { scriptVirtualFile -> + getScriptDependenciesClassFiles(scriptVirtualFile).any { scriptDependency -> + dependencies.contains(scriptDependency.presentableUrl) + } + } + } + override fun getScriptDependenciesClassFiles(file: VirtualFile): Collection = classpathRoots.getScriptDependenciesClassFiles(file) diff --git a/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/ucache/ScriptClassRootsCache.kt b/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/ucache/ScriptClassRootsCache.kt index 7822de9c7510..c58e41c5b516 100644 --- a/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/ucache/ScriptClassRootsCache.kt +++ b/plugins/kotlin/base/scripting/src/org/jetbrains/kotlin/idea/core/script/ucache/ScriptClassRootsCache.kt @@ -148,8 +148,11 @@ class ScriptClassRootsCache( fun getScriptConfiguration(file: VirtualFile): ScriptCompilationConfigurationWrapper? = getHeavyScriptInfo(file.path)?.scriptConfiguration + fun getScriptSdk(path: String): Sdk? = + getHeavyScriptInfo(path)?.sdk + fun getScriptSdk(file: VirtualFile): Sdk? = - getHeavyScriptInfo(file.path)?.sdk + getScriptSdk(file.path) fun getScriptDependenciesClassFilesScope(file: VirtualFile): GlobalSearchScope = getHeavyScriptInfo(file.path)?.classFilesScope ?: GlobalSearchScope.EMPTY_SCOPE