diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt index e774866da3a..d2ae6dd1890 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/FirUtils.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.analysis.api.fir import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationInfo import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationWithArgumentsInfo +import org.jetbrains.kotlin.analysis.api.annotations.KtNamedAnnotationValue import org.jetbrains.kotlin.analysis.api.fir.annotations.mapAnnotationParameters import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirAnnotationValueConverter import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol @@ -71,14 +72,15 @@ internal fun ConeDiagnostic.getCandidateSymbols(): Collection> internal fun FirAnnotation.toKtAnnotationApplication( useSiteSession: FirSession, index: Int, + arguments: List = FirAnnotationValueConverter.toNamedConstantValue( + mapAnnotationParameters(this), + useSiteSession, + ) ): KtAnnotationApplicationWithArgumentsInfo = KtAnnotationApplicationWithArgumentsInfo( classId = toAnnotationClassId(useSiteSession), psi = psi as? KtCallElement, useSiteTarget = useSiteTarget, - arguments = FirAnnotationValueConverter.toNamedConstantValue( - mapAnnotationParameters(this), - useSiteSession, - ), + arguments = arguments, index = index, ) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt index 9ef20924163..ea747fddfbc 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt @@ -8,13 +8,18 @@ package org.jetbrains.kotlin.analysis.api.fir.annotations import org.jetbrains.kotlin.analysis.api.annotations.AnnotationUseSiteTargetFilter import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationInfo import org.jetbrains.kotlin.analysis.api.annotations.KtAnnotationApplicationWithArgumentsInfo +import org.jetbrains.kotlin.analysis.api.annotations.KtArrayAnnotationValue +import org.jetbrains.kotlin.analysis.api.annotations.KtEnumEntryAnnotationValue +import org.jetbrains.kotlin.analysis.api.annotations.KtNamedAnnotationValue import org.jetbrains.kotlin.analysis.api.fir.toKtAnnotationApplication import org.jetbrains.kotlin.analysis.api.fir.toKtAnnotationInfo import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirEntry import org.jetbrains.kotlin.analysis.utils.errors.checkWithAttachmentBuilder import org.jetbrains.kotlin.analysis.utils.errors.withClassEntry +import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.fir.FirAnnotationContainer import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.containingClassLookupTag import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.resolved import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId @@ -31,6 +36,13 @@ import org.jetbrains.kotlin.fir.symbols.resolvedCompilerRequiredAnnotations import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.fir.declarations.resolvePhase +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.expressions.unwrapAndFlattenArgument +import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty internal fun mapAnnotationParameters(annotation: FirAnnotation): Map { if (annotation is FirAnnotationCall && annotation.arguments.isEmpty()) return emptyMap() @@ -50,13 +62,72 @@ internal fun annotationsByClassId( useSiteTargetFilter: AnnotationUseSiteTargetFilter, useSiteSession: FirSession, annotationContainer: FirAnnotationContainer = firSymbol.fir, -): List { - return annotationContainer.resolvedAnnotationsWithArguments(firSymbol).mapIndexedNotNull { index, annotation -> - if (!useSiteTargetFilter.isAllowed(annotation.useSiteTarget) || annotation.toAnnotationClassId(useSiteSession) != classId) { - return@mapIndexedNotNull null - } +): List = + if (classId == StandardClassIds.Annotations.Target && firSymbol.fir.resolvePhase < FirResolvePhase.ANNOTATIONS_ARGUMENTS_MAPPING) { + annotationContainer.resolvedAnnotationsWithClassIds(firSymbol) + .mapIndexedToAnnotationApplication(useSiteTargetFilter, useSiteSession, classId) { index, annotation -> + annotation.asKtAnnotationApplicationForTargetAnnotation(useSiteSession, index) + } + } else { + annotationContainer.resolvedAnnotationsWithArguments(firSymbol) + .mapIndexedToAnnotationApplication(useSiteTargetFilter, useSiteSession, classId) { index, annotation -> + annotation.toKtAnnotationApplication(useSiteSession, index) + } + } - annotation.toKtAnnotationApplication(useSiteSession, index) +private inline fun List.mapIndexedToAnnotationApplication( + useSiteTargetFilter: AnnotationUseSiteTargetFilter, + useSiteSession: FirSession, + classId: ClassId, + transformer: (index: Int, annotation: FirAnnotation) -> KtAnnotationApplicationWithArgumentsInfo?, +): List = mapIndexedNotNull { index, annotation -> + if (!useSiteTargetFilter.isAllowed(annotation.useSiteTarget) || annotation.toAnnotationClassId(useSiteSession) != classId) { + return@mapIndexedNotNull null + } + + transformer(index, annotation) +} + +private fun FirAnnotation.asKtAnnotationApplicationForTargetAnnotation( + useSiteSession: FirSession, + index: Int, +): KtAnnotationApplicationWithArgumentsInfo { + val arguments = findAllowedTargets().ifNotEmpty { + listOf( + KtNamedAnnotationValue( + name = StandardClassIds.Annotations.ParameterNames.targetAllowedTargets, + expression = KtArrayAnnotationValue( + values = map { + KtEnumEntryAnnotationValue( + callableId = CallableId( + classId = StandardClassIds.AnnotationTarget, + callableName = Name.identifier(it.name), + ), + sourcePsi = null, + ) + }, + sourcePsi = null, + ) + ) + ) + }.orEmpty() + + return toKtAnnotationApplication(useSiteSession, index, arguments) +} + +private fun FirAnnotation.findAllowedTargets(): Set = buildSet { + fun addIfMatching(arg: FirExpression) { + if (arg !is FirQualifiedAccessExpression) return + val callableSymbol = arg.calleeReference.toResolvedCallableSymbol() ?: return + if (callableSymbol.containingClassLookupTag()?.classId != StandardClassIds.AnnotationTarget) return + val identifier = callableSymbol.callableId.callableName.identifier + KotlinTarget.values().firstOrNull { identifier == it.name }?.let(::add) + } + + if (this@findAllowedTargets is FirAnnotationCall) { + for (arg in argumentList.arguments) { + arg.unwrapAndFlattenArgument().forEach(::addIfMatching) + } } } diff --git a/analysis/low-level-api-fir/testdata/lazyResolve/annotationFromImplicitJavaType.kt b/analysis/low-level-api-fir/testdata/lazyResolve/annotationFromImplicitJavaType.kt new file mode 100644 index 00000000000..f3ee9c252b3 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/lazyResolve/annotationFromImplicitJavaType.kt @@ -0,0 +1,18 @@ +// SKIP_WHEN_OUT_OF_CONTENT_ROOT +// DISABLE_SEALED_INHERITOR_CALCULATOR +// FILE: main.kt +fun resolveMe(i: JavaInterface) = i.id + +// FILE: JavaClass.java +public interface JavaInterface { + @KotlinAnnotation + default int getId() { + return -1; + } +} + +// FILE: KotlinAnnotation.kt +@Target( + AnnotationTarget.TYPE +) +annotation class KotlinAnnotation \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/lazyResolve/annotationFromImplicitJavaType.txt b/analysis/low-level-api-fir/testdata/lazyResolve/annotationFromImplicitJavaType.txt new file mode 100644 index 00000000000..fc6d4ee5fcc --- /dev/null +++ b/analysis/low-level-api-fir/testdata/lazyResolve/annotationFromImplicitJavaType.txt @@ -0,0 +1,79 @@ +RAW_FIR: +FILE: [ResolvedTo(RAW_FIR)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public? final? [ResolvedTo(RAW_FIR)] fun resolveMe([ResolvedTo(RAW_FIR)] i: JavaInterface): { LAZY_BLOCK } + +IMPORTS: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public? final? [ResolvedTo(RAW_FIR)] fun resolveMe([ResolvedTo(RAW_FIR)] i: JavaInterface): { LAZY_BLOCK } + +COMPILER_REQUIRED_ANNOTATIONS: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public? final? [ResolvedTo(COMPILER_REQUIRED_ANNOTATIONS)] fun resolveMe([ResolvedTo(COMPILER_REQUIRED_ANNOTATIONS)] i: JavaInterface): { LAZY_BLOCK } + +COMPANION_GENERATION: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public? final? [ResolvedTo(COMPANION_GENERATION)] fun resolveMe([ResolvedTo(COMPANION_GENERATION)] i: JavaInterface): { LAZY_BLOCK } + +SUPER_TYPES: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public? final? [ResolvedTo(SUPER_TYPES)] fun resolveMe([ResolvedTo(SUPER_TYPES)] i: JavaInterface): { LAZY_BLOCK } + +TYPES: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public? final? [ResolvedTo(TYPES)] fun resolveMe([ResolvedTo(TYPES)] i: R|JavaInterface|): { LAZY_BLOCK } + +STATUS: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public final [ResolvedTo(STATUS)] fun resolveMe([ResolvedTo(STATUS)] i: R|JavaInterface|): { LAZY_BLOCK } + +EXPECT_ACTUAL_MATCHING: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public final [ResolvedTo(EXPECT_ACTUAL_MATCHING)] fun resolveMe([ResolvedTo(EXPECT_ACTUAL_MATCHING)] i: R|JavaInterface|): { LAZY_BLOCK } + +ARGUMENTS_OF_ANNOTATIONS: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public final [ResolvedTo(ARGUMENTS_OF_ANNOTATIONS)] fun resolveMe([ResolvedTo(ARGUMENTS_OF_ANNOTATIONS)] i: R|JavaInterface|): { LAZY_BLOCK } + +CONTRACTS: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public final [ResolvedTo(CONTRACTS)] fun resolveMe([ResolvedTo(CONTRACTS)] i: R|JavaInterface|): { + ^resolveMe i#.id# + } + +IMPLICIT_TYPES_BODY_RESOLVE: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public final [ResolvedTo(IMPLICIT_TYPES_BODY_RESOLVE)] fun resolveMe([ResolvedTo(IMPLICIT_TYPES_BODY_RESOLVE)] i: R|JavaInterface|): R|@R|KotlinAnnotation|() kotlin/Int| { + ^resolveMe R|/i|.R|/JavaInterface.id| + } + +ANNOTATIONS_ARGUMENTS_MAPPING: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public final [ResolvedTo(ANNOTATIONS_ARGUMENTS_MAPPING)] fun resolveMe([ResolvedTo(ANNOTATIONS_ARGUMENTS_MAPPING)] i: R|JavaInterface|): R|@R|KotlinAnnotation|() kotlin/Int| { + ^resolveMe R|/i|.R|/JavaInterface.id| + } + +BODY_RESOLVE: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(RAW_FIR)] annotations container + public final [ResolvedTo(BODY_RESOLVE)] fun resolveMe([ResolvedTo(BODY_RESOLVE)] i: R|JavaInterface|): R|@R|KotlinAnnotation|() kotlin/Int| { + ^resolveMe R|/i|.R|/JavaInterface.id| + } + +FILE RAW TO BODY: +FILE: [ResolvedTo(IMPORTS)] main.kt + [ResolvedTo(BODY_RESOLVE)] annotations container + public final [ResolvedTo(BODY_RESOLVE)] fun resolveMe([ResolvedTo(BODY_RESOLVE)] i: R|JavaInterface|): R|@R|KotlinAnnotation|() kotlin/Int| { + ^resolveMe R|/i|.R|/JavaInterface.id| + } diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyDeclarationResolveTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyDeclarationResolveTestGenerated.java index 4fe76974eff..da1ab460100 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyDeclarationResolveTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirOutOfContentRootLazyDeclarationResolveTestGenerated.java @@ -24,6 +24,12 @@ public class FirOutOfContentRootLazyDeclarationResolveTestGenerated extends Abst KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testdata/lazyResolve"), Pattern.compile("^(.+)\\.kt$"), null, true); } + @Test + @TestMetadata("annotationFromImplicitJavaType.kt") + public void testAnnotationFromImplicitJavaType() throws Exception { + runTest("analysis/low-level-api-fir/testdata/lazyResolve/annotationFromImplicitJavaType.kt"); + } + @Test @TestMetadata("annotationParameters.kt") public void testAnnotationParameters() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyDeclarationResolveTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyDeclarationResolveTestGenerated.java index 1eab277df55..eefac6ea45d 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyDeclarationResolveTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/FirSourceLazyDeclarationResolveTestGenerated.java @@ -24,6 +24,12 @@ public class FirSourceLazyDeclarationResolveTestGenerated extends AbstractFirSou KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testdata/lazyResolve"), Pattern.compile("^(.+)\\.kt$"), null, true); } + @Test + @TestMetadata("annotationFromImplicitJavaType.kt") + public void testAnnotationFromImplicitJavaType() throws Exception { + runTest("analysis/low-level-api-fir/testdata/lazyResolve/annotationFromImplicitJavaType.kt"); + } + @Test @TestMetadata("annotationParameters.kt") public void testAnnotationParameters() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AbstractLowLevelApiSingleFileTest.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AbstractLowLevelApiSingleFileTest.kt index ebe4d406dc1..1acf51416b6 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AbstractLowLevelApiSingleFileTest.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/base/AbstractLowLevelApiSingleFileTest.kt @@ -14,7 +14,7 @@ import org.jetbrains.kotlin.test.services.TestServices abstract class AbstractLowLevelApiSingleFileTest : AbstractAnalysisApiBasedTest() { final override fun doTestByModuleStructure(moduleStructure: TestModuleStructure, testServices: TestServices) { val singleModule = moduleStructure.modules.single() - val singleFile = testServices.ktModuleProvider.getModuleFiles(singleModule).filterIsInstance().single() + val singleFile = testServices.ktModuleProvider.getModuleFiles(singleModule).filterIsInstance().first() doTestByFileStructure(singleFile, moduleStructure, testServices) } diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirOutOfContentRootTestConfigurator.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirOutOfContentRootTestConfigurator.kt index 73b86d33226..504134b2fa8 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirOutOfContentRootTestConfigurator.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/test/configurators/AnalysisApiFirOutOfContentRootTestConfigurator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2023 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. */ @@ -61,7 +61,7 @@ private class KtOutOfContentRootModuleFactory : KtModuleFactory { override fun createModule(testModule: TestModule, testServices: TestServices, project: Project): KtModuleWithFiles { val psiFiles = TestModuleStructureFactory.createSourcePsiFiles(testModule, testServices, project) val platform = testModule.targetPlatform - val module = KtNotUnderContentRootModuleForTest(testModule.name, psiFiles.single(), platform) + val module = KtNotUnderContentRootModuleForTest(testModule.name, psiFiles.first(), platform) return KtModuleWithFiles(module, psiFiles) } }