[Analysis API] Add source shadowing for resolve extensions.
KtResolveExtensions are designed to handle IDE analysis use cases where source might not be available at analysis time, because that source is generated by an external source generator, such as an annotation processor or resource compiler. The sources generated by those external generators can appear in the analysis scope, and cause issues with source clash - resolution may find the virtual source from the KtResolveExtension, the on-disk generated source from the external generator, or both. This can cause issues, because that on-disk generated source may be stale, and may not have symbols that will exist the next time the generator is run (or, conversely, may have symbols that will disappear on the next build). To solve this, add a `getShadowedScope(): GlobalSearchScope` to `KtResolveExtension`. Any files in the module that are included in that scope will be hidden from resolution, allowing the resolve extension to cleanly replace those files. ^KT-58834 fixed
This commit is contained in:
committed by
Ilya Kirillov
parent
7c87eb098d
commit
f590e88bdd
+11
-5
@@ -23,7 +23,8 @@ import org.jetbrains.kotlin.psi.psiUtil.contains
|
||||
|
||||
class KtAnalysisScopeProviderImpl(
|
||||
override val analysisSession: KtAnalysisSession,
|
||||
override val token: KtLifetimeToken
|
||||
override val token: KtLifetimeToken,
|
||||
private val shadowedScope: GlobalSearchScope
|
||||
) : KtAnalysisScopeProvider() {
|
||||
|
||||
private val baseResolveScope by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
@@ -31,13 +32,13 @@ class KtAnalysisScopeProviderImpl(
|
||||
}
|
||||
|
||||
private val resolveScope by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
KtAnalysisScopeProviderResolveScope(baseResolveScope, analysisSession.useSiteModule)
|
||||
KtAnalysisScopeProviderResolveScope(baseResolveScope, analysisSession.useSiteModule, shadowedScope)
|
||||
}
|
||||
|
||||
override fun getAnalysisScope(): GlobalSearchScope = resolveScope
|
||||
|
||||
override fun canBeAnalysed(psi: PsiElement): Boolean {
|
||||
return baseResolveScope.contains(psi)
|
||||
return (baseResolveScope.contains(psi) && !shadowedScope.contains(psi))
|
||||
|| psi.isFromGeneratedModule()
|
||||
}
|
||||
|
||||
@@ -49,12 +50,17 @@ class KtAnalysisScopeProviderImpl(
|
||||
|
||||
private class KtAnalysisScopeProviderResolveScope(
|
||||
private val base: GlobalSearchScope,
|
||||
private val useSiteModule: KtModule
|
||||
private val useSiteModule: KtModule,
|
||||
private val shadowed: GlobalSearchScope,
|
||||
) : GlobalSearchScope() {
|
||||
override fun getProject(): Project? = base.project
|
||||
override fun isSearchInModuleContent(aModule: Module): Boolean = base.isSearchInModuleContent(aModule)
|
||||
override fun isSearchInLibraries(): Boolean = base.isSearchInLibraries
|
||||
override fun contains(file: VirtualFile): Boolean = base.contains(file) || file.isFromGeneratedModule(useSiteModule)
|
||||
override fun contains(file: VirtualFile): Boolean =
|
||||
(base.contains(file) && !shadowed.contains(file)) || file.isFromGeneratedModule(useSiteModule)
|
||||
|
||||
override fun toString() =
|
||||
"Analysis scope for $useSiteModule (base: $base, shadowed: $shadowed)"
|
||||
}
|
||||
|
||||
@OptIn(KtModuleStructureInternals::class)
|
||||
|
||||
+9
-2
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.references
|
||||
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.test.util.KtMultiModuleResolveExtensionProviderForTest
|
||||
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile
|
||||
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider
|
||||
@@ -12,6 +13,12 @@ import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
abstract class AbstractMultiModuleReferenceResolveWithResolveExtensionTest : AbstractReferenceResolveWithResolveExtensionTest() {
|
||||
override fun createResolveExtensionProvider(files: List<KtResolveExtensionFile>, packages: Set<FqName>): KtResolveExtensionProvider =
|
||||
KtMultiModuleResolveExtensionProviderForTest(files, packages) { it is KtSourceModule && it.moduleName == "extendedModule" }
|
||||
override fun createResolveExtensionProvider(
|
||||
files: List<KtResolveExtensionFile>,
|
||||
packages: Set<FqName>,
|
||||
shadowedScope: GlobalSearchScope,
|
||||
): KtResolveExtensionProvider =
|
||||
KtMultiModuleResolveExtensionProviderForTest(files, packages, shadowedScope) { module ->
|
||||
module is KtSourceModule && module.moduleName == "extendedModule"
|
||||
}
|
||||
}
|
||||
|
||||
+20
-3
@@ -5,6 +5,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.references
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.test.util.KtResolveExtensionFileForTests
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.test.util.KtResolveExtensionProviderForTestPreAnalysisHandler
|
||||
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile
|
||||
@@ -17,17 +20,22 @@ abstract class AbstractReferenceResolveWithResolveExtensionTest : AbstractRefere
|
||||
abstract fun createResolveExtensionProvider(
|
||||
files: List<KtResolveExtensionFile>,
|
||||
packages: Set<FqName>,
|
||||
shadowedScope: GlobalSearchScope,
|
||||
): KtResolveExtensionProvider
|
||||
|
||||
override fun configureTest(builder: TestConfigurationBuilder) {
|
||||
super.configureTest(builder)
|
||||
val provider = createResolveExtensionProvider(
|
||||
listOf(
|
||||
files = listOf(
|
||||
KtResolveExtensionFileForTests(
|
||||
"extension1.kt",
|
||||
packageName = FqName("generated"),
|
||||
topLevelClassifiersNames = setOf("GeneratedClass1"),
|
||||
topLevelCallableNames = setOf("generatedTopLevelFunction1", "generatedTopLevelExtensionFunction1"),
|
||||
topLevelCallableNames = setOf(
|
||||
"generatedTopLevelFunction1",
|
||||
"generatedTopLevelExtensionFunction1",
|
||||
"generatedOverloadedExtensionFunction",
|
||||
),
|
||||
fileText = """|package generated
|
||||
|
|
||||
|class GeneratedClass1 {
|
||||
@@ -37,6 +45,8 @@ abstract class AbstractReferenceResolveWithResolveExtensionTest : AbstractRefere
|
||||
|fun generatedTopLevelFunction1(): GeneratedClass2
|
||||
|
|
||||
|fun String.generatedTopLevelExtensionFunction1(boolean: Boolean): Int
|
||||
|
|
||||
|fun Any.generatedOverloadedExtensionFunction(): Int
|
||||
""".trimMargin()
|
||||
),
|
||||
KtResolveExtensionFileForTests(
|
||||
@@ -52,7 +62,14 @@ abstract class AbstractReferenceResolveWithResolveExtensionTest : AbstractRefere
|
||||
""".trimMargin(),
|
||||
)
|
||||
),
|
||||
setOf(FqName("generated"))
|
||||
packages = setOf(FqName("generated")),
|
||||
shadowedScope = object : GlobalSearchScope() {
|
||||
override fun contains(file: VirtualFile): Boolean = ".hidden." in file.name
|
||||
|
||||
override fun isSearchInModuleContent(aModule: Module): Boolean = false
|
||||
|
||||
override fun isSearchInLibraries(): Boolean = false
|
||||
}
|
||||
)
|
||||
with(builder) {
|
||||
usePreAnalysisHandlers(::KtResolveExtensionProviderForTestPreAnalysisHandler.bind(listOf(provider)))
|
||||
|
||||
+7
-2
@@ -5,12 +5,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.references
|
||||
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analysis.api.impl.base.test.util.KtSingleModuleResolveExtensionProviderForTest
|
||||
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile
|
||||
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
abstract class AbstractSingleModuleReferenceResolveWithResolveExtensionTest : AbstractReferenceResolveWithResolveExtensionTest() {
|
||||
override fun createResolveExtensionProvider(files: List<KtResolveExtensionFile>, packages: Set<FqName>): KtResolveExtensionProvider =
|
||||
KtSingleModuleResolveExtensionProviderForTest(files, packages)
|
||||
override fun createResolveExtensionProvider(
|
||||
files: List<KtResolveExtensionFile>,
|
||||
packages: Set<FqName>,
|
||||
shadowedScope: GlobalSearchScope,
|
||||
): KtResolveExtensionProvider =
|
||||
KtSingleModuleResolveExtensionProviderForTest(files, packages, shadowedScope)
|
||||
}
|
||||
|
||||
+7
-2
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.api.impl.base.test.util
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.util.ModificationTracker
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtension
|
||||
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionFile
|
||||
@@ -25,20 +26,22 @@ import org.jetbrains.kotlin.test.services.TestServices
|
||||
class KtSingleModuleResolveExtensionProviderForTest(
|
||||
private val files: List<KtResolveExtensionFile>,
|
||||
private val packages: Set<FqName>,
|
||||
private val shadowedScope: GlobalSearchScope,
|
||||
) : KtResolveExtensionProvider() {
|
||||
override fun provideExtensionsFor(module: KtModule): List<KtResolveExtension> {
|
||||
return listOf(KtResolveExtensionForTest(files, packages))
|
||||
return listOf(KtResolveExtensionForTest(files, packages, shadowedScope))
|
||||
}
|
||||
}
|
||||
|
||||
class KtMultiModuleResolveExtensionProviderForTest(
|
||||
private val files: List<KtResolveExtensionFile>,
|
||||
private val packages: Set<FqName>,
|
||||
private val shadowedScope: GlobalSearchScope,
|
||||
private val hasResolveExtension: (KtModule) -> Boolean,
|
||||
) : KtResolveExtensionProvider() {
|
||||
override fun provideExtensionsFor(module: KtModule): List<KtResolveExtension> {
|
||||
if (!hasResolveExtension(module)) return emptyList()
|
||||
return listOf(KtResolveExtensionForTest(files, packages))
|
||||
return listOf(KtResolveExtensionForTest(files, packages, shadowedScope))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,10 +62,12 @@ class KtResolveExtensionProviderForTestPreAnalysisHandler(
|
||||
class KtResolveExtensionForTest(
|
||||
private val files: List<KtResolveExtensionFile>,
|
||||
private val packages: Set<FqName>,
|
||||
private val shadowedScope: GlobalSearchScope,
|
||||
) : KtResolveExtension() {
|
||||
override fun getKtFiles(): List<KtResolveExtensionFile> = files
|
||||
override fun getModificationTracker(): ModificationTracker = ModificationTracker.NEVER_CHANGED
|
||||
override fun getContainedPackages(): Set<FqName> = packages
|
||||
override fun getShadowedScope(): GlobalSearchScope = shadowedScope
|
||||
}
|
||||
|
||||
class KtResolveExtensionFileForTests(
|
||||
|
||||
Reference in New Issue
Block a user