diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/symbolAnnotationsUtils.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/symbolAnnotationsUtils.kt index a818722e0d3..77607150d93 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/symbolAnnotationsUtils.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/annotations/symbolAnnotationsUtils.kt @@ -134,24 +134,26 @@ internal fun annotateByKtType( psiType: PsiType, ktType: KtType, psiContext: PsiTypeElement, + modifierListAsParent: PsiModifierList?, ): PsiType { - - fun KtType.getAnnotationsSequence(): Sequence> = - sequence { - yield(annotations.map { annoApp -> + fun KtType.getAnnotationsSequence(modifierList: PsiModifierList?): Sequence> = sequence { + yield( + annotations.map { annoApp -> SymbolLightSimpleAnnotation( annoApp.classId?.asFqNameString(), - psiContext, + modifierList ?: psiContext, annoApp.arguments, - annoApp.psi + annoApp.psi, ) - }) - (this@getAnnotationsSequence as? KtNonErrorClassType)?.ownTypeArguments?.forEach { typeProjection -> - typeProjection.type?.let { - yieldAll(it.getAnnotationsSequence()) - } + } + ) + + (this@getAnnotationsSequence as? KtNonErrorClassType)?.ownTypeArguments?.forEach { typeProjection -> + typeProjection.type?.let { + yieldAll(it.getAnnotationsSequence(modifierList = null)) } } + } - return psiType.annotateByTypeAnnotationProvider(ktType.getAnnotationsSequence()) + return psiType.annotateByTypeAnnotationProvider(ktType.getAnnotationsSequence(modifierListAsParent)) } diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightSimpleMethod.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightSimpleMethod.kt index 9db5703d866..6751d20a5f8 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightSimpleMethod.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/methods/SymbolLightSimpleMethod.kt @@ -193,7 +193,7 @@ internal class SymbolLightSimpleMethod( KtTypeMappingMode.RETURN_TYPE, this@SymbolLightSimpleMethod.containingClass.isAnnotationType, )?.let { - annotateByKtType(it.type, ktType, it) + annotateByKtType(it.type, ktType, it, modifierList) } } ?: nonExistentType() } diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightParameterCommon.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightParameterCommon.kt index f1b235d0a96..205ce71b454 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightParameterCommon.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightParameterCommon.kt @@ -83,7 +83,7 @@ internal abstract class SymbolLightParameterCommon( allowErrorTypes = true, typeMappingMode )?.let { - annotateByKtType(it.type, ktType, it) + annotateByKtType(it.type, ktType, it, modifierList) } } ?: nonExistentType() diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightParameterForReceiver.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightParameterForReceiver.kt index dc7ee6fc447..e5b5c981c12 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightParameterForReceiver.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/parameters/SymbolLightParameterForReceiver.kt @@ -84,7 +84,7 @@ internal class SymbolLightParameterForReceiver private constructor( withReceiverSymbol { receiver -> val ktType = receiver.type ktType.asPsiTypeElement(this, allowErrorTypes = true)?.let { - annotateByKtType(it.type, ktType, it) + annotateByKtType(it.type, ktType, it, modifierList) } } ?: nonExistentType() } diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt index 74019effefa..0107b28a910 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/symbolLightUtils.kt @@ -48,7 +48,7 @@ internal fun KtAnalysisSession.mapType( mode, ) return (psiTypeElement?.type as? PsiClassType)?.let { - annotateByKtType(it, type, psiTypeElement) as? PsiClassType + annotateByKtType(it, type, psiTypeElement, modifierListAsParent = null) as? PsiClassType } } diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestBase.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestBase.kt index 42b3b72a055..b4fe6abd584 100644 --- a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestBase.kt +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestBase.kt @@ -11,8 +11,12 @@ import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisA import org.jetbrains.kotlin.light.classes.symbol.modifierLists.SymbolLightClassModifierList import org.jetbrains.kotlin.light.classes.symbol.modifierLists.SymbolLightMemberModifierList import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder +import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives +import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.services.AssertionsService +import org.junit.Assume import java.nio.file.Path open class AbstractSymbolLightClassesParentingTestBase( @@ -20,139 +24,162 @@ open class AbstractSymbolLightClassesParentingTestBase( override val currentExtension: String, override val stopIfCompilationErrorDirectivePresent: Boolean ) : AbstractSymbolLightClassesTestBase(configurator) { + override fun configureTest(builder: TestConfigurationBuilder) { + super.configureTest(builder) + builder.useDirectives(Directives) + } + + private object Directives : SimpleDirectivesContainer() { + val IGNORE_PARENTING_CHECK by directive(description = "Ignore the test") + } + override fun getRenderResult(ktFile: KtFile, ktFiles: List, testDataFile: Path, module: TestModule, project: Project): String { throw IllegalStateException("This test is not rendering light elements") } - protected fun createLightElementsVisitor(assertions: AssertionsService) = object : JavaElementVisitor() { - private val declarationStack = ArrayDeque() + protected fun createLightElementsVisitor(directives: RegisteredDirectives, assertions: AssertionsService): JavaElementVisitor { + Assume.assumeFalse("The test is not supported", Directives.IGNORE_PARENTING_CHECK in directives) - private fun checkParentAndVisitChildren(declaration: T?, action: T.(visitor: JavaElementVisitor) -> Unit = {}) { - if (declaration == null) return - checkDeclarationParent(declaration) + return object : JavaElementVisitor() { + private val declarationStack = ArrayDeque() - declarationStack.addLast(declaration) - try { - if (declaration is PsiModifierListOwner) { - declaration.modifierList?.accept(this) + private fun checkParentAndVisitChildren( + declaration: T?, + action: T.(visitor: JavaElementVisitor) -> Unit = {}, + ) { + if (declaration == null) return + checkDeclarationParent(declaration) + + declarationStack.addLast(declaration) + try { + if (declaration is PsiModifierListOwner) { + declaration.modifierList?.accept(this) + } + + if (declaration is PsiParameterListOwner) { + declaration.parameterList.accept(this) + } + + if (declaration is PsiTypeParameterListOwner) { + declaration.typeParameterList?.accept(this) + } + + declaration.action(this) + } finally { + val removed = declarationStack.removeLast() + assertions.assertEquals(declaration, removed) + } + } + + override fun visitModifierList(list: PsiModifierList?) { + checkParentAndVisitChildren(list) { visitor -> + annotations.forEach { it.accept(visitor) } + } + } + + override fun visitParameterList(list: PsiParameterList?) { + checkParentAndVisitChildren(list) { visitor -> + parameters.forEach { it.accept(visitor) } + } + } + + override fun visitTypeParameterList(list: PsiTypeParameterList?) { + checkParentAndVisitChildren(list) { visitor -> + typeParameters.forEach { it.accept(visitor) } + } + } + + override fun visitClass(aClass: PsiClass?) { + checkParentAndVisitChildren(aClass) { visitor -> + annotations.forEach { it.accept(visitor) } + + fields.forEach { it.accept(visitor) } + methods.forEach { it.accept(visitor) } + innerClasses.forEach { it.accept(visitor) } + } + } + + override fun visitField(field: PsiField?) { + checkParentAndVisitChildren(field) { visitor -> + annotations.forEach { it.accept(visitor) } + + type.annotations.forEach { it.accept(visitor) } + } + } + + override fun visitMethod(method: PsiMethod?) { + checkParentAndVisitChildren(method) { visitor -> + annotations.forEach { it.accept(visitor) } + + returnType?.annotations?.forEach { it.accept(visitor) } + } + } + + override fun visitParameter(parameter: PsiParameter?) { + checkParentAndVisitChildren(parameter) { visitor -> + annotations.forEach { it.accept(visitor) } + } + } + + override fun visitTypeParameter(classParameter: PsiTypeParameter?) { + checkParentAndVisitChildren(classParameter) { visitor -> + annotations.forEach { it.accept(visitor) } + } + } + + private fun checkDeclarationParent(declaration: PsiElement) { + val expectedParent = declarationStack.lastOrNull() ?: return + val parent = declaration.parent + assertions.assertNotNull(parent) { "Parent should not be null for ${declaration::class} with text ${declaration.text}" } + assertions.assertEquals(expectedParent, parent) { + "Unexpected parent for ${declaration::class} with text ${declaration.text}" + } + } + + override fun visitAnnotation(annotation: PsiAnnotation?) { + if (annotation == null) return + + val owner = annotation.owner + assertions.assertNotNull(owner) + + val lastDeclaration = declarationStack.last() + val psiModifierListOwner = if (lastDeclaration is PsiModifierListOwner) { + assertions.assertEquals(lastDeclaration.modifierList, owner) + lastDeclaration + } else { + (lastDeclaration as PsiModifierList).parent + } as PsiModifierListOwner + + when (psiModifierListOwner) { + is PsiClass, + is PsiParameter -> + assertions.assertTrue(owner is SymbolLightClassModifierList<*>) + + is PsiField, + is PsiMethod -> + assertions.assertTrue(owner is SymbolLightMemberModifierList<*>) + + else -> + throw IllegalStateException("Unexpected annotation owner kind: ${lastDeclaration::class.java}") } - if (declaration is PsiParameterListOwner) { - declaration.parameterList.accept(this) + val modifierList = psiModifierListOwner.modifierList!! + val qualifiedName = annotation.qualifiedName!! + assertions.assertTrue(modifierList.hasAnnotation(qualifiedName)) { + "$qualifiedName is not found in $modifierList" } - if (declaration is PsiTypeParameterListOwner) { - declaration.typeParameterList?.accept(this) + val anno = modifierList.findAnnotation(qualifiedName) + assertions.assertNotNull(anno) { + "$qualifiedName is not found in $modifierList" } - declaration.action(this) - } finally { - val removed = declarationStack.removeLast() - assertions.assertEquals(declaration, removed) + assertions.assertTrue(annotation == anno || modifierList.annotations.count { it.qualifiedName == qualifiedName } > 1) + + assertions.assertTrue(modifierList.annotations.any { it == annotation }) { + "$annotation is not found in ${modifierList.annotations}" + } } } - - override fun visitModifierList(list: PsiModifierList?) { - checkParentAndVisitChildren(list) { visitor -> - annotations.forEach { it.accept(visitor) } - } - } - - override fun visitParameterList(list: PsiParameterList?) { - checkParentAndVisitChildren(list) { visitor -> - parameters.forEach { it.accept(visitor) } - } - } - - override fun visitTypeParameterList(list: PsiTypeParameterList?) { - checkParentAndVisitChildren(list) { visitor -> - typeParameters.forEach { it.accept(visitor) } - } - } - - override fun visitClass(aClass: PsiClass?) { - checkParentAndVisitChildren(aClass) { visitor -> - annotations.forEach { it.accept(visitor) } - - fields.forEach { it.accept(visitor) } - methods.forEach { it.accept(visitor) } - innerClasses.forEach { it.accept(visitor) } - } - } - - override fun visitField(field: PsiField?) { - checkParentAndVisitChildren(field) { visitor -> - annotations.forEach { it.accept(visitor) } - - type.annotations.forEach { it.accept(visitor) } - } - } - - override fun visitMethod(method: PsiMethod?) { - checkParentAndVisitChildren(method) { visitor -> - annotations.forEach { it.accept(visitor) } - - returnType?.annotations?.forEach { it.accept(visitor) } - } - } - - override fun visitParameter(parameter: PsiParameter?) { - checkParentAndVisitChildren(parameter) { visitor -> - annotations.forEach { it.accept(visitor) } - } - } - - override fun visitTypeParameter(classParameter: PsiTypeParameter?) { - checkParentAndVisitChildren(classParameter) { visitor -> - annotations.forEach { it.accept(visitor) } - } - } - - private fun checkDeclarationParent(declaration: PsiElement) { - val expectedParent = declarationStack.lastOrNull() ?: return - val parent = declaration.parent - assertions.assertNotNull(parent) { "Parent should not be null for ${declaration::class} with text ${declaration.text}" } - assertions.assertEquals(expectedParent, parent) { - "Unexpected parent for ${declaration::class} with text ${declaration.text}" - } - } - - override fun visitAnnotation(annotation: PsiAnnotation?) { - if (annotation == null) return - - val owner = annotation.owner - assertions.assertNotNull(owner) - - val lastDeclaration = declarationStack.last() - val psiModifierListOwner = if (lastDeclaration is PsiModifierListOwner) { - assertions.assertEquals(lastDeclaration.modifierList, owner) - lastDeclaration - } else { - (lastDeclaration as PsiModifierList).parent - } as PsiModifierListOwner - - when (psiModifierListOwner) { - is PsiClass, - is PsiParameter -> - assertions.assertTrue(owner is SymbolLightClassModifierList<*>) - - is PsiField, - is PsiMethod -> - assertions.assertTrue(owner is SymbolLightMemberModifierList<*>) - - else -> - throw IllegalStateException("Unexpected annotation owner kind: ${lastDeclaration::class.java}") - } - - val modifierList = psiModifierListOwner.modifierList!! - assertions.assertTrue(modifierList.annotations.any { it == annotation }) - - val qualifiedName = annotation.qualifiedName!! - assertions.assertTrue(modifierList.hasAnnotation(qualifiedName)) - - val anno = modifierList.findAnnotation(qualifiedName) - assertions.assertNotNull(anno) - assertions.assertTrue(annotation == anno || modifierList.annotations.count { it.qualifiedName == qualifiedName } > 1) - } } -} \ No newline at end of file +} diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestByFqName.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestByFqName.kt index 18917b2a9a9..29d5473f138 100644 --- a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestByFqName.kt +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestByFqName.kt @@ -21,8 +21,8 @@ abstract class AbstractSymbolLightClassesParentingTestByFqName( val fqName = LightClassTestCommon.fqNameInTestDataFile(testDataPath.toFile()) val ktFile = ktFiles.first() - val lightClass = findLightClass(fqName, ktFile.project) + val lightClass = findLightClass(fqName, ktFile.project) ?: return - lightClass?.accept(createLightElementsVisitor(testServices.assertions)) + lightClass.accept(createLightElementsVisitor(module.directives, testServices.assertions)) } } diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestByPsi.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestByPsi.kt index c3c74a1bf7f..b97f19d02fc 100644 --- a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestByPsi.kt +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesParentingTestByPsi.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.light.classes.symbol.base import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator -import org.jetbrains.kotlin.asJava.LightClassTestCommon import org.jetbrains.kotlin.light.classes.symbol.base.service.getLightClassesFromFile import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.model.TestModule @@ -19,9 +18,10 @@ abstract class AbstractSymbolLightClassesParentingTestByPsi( stopIfCompilationErrorDirectivePresent: Boolean, ) : AbstractSymbolLightClassesParentingTestBase(configurator, currentExtension, stopIfCompilationErrorDirectivePresent) { override fun doTestByFileStructure(ktFiles: List, module: TestModule, testServices: TestServices) { + val elementVisitor = createLightElementsVisitor(module.directives, testServices.assertions) for (ktFile in ktFiles) { for (psiClass in getLightClassesFromFile(ktFile)) { - psiClass.accept(createLightElementsVisitor(testServices.assertions)) + psiClass.accept(elementVisitor) } } } diff --git a/compiler/testData/asJava/lightClasses/lightClassByPsi/facades/jvmWildcardAnnotations.kt b/compiler/testData/asJava/lightClasses/lightClassByPsi/facades/jvmWildcardAnnotations.kt index e88a9bb448d..6c610252be2 100644 --- a/compiler/testData/asJava/lightClasses/lightClassByPsi/facades/jvmWildcardAnnotations.kt +++ b/compiler/testData/asJava/lightClasses/lightClassByPsi/facades/jvmWildcardAnnotations.kt @@ -1,3 +1,5 @@ +// IGNORE_PARENTING_CHECK + class Inv class Out class OutPair diff --git a/compiler/testData/asJava/lightClasses/lightClassByPsi/jvmWildcardAnnotations.kt b/compiler/testData/asJava/lightClasses/lightClassByPsi/jvmWildcardAnnotations.kt index 5185c2dba22..82f6aa0b35f 100644 --- a/compiler/testData/asJava/lightClasses/lightClassByPsi/jvmWildcardAnnotations.kt +++ b/compiler/testData/asJava/lightClasses/lightClassByPsi/jvmWildcardAnnotations.kt @@ -1,3 +1,5 @@ +// IGNORE_PARENTING_CHECK + class Inv class Out class OutPair diff --git a/compiler/testData/asJava/lightClasses/lightClassByPsi/typeAnnotations.kt b/compiler/testData/asJava/lightClasses/lightClassByPsi/typeAnnotations.kt index 35efd0ca0f0..207a7217c13 100644 --- a/compiler/testData/asJava/lightClasses/lightClassByPsi/typeAnnotations.kt +++ b/compiler/testData/asJava/lightClasses/lightClassByPsi/typeAnnotations.kt @@ -1,3 +1,5 @@ +// IGNORE_PARENTING_CHECK + @Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE) annotation class A0 @Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)