KT-63096 [LL] Make KotlinStaticAnnotationsResolver more correct

By using `KotlinDeclarationProvider`, it can now check that the
annotation is actually present in the scope
This commit is contained in:
Roman Golyshev
2023-11-06 18:37:19 +01:00
committed by teamcity
parent a1155204c7
commit 2f50267d3f
3 changed files with 28 additions and 6 deletions
@@ -72,7 +72,7 @@ object AnalysisApiBaseTestServiceRegistrar: AnalysisApiTestServiceRegistrar() {
testServices.environmentManager.getProjectEnvironment() testServices.environmentManager.getProjectEnvironment()
).distinct() ).distinct()
project.apply { project.apply {
registerService(KotlinAnnotationsResolverFactory::class.java, KotlinStaticAnnotationsResolverFactory(allSourceKtFiles)) registerService(KotlinAnnotationsResolverFactory::class.java, KotlinStaticAnnotationsResolverFactory(project, allSourceKtFiles))
val filter = BuiltInDefinitionFile.FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES val filter = BuiltInDefinitionFile.FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES
val ktFilesForBinaries: List<KtFile> val ktFilesForBinaries: List<KtFile>
@@ -5,22 +5,33 @@
package org.jetbrains.kotlin.analysis.providers.impl package org.jetbrains.kotlin.analysis.providers.impl
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.providers.KotlinAnnotationsResolver import org.jetbrains.kotlin.analysis.providers.KotlinAnnotationsResolver
import org.jetbrains.kotlin.analysis.providers.KotlinAnnotationsResolverFactory import org.jetbrains.kotlin.analysis.providers.KotlinAnnotationsResolverFactory
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.utils.filterIsInstanceAnd
/** /**
* This implementation works only for fully-qualified annotations usages, and does not check if they resolve somewhere. * This implementation works only for FQN annotations usages (`@foo.Bar` instead of `@Bar`).
* It does not perform the full resolve of the annotation call, but it checks whether the annotation
* with such FQN is present in the [scope] with [KotlinDeclarationProvider].
* *
* Required for the test infrastructure. * Required for and used only in the test infrastructure.
*/ */
private class KotlinStaticAnnotationsResolver( private class KotlinStaticAnnotationsResolver(
private val project: Project,
ktFiles: Collection<KtFile>, ktFiles: Collection<KtFile>,
scope: GlobalSearchScope scope: GlobalSearchScope
) : KotlinAnnotationsResolver { ) : KotlinAnnotationsResolver {
private val declarationProvider: KotlinDeclarationProvider by lazy {
project.createDeclarationProvider(scope, contextualModule = null)
}
private val filesInScope = ktFiles.filter { scope.contains(it.virtualFile) } private val filesInScope = ktFiles.filter { scope.contains(it.virtualFile) }
private val allDeclarations: List<KtDeclaration> by lazy { private val allDeclarations: List<KtDeclaration> by lazy {
@@ -54,12 +65,23 @@ private class KotlinStaticAnnotationsResolver(
return declaration.annotationEntries.asSequence() return declaration.annotationEntries.asSequence()
.mapNotNull { it.typeReference?.text } .mapNotNull { it.typeReference?.text }
.map { ClassId.topLevel(FqName(it)) } .map { ClassId.topLevel(FqName(it)) }
.filter { it.resolveToAnnotation() != null }
.toSet() .toSet()
} }
private fun ClassId.resolveToAnnotation(): KtClass? {
val classes = declarationProvider.getAllClassesByClassId(this)
val annotations = classes.filterIsInstanceAnd<KtClass> { it.isAnnotation() }
return annotations.singleOrNull()
}
} }
public class KotlinStaticAnnotationsResolverFactory(private val files: Collection<KtFile>) : KotlinAnnotationsResolverFactory { public class KotlinStaticAnnotationsResolverFactory(
private val project: Project,
private val files: Collection<KtFile>,
) : KotlinAnnotationsResolverFactory {
override fun createAnnotationResolver(searchScope: GlobalSearchScope): KotlinAnnotationsResolver { override fun createAnnotationResolver(searchScope: GlobalSearchScope): KotlinAnnotationsResolver {
return KotlinStaticAnnotationsResolver(files, searchScope) return KotlinStaticAnnotationsResolver(project, files, searchScope)
} }
} }
@@ -125,7 +125,7 @@ public class StandaloneAnalysisAPISessionBuilder(
registerService(KotlinModificationTrackerFactory::class.java, KotlinStaticModificationTrackerFactory::class.java) registerService(KotlinModificationTrackerFactory::class.java, KotlinStaticModificationTrackerFactory::class.java)
registerService(KotlinGlobalModificationService::class.java, KotlinStaticGlobalModificationService::class.java) registerService(KotlinGlobalModificationService::class.java, KotlinStaticGlobalModificationService::class.java)
registerService(KotlinAnnotationsResolverFactory::class.java, KotlinStaticAnnotationsResolverFactory(sourceKtFiles)) registerService(KotlinAnnotationsResolverFactory::class.java, KotlinStaticAnnotationsResolverFactory(this, sourceKtFiles))
registerService(KotlinResolutionScopeProvider::class.java, KotlinByModulesResolutionScopeProvider::class.java) registerService(KotlinResolutionScopeProvider::class.java, KotlinByModulesResolutionScopeProvider::class.java)
val declarationProviderFactory = KotlinStaticDeclarationProviderFactory( val declarationProviderFactory = KotlinStaticDeclarationProviderFactory(
this, this,