[Analysis API] Include files generated by `KaResolveExtension` directly in `KaModule.contentScope`
Before that change, `KaModule.contentScope` didn't have any support for such files.
The only provided support of generated files was contained in `KaBaseResolutionScope`.
Now every module that has at least one `KaResolveExtension` provided will contain a special `KaResolveExtensionGeneratedFilesScope` directly in its content scope.
^KT-74541 fixed
diff --git a/analysis/analysis-api-impl-base/resources/META-INF/analysis-api/analysis-api-impl-base.xml b/analysis/analysis-api-impl-base/resources/META-INF/analysis-api/analysis-api-impl-base.xml
index b8e1280..a1ff03a 100644
--- a/analysis/analysis-api-impl-base/resources/META-INF/analysis-api/analysis-api-impl-base.xml
+++ b/analysis/analysis-api-impl-base/resources/META-INF/analysis-api/analysis-api-impl-base.xml
@@ -93,5 +93,6 @@
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<kotlinContentScopeRefiner implementation="org.jetbrains.kotlin.analysis.api.impl.base.projectStructure.KaResolveExtensionToContentScopeRefinerBridge"/>
+ <kotlinGlobalSearchScopeMergeStrategy implementation="org.jetbrains.kotlin.analysis.api.impl.base.projectStructure.KotlinResolveExtensionGeneratedFileScopeMergeStrategy"/>
</extensions>
</idea-plugin>
\ No newline at end of file
diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaBaseResolutionScope.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaBaseResolutionScope.kt
index b4c5802..41bd679 100644
--- a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaBaseResolutionScope.kt
+++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaBaseResolutionScope.kt
@@ -16,10 +16,6 @@
import org.jetbrains.kotlin.psi.psiUtil.contains
/**
- * [KaBaseResolutionScope] encapsulates special handling required for
- * generated and shadowed scopes provided by [org.jetbrains.kotlin.analysis.api.resolve.extensions.KaResolveExtensionProvider].
- * After KT-74541 is fixed, these scopes will be contained directly in [org.jetbrains.kotlin.analysis.api.projectStructure.KaModule.contentScope].
- *
* [KaBaseResolutionScope] is not intended to be created manually,
* it's a responsibility of [org.jetbrains.kotlin.analysis.api.platform.projectStructure.KaResolutionScopeProvider]
* Please, use [Companion.forModule]
@@ -41,35 +37,36 @@
}
override fun contains(file: VirtualFile): Boolean {
- return resolutionScope.contains(file) || isFromGeneratedModule(file)
+ return resolutionScope.contains(file) || isAccessibleDanglingFile(file)
}
override fun contains(element: PsiElement): Boolean {
- return resolutionScope.contains(element) || isFromGeneratedModule(element)
+ return resolutionScope.contains(element) || isAccessibleDanglingFile(element)
}
- private fun isFromGeneratedModule(element: PsiElement): Boolean {
+ private fun isAccessibleDanglingFile(element: PsiElement): Boolean {
val ktFile = element.containingFile as? KtFile ?: return false
- if (ktFile.isDangling) {
- val module = KaModuleProvider.getModule(useSiteModule.project, ktFile, useSiteModule)
- return module.isAccessibleFromUseSiteModule()
+ if (!ktFile.isDangling) {
+ return false
}
-
- val virtualFile = ktFile.virtualFile ?: return false
- return isFromGeneratedModule(virtualFile)
+ val module = ktFile.contextModule ?: KaModuleProvider.getModule(useSiteModule.project, ktFile, useSiteModule)
+ return module.isAccessibleFromUseSiteModule()
}
- /**
- * To support files from [org.jetbrains.kotlin.analysis.api.resolve.extensions.KaResolveExtensionProvider]
- * which are not dangling files
- */
- private fun isFromGeneratedModule(virtualFile: VirtualFile): Boolean {
- val analysisContextModule = virtualFile.analysisContextModule ?: return false
- return analysisContextModule.isAccessibleFromUseSiteModule()
+ private fun isAccessibleDanglingFile(virtualFile: VirtualFile): Boolean {
+ return virtualFile.analysisContextModule?.isAccessibleFromUseSiteModule() == true
}
private fun KaModule.isAccessibleFromUseSiteModule(): Boolean {
- return this == useSiteModule || this in useSiteModule.allDirectDependencies()
+ return this in buildSet {
+ add(useSiteModule)
+ addAll(useSiteModule.directRegularDependencies)
+ addAll(useSiteModule.directFriendDependencies)
+ addAll(useSiteModule.transitiveDependsOnDependencies)
+ if (useSiteModule is KaLibrarySourceModule) {
+ add(useSiteModule.binaryLibrary)
+ }
+ }
}
override fun toString(): String {
diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaBaseResolveExtensionGeneratedFilesScope.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaBaseResolveExtensionGeneratedFilesScope.kt
new file mode 100644
index 0000000..af779c7
--- /dev/null
+++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaBaseResolveExtensionGeneratedFilesScope.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.analysis.api.impl.base.projectStructure
+
+import com.intellij.openapi.module.Module
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vfs.VirtualFile
+import org.jetbrains.kotlin.analysis.api.KaImplementationDetail
+import org.jetbrains.kotlin.analysis.api.platform.projectStructure.isGeneratedByResolveExtensions
+import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule
+import org.jetbrains.kotlin.analysis.api.projectStructure.analysisContextModule
+
+@KaImplementationDetail
+class KaBaseResolveExtensionGeneratedFilesScope(val useSiteModules: List<KaModule>) : KaResolveExtensionGeneratedFilesScope() {
+ override fun isSearchInModuleContent(aModule: Module): Boolean = false
+
+ override fun isSearchInLibraries(): Boolean = false
+
+ override fun getProject(): Project? = useSiteModules.firstOrNull()?.project
+
+ override fun contains(file: VirtualFile): Boolean {
+ return file.isGeneratedByResolveExtensions == true && file.analysisContextModule in useSiteModules
+ }
+
+ override fun toString(): String {
+ return "Resolve Extensions Generated File Scope for [" + useSiteModules.joinToString(", ") { it.moduleDescription } + "]"
+ }
+}
\ No newline at end of file
diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaResolveExtensionGeneratedFilesScope.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaResolveExtensionGeneratedFilesScope.kt
new file mode 100644
index 0000000..7e42086
--- /dev/null
+++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaResolveExtensionGeneratedFilesScope.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.analysis.api.impl.base.projectStructure
+
+import com.intellij.psi.search.GlobalSearchScope
+import org.jetbrains.kotlin.analysis.api.KaImplementationDetail
+
+/**
+ * Used to include files generated by [KaResolveExtension][org.jetbrains.kotlin.analysis.api.resolve.extensions.KaResolveExtension] in [org.jetbrains.kotlin.analysis.api.platform.projectStructure.KaResolutionScope].
+ */
+@KaImplementationDetail
+abstract class KaResolveExtensionGeneratedFilesScope : GlobalSearchScope()
\ No newline at end of file
diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaResolveExtensionToContentScopeRefinerBridge.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaResolveExtensionToContentScopeRefinerBridge.kt
index 61216ec..3a08876 100644
--- a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaResolveExtensionToContentScopeRefinerBridge.kt
+++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KaResolveExtensionToContentScopeRefinerBridge.kt
@@ -13,6 +13,13 @@
@KaImplementationDetail
class KaResolveExtensionToContentScopeRefinerBridge : KotlinContentScopeRefiner {
+ override fun getEnlargementScopes(module: KaModule): List<GlobalSearchScope> =
+ buildList {
+ if (KaResolveExtensionProvider.provideExtensionsFor(module).isNotEmpty()) {
+ add(KaBaseResolveExtensionGeneratedFilesScope(listOf(module)))
+ }
+ }
+
override fun getRestrictionScopes(module: KaModule): List<GlobalSearchScope> =
KaResolveExtensionProvider.provideExtensionsFor(module).map { resolveExtension ->
GlobalSearchScope.notScope(resolveExtension.getShadowedScope())
diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KotlinResolveExtensionGeneratedFileScopeMergeStrategy.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KotlinResolveExtensionGeneratedFileScopeMergeStrategy.kt
new file mode 100644
index 0000000..f94afb5
--- /dev/null
+++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/projectStructure/KotlinResolveExtensionGeneratedFileScopeMergeStrategy.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.analysis.api.impl.base.projectStructure
+
+import com.intellij.psi.search.GlobalSearchScope
+import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinGlobalSearchScopeMergeStrategy
+import kotlin.reflect.KClass
+
+class KotlinResolveExtensionGeneratedFileScopeMergeStrategy : KotlinGlobalSearchScopeMergeStrategy<KaBaseResolveExtensionGeneratedFilesScope> {
+ override val targetType: KClass<KaBaseResolveExtensionGeneratedFilesScope> = KaBaseResolveExtensionGeneratedFilesScope::class
+
+ override fun uniteScopes(scopes: List<KaBaseResolveExtensionGeneratedFilesScope>): List<GlobalSearchScope> {
+ val useSiteModules =
+ scopes.flatMap { scope ->
+ scope.useSiteModules
+ }
+ return listOf(KaBaseResolveExtensionGeneratedFilesScope(useSiteModules))
+ }
+}
\ No newline at end of file
diff --git a/analysis/analysis-api-platform-interface/src/org/jetbrains/kotlin/analysis/api/platform/projectStructure/utils.kt b/analysis/analysis-api-platform-interface/src/org/jetbrains/kotlin/analysis/api/platform/projectStructure/utils.kt
index 08888c2..b1e26d1 100644
--- a/analysis/analysis-api-platform-interface/src/org/jetbrains/kotlin/analysis/api/platform/projectStructure/utils.kt
+++ b/analysis/analysis-api-platform-interface/src/org/jetbrains/kotlin/analysis/api/platform/projectStructure/utils.kt
@@ -5,8 +5,13 @@
package org.jetbrains.kotlin.analysis.api.platform.projectStructure
+import com.intellij.openapi.util.Key
+import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.analysis.api.KaExperimentalApi
+import org.jetbrains.kotlin.analysis.api.KaImplementationDetail
import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule
+import org.jetbrains.kotlin.analysis.api.projectStructure.analysisContextModule
+import org.jetbrains.kotlin.psi.UserDataProperty
import org.jetbrains.kotlin.utils.topologicalSort
/**
@@ -34,3 +39,13 @@
}
append("}")
}.prependIndent(" ".repeat(indent))
+
+/**
+ * Used by the Analysis API engine
+ * to mark [VirtualFile] generated from [KaResolveExtension][org.jetbrains.kotlin.analysis.api.resolve.extensions.KaResolveExtension].
+ *
+ * This property is used to define whether [analysisContextModule] of a [VirtualFile]
+ * was set by [KaResolveExtensionProvider][org.jetbrains.kotlin.analysis.api.resolve.extensions.KaResolveExtensionProvider] or not.
+ */
+@KaImplementationDetail
+public var VirtualFile.isGeneratedByResolveExtensions: Boolean? by UserDataProperty(Key.create("IS_GENERATED_BY_RESOLVE_EXTENSIONS"))
diff --git a/analysis/analysis-api-standalone/analysis-api-fir-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/projectStructure/FirStandaloneServiceRegistrar.kt b/analysis/analysis-api-standalone/analysis-api-fir-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/projectStructure/FirStandaloneServiceRegistrar.kt
index 897a6a4..008cc3c 100644
--- a/analysis/analysis-api-standalone/analysis-api-fir-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/projectStructure/FirStandaloneServiceRegistrar.kt
+++ b/analysis/analysis-api-standalone/analysis-api-fir-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/projectStructure/FirStandaloneServiceRegistrar.kt
@@ -14,6 +14,8 @@
import org.jetbrains.kotlin.analysis.api.KaImplementationDetail
import org.jetbrains.kotlin.analysis.api.impl.base.projectStructure.KaResolveExtensionToContentScopeRefinerBridge
import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinContentScopeRefiner
+import org.jetbrains.kotlin.analysis.api.impl.base.projectStructure.KotlinResolveExtensionGeneratedFileScopeMergeStrategy
+import org.jetbrains.kotlin.analysis.api.platform.projectStructure.KotlinGlobalSearchScopeMergeStrategy
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
@OptIn(KaImplementationDetail::class)
@@ -44,5 +46,9 @@
with(project.extensionArea.getExtensionPoint(KotlinContentScopeRefiner.EP_NAME)) {
registerExtension(KaResolveExtensionToContentScopeRefinerBridge(), disposable)
}
+
+ with(project.extensionArea.getExtensionPoint(KotlinGlobalSearchScopeMergeStrategy.EP_NAME)) {
+ registerExtension(KotlinResolveExtensionGeneratedFileScopeMergeStrategy(), disposable)
+ }
}
}
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt
index 17d274b..312778a 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt
@@ -19,6 +19,7 @@
import org.jetbrains.kotlin.analysis.api.projectStructure.analysisContextModule
import org.jetbrains.kotlin.analysis.api.platform.declarations.KotlinDeclarationProvider
import org.jetbrains.kotlin.analysis.api.platform.packages.KotlinPackageProvider
+import org.jetbrains.kotlin.analysis.api.platform.projectStructure.isGeneratedByResolveExtensions
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolNamesProvider
@@ -301,6 +302,7 @@
val ktFile = factory.createFile(fileName, fileText)
val virtualFile = ktFile.virtualFile
virtualFile.analysisContextModule = ktModule
+ virtualFile.isGeneratedByResolveExtensions = true
virtualFile.navigationTargetsProvider = navigationTargetsProvider
return ktFile
}