[FIR] Remove duplicate annotations from primary ctor params/properties
If an annotation doesn't specify an explicit use-site target, previously it was added to both, the primary constructor value parameter and the property in the FIR. Then, in FIR2IR, only the "correct" one was added to the IR. Move up the deduplication logic into the frontend. ^KT-56177 Fixed
This commit is contained in:
committed by
Space Team
parent
9268fd0e87
commit
eee66ab43f
@@ -10,7 +10,7 @@ FILE: kt55286.kt
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
@R|Deprecated<Base.Nested>|() public final val a: R|kotlin/String| = R|<local>/a|
|
||||
public final val a: R|kotlin/String| = R|<local>/a|
|
||||
public get(): R|kotlin/String|
|
||||
|
||||
public final class Nested : R|kotlin/Any| {
|
||||
@@ -26,7 +26,7 @@ FILE: kt55286.kt
|
||||
super<R|Base|>(String())
|
||||
}
|
||||
|
||||
@R|Deprecated<Base.Nested>|() public final val b: R|kotlin/String| = R|<local>/b|
|
||||
public final val b: R|kotlin/String| = R|<local>/b|
|
||||
public get(): R|kotlin/String|
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ FILE: test.kt
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
@R|KotlinMessage|() public final val message: R|kotlin/String| = R|<local>/message|
|
||||
public final val message: R|kotlin/String| = R|<local>/message|
|
||||
@PROPERTY_GETTER:R|KotlinMessage|() public get(): R|kotlin/String|
|
||||
|
||||
public final operator fun component1(): R|kotlin/String|
|
||||
|
||||
+1
-3
@@ -46,8 +46,6 @@ object FirNativeSharedImmutableChecker : FirBasicDeclarationChecker() {
|
||||
return
|
||||
}
|
||||
|
||||
if (declaration.source?.kind is KtFakeSourceElementKind) return
|
||||
|
||||
if (!context.isTopLevel) {
|
||||
reporter.reportIfHasAnnotation(
|
||||
declaration,
|
||||
@@ -57,4 +55,4 @@ object FirNativeSharedImmutableChecker : FirBasicDeclarationChecker() {
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-10
@@ -72,7 +72,7 @@ fun FirRegularClass.getAllowedAnnotationTargets(session: FirSession): Set<Kotlin
|
||||
fun FirClassLikeSymbol<*>.getAllowedAnnotationTargets(session: FirSession): Set<KotlinTarget> {
|
||||
lazyResolveToPhase(FirResolvePhase.ANNOTATIONS_ARGUMENTS_MAPPING)
|
||||
val targetAnnotation = getTargetAnnotation(session) ?: return defaultAnnotationTargets
|
||||
val arguments = targetAnnotation.findArgumentByName(ParameterNames.targetAllowedTargets)?.unfoldArrayOrVararg().orEmpty()
|
||||
val arguments = targetAnnotation.findArgumentByName(ParameterNames.targetAllowedTargets)?.unwrapAndFlattenArgument().orEmpty()
|
||||
|
||||
return arguments.mapNotNullTo(mutableSetOf()) { argument ->
|
||||
val targetExpression = argument as? FirQualifiedAccessExpression
|
||||
@@ -94,7 +94,7 @@ fun FirClassLikeSymbol<*>.getTargetAnnotation(session: FirSession): FirAnnotatio
|
||||
}
|
||||
|
||||
fun FirExpression.extractClassesFromArgument(session: FirSession): List<FirRegularClassSymbol> {
|
||||
return unfoldArrayOrVararg().mapNotNull {
|
||||
return unwrapAndFlattenArgument().mapNotNull {
|
||||
it.extractClassFromArgument(session)
|
||||
}
|
||||
}
|
||||
@@ -178,14 +178,6 @@ fun FirAnnotationContainer.getImplicitUseSiteTargetList(context: CheckerContext)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirExpression.unfoldArrayOrVararg(): List<FirExpression> {
|
||||
return when (this) {
|
||||
is FirVarargArgumentsExpression -> arguments
|
||||
is FirArrayOfCall -> arguments
|
||||
else -> return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun checkRepeatedAnnotation(
|
||||
annotationContainer: FirAnnotationContainer?,
|
||||
annotations: List<FirAnnotation>,
|
||||
|
||||
-3
@@ -162,7 +162,6 @@ object FirAnnotationChecker : FirBasicDeclarationChecker() {
|
||||
context
|
||||
)
|
||||
} else {
|
||||
if (declaration is FirProperty && declaration.source?.kind == KtFakeSourceElementKind.PropertyFromParameter) return
|
||||
reporter.reportOn(
|
||||
annotation.source,
|
||||
FirErrors.WRONG_ANNOTATION_TARGET,
|
||||
@@ -213,8 +212,6 @@ object FirAnnotationChecker : FirBasicDeclarationChecker() {
|
||||
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_PARAM_TARGET, context)
|
||||
}
|
||||
}
|
||||
annotated is FirProperty && annotated.source?.kind == KtFakeSourceElementKind.PropertyFromParameter -> {
|
||||
}
|
||||
else -> reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_PARAM_TARGET, context)
|
||||
}
|
||||
FILE -> {
|
||||
|
||||
+1
-1
@@ -1657,7 +1657,7 @@ class Fir2IrDeclarationStorage(
|
||||
annotationGenerator.generate(this, firAnnotationContainer)
|
||||
if (this is IrFunction && firAnnotationContainer is FirSimpleFunction) {
|
||||
valueParameters.zip(firAnnotationContainer.valueParameters).forEach { (irParameter, firParameter) ->
|
||||
annotationGenerator.generate(irParameter, firParameter, isInConstructor = false)
|
||||
annotationGenerator.generate(irParameter, firParameter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-24
@@ -24,6 +24,9 @@ import org.jetbrains.kotlin.ir.util.isSetter
|
||||
* need special handling: [AnnotationUseSiteTarget]. In particular, [FirProperty] contains all annotations associated with that property,
|
||||
* whose targets may vary. After all the necessary pieces of IR elements, e.g., backing field, are ready, this generator splits those
|
||||
* annotations to the specified targets.
|
||||
*
|
||||
* Note: Annotations on primary constructor properties are already split between value parameters and properties in FIR. Before this change,
|
||||
* it used to be done here.
|
||||
*/
|
||||
class AnnotationGenerator(private val components: Fir2IrComponents) : Fir2IrComponents by components {
|
||||
|
||||
@@ -40,32 +43,16 @@ class AnnotationGenerator(private val components: Fir2IrComponents) : Fir2IrComp
|
||||
useSiteTarget ?: applicable.firstOrNull(useSiteTargetsFromMetaAnnotation(session)::contains)
|
||||
|
||||
companion object {
|
||||
// Priority order: constructor parameter (if applicable) -> property -> field. So, for example, if `A`
|
||||
// can be attached to all three, then in a declaration like
|
||||
// class C(@A val x: Int) { @A val y = 1 }
|
||||
// the parameter `x` and the property `y` will have the annotation, while the property `x` and both backing fields will not.
|
||||
private val propertyTargets = listOf(AnnotationUseSiteTarget.PROPERTY, AnnotationUseSiteTarget.FIELD)
|
||||
private val constructorPropertyTargets = listOf(AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER) + propertyTargets
|
||||
private val delegatedPropertyTargets = propertyTargets + listOf(AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD)
|
||||
}
|
||||
|
||||
// TODO: third argument should be whether this parameter is a property declaration (though this probably makes no difference)
|
||||
fun generate(irValueParameter: IrValueParameter, firValueParameter: FirValueParameter, isInConstructor: Boolean) {
|
||||
if (isInConstructor) {
|
||||
irValueParameter.annotations += firValueParameter.annotations
|
||||
.filter { it.target(constructorPropertyTargets) == AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER }
|
||||
.toIrAnnotations()
|
||||
} else {
|
||||
irValueParameter.annotations += firValueParameter.annotations.toIrAnnotations()
|
||||
}
|
||||
fun generate(irValueParameter: IrValueParameter, firValueParameter: FirValueParameter) {
|
||||
irValueParameter.annotations += firValueParameter.annotations.toIrAnnotations()
|
||||
}
|
||||
|
||||
fun generate(irProperty: IrProperty, property: FirProperty) {
|
||||
val applicableTargets = when {
|
||||
property.source?.kind == KtFakeSourceElementKind.PropertyFromParameter -> constructorPropertyTargets
|
||||
irProperty.isDelegated -> delegatedPropertyTargets
|
||||
else -> propertyTargets
|
||||
}
|
||||
val applicableTargets = if (irProperty.isDelegated) delegatedPropertyTargets else propertyTargets
|
||||
irProperty.annotations += property.annotations
|
||||
.filter { it.target(applicableTargets) == AnnotationUseSiteTarget.PROPERTY }
|
||||
.toIrAnnotations()
|
||||
@@ -73,11 +60,8 @@ class AnnotationGenerator(private val components: Fir2IrComponents) : Fir2IrComp
|
||||
|
||||
fun generate(irField: IrField, property: FirProperty) {
|
||||
val irProperty = irField.correspondingPropertySymbol?.owner ?: throw AssertionError("$irField is not a property field")
|
||||
val applicableTargets = when {
|
||||
property.source?.kind == KtFakeSourceElementKind.PropertyFromParameter -> constructorPropertyTargets
|
||||
irProperty.isDelegated -> delegatedPropertyTargets
|
||||
else -> propertyTargets
|
||||
}
|
||||
|
||||
val applicableTargets = if (irProperty.isDelegated) delegatedPropertyTargets else propertyTargets
|
||||
irField.annotations += property.annotations.filter {
|
||||
val target = it.target(applicableTargets)
|
||||
target == AnnotationUseSiteTarget.FIELD || target == AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ internal class ClassMemberGenerator(
|
||||
val annotationMode = containingClass?.classKind == ClassKind.ANNOTATION_CLASS && irFunction is IrConstructor
|
||||
for ((valueParameter, firValueParameter) in irParameters.zip(firFunction.valueParameters)) {
|
||||
valueParameter.setDefaultValue(firValueParameter, annotationMode)
|
||||
annotationGenerator.generate(valueParameter, firValueParameter, irFunction is IrConstructor)
|
||||
annotationGenerator.generate(valueParameter, firValueParameter)
|
||||
}
|
||||
annotationGenerator.generate(irFunction, firFunction)
|
||||
}
|
||||
|
||||
+18
@@ -233,6 +233,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
|
||||
runTest("compiler/testData/codegen/box/annotations/javaPropertyWithIntInitializer.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaTargetOnPrimaryCtorParameter.kt")
|
||||
public void testJavaTargetOnPrimaryCtorParameter() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/annotations/javaTargetOnPrimaryCtorParameter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jvmAnnotationFlags.kt")
|
||||
public void testJvmAnnotationFlags() throws Exception {
|
||||
@@ -371,6 +377,18 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
|
||||
runTest("compiler/testData/codegen/box/annotations/syntheticMethodForProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("targetOnPrimaryCtorParameter.kt")
|
||||
public void testTargetOnPrimaryCtorParameter() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/annotations/targetOnPrimaryCtorParameter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("targetOnPrimaryCtorParameterMultiModule.kt")
|
||||
public void testTargetOnPrimaryCtorParameterMultiModule() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/annotations/targetOnPrimaryCtorParameterMultiModule.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typeAnnotationOnJdk6.kt")
|
||||
public void testTypeAnnotationOnJdk6() throws Exception {
|
||||
|
||||
+18
@@ -233,6 +233,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
|
||||
runTest("compiler/testData/codegen/box/annotations/javaPropertyWithIntInitializer.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaTargetOnPrimaryCtorParameter.kt")
|
||||
public void testJavaTargetOnPrimaryCtorParameter() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/annotations/javaTargetOnPrimaryCtorParameter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jvmAnnotationFlags.kt")
|
||||
public void testJvmAnnotationFlags() throws Exception {
|
||||
@@ -371,6 +377,18 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
|
||||
runTest("compiler/testData/codegen/box/annotations/syntheticMethodForProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("targetOnPrimaryCtorParameter.kt")
|
||||
public void testTargetOnPrimaryCtorParameter() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/annotations/targetOnPrimaryCtorParameter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("targetOnPrimaryCtorParameterMultiModule.kt")
|
||||
public void testTargetOnPrimaryCtorParameterMultiModule() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/annotations/targetOnPrimaryCtorParameterMultiModule.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typeAnnotationOnJdk6.kt")
|
||||
public void testTypeAnnotationOnJdk6() throws Exception {
|
||||
|
||||
+21
-9
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.declarations
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.containingClassLookupTag
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
@@ -64,6 +65,7 @@ fun List<FirAnnotation>.nonSourceAnnotations(session: FirSession): List<FirAnnot
|
||||
?.callableNameOfMetaAnnotationArgument == sourceName
|
||||
}
|
||||
}
|
||||
|
||||
fun FirAnnotationContainer.nonSourceAnnotations(session: FirSession): List<FirAnnotation> =
|
||||
annotations.nonSourceAnnotations(session)
|
||||
|
||||
@@ -77,21 +79,31 @@ fun FirAnnotation.useSiteTargetsFromMetaAnnotation(session: FirSession): Set<Ann
|
||||
return toAnnotationClass(session)
|
||||
?.annotations
|
||||
?.find { it.toAnnotationClassId(session) == StandardClassIds.Annotations.Target }
|
||||
?.findArgumentByName(StandardClassIds.Annotations.ParameterNames.targetAllowedTargets)
|
||||
?.unwrapVarargValue()
|
||||
?.toAnnotationUseSiteTargets()
|
||||
?.findUseSiteTargets()
|
||||
?: DEFAULT_USE_SITE_TARGETS
|
||||
}
|
||||
|
||||
private fun List<FirExpression>.toAnnotationUseSiteTargets(): Set<AnnotationUseSiteTarget> =
|
||||
flatMapTo(mutableSetOf()) { arg ->
|
||||
when (val unwrappedArg = if (arg is FirNamedArgumentExpression) arg.expression else arg) {
|
||||
is FirArrayOfCall -> unwrappedArg.argumentList.arguments.toAnnotationUseSiteTargets()
|
||||
is FirVarargArgumentsExpression -> unwrappedArg.arguments.toAnnotationUseSiteTargets()
|
||||
else -> USE_SITE_TARGET_NAME_MAP[unwrappedArg.callableNameOfMetaAnnotationArgument?.identifier] ?: setOf()
|
||||
private fun FirAnnotation.findUseSiteTargets(): Set<AnnotationUseSiteTarget> = buildSet {
|
||||
fun addIfMatching(arg: FirExpression) {
|
||||
if (arg !is FirQualifiedAccessExpression) return
|
||||
val callableSymbol = arg.calleeReference.toResolvedCallableSymbol() ?: return
|
||||
if (callableSymbol.containingClassLookupTag()?.classId == StandardClassIds.AnnotationTarget) {
|
||||
USE_SITE_TARGET_NAME_MAP[callableSymbol.callableId.callableName.identifier]?.let { addAll(it) }
|
||||
}
|
||||
}
|
||||
|
||||
if (this@findUseSiteTargets is FirAnnotationCall) {
|
||||
for (arg in argumentList.arguments) {
|
||||
arg.unwrapAndFlattenArgument().forEach(::addIfMatching)
|
||||
}
|
||||
} else {
|
||||
argumentMapping.mapping[StandardClassIds.Annotations.ParameterNames.targetAllowedTargets]
|
||||
?.unwrapAndFlattenArgument()
|
||||
?.forEach(::addIfMatching)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// See [org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.USE_SITE_MAPPING] (it's in reverse)
|
||||
private val USE_SITE_TARGET_NAME_MAP = mapOf(
|
||||
"FIELD" to setOf(AnnotationUseSiteTarget.FIELD, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD),
|
||||
|
||||
@@ -19,6 +19,7 @@ abstract class FirTypeResolver : FirSessionComponent {
|
||||
scopeClassDeclaration: ScopeClassDeclaration,
|
||||
areBareTypesAllowed: Boolean,
|
||||
isOperandOfIsOperator: Boolean,
|
||||
resolveDeprecations: Boolean,
|
||||
// Note: sometimes we don't have useSiteFile in IDE context
|
||||
useSiteFile: FirFile?,
|
||||
supertypeSupplier: SupertypeSupplier
|
||||
|
||||
+10
-6
@@ -82,7 +82,8 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
||||
typeRef: FirUserTypeRef,
|
||||
scopeClassDeclaration: ScopeClassDeclaration,
|
||||
useSiteFile: FirFile?,
|
||||
supertypeSupplier: SupertypeSupplier
|
||||
supertypeSupplier: SupertypeSupplier,
|
||||
resolveDeprecations: Boolean
|
||||
): TypeResolutionResult {
|
||||
val qualifierResolver = session.qualifierResolver
|
||||
var applicability: CandidateApplicability? = null
|
||||
@@ -101,10 +102,12 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
||||
diagnostic = ConeVisibilityError(symbol)
|
||||
}
|
||||
|
||||
val deprecation = symbol.getDeprecation(session, useSiteFile)
|
||||
if (deprecation != null && deprecation.deprecationLevel == DeprecationLevelValue.HIDDEN) {
|
||||
symbolApplicability = minOf(CandidateApplicability.HIDDEN, symbolApplicability)
|
||||
diagnostic = null
|
||||
if (resolveDeprecations) {
|
||||
val deprecation = symbol.getDeprecation(session, useSiteFile)
|
||||
if (deprecation != null && deprecation.deprecationLevel == DeprecationLevelValue.HIDDEN) {
|
||||
symbolApplicability = minOf(CandidateApplicability.HIDDEN, symbolApplicability)
|
||||
diagnostic = null
|
||||
}
|
||||
}
|
||||
|
||||
if (applicability == null || symbolApplicability > applicability!!) {
|
||||
@@ -491,13 +494,14 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
||||
scopeClassDeclaration: ScopeClassDeclaration,
|
||||
areBareTypesAllowed: Boolean,
|
||||
isOperandOfIsOperator: Boolean,
|
||||
resolveDeprecations: Boolean,
|
||||
useSiteFile: FirFile?,
|
||||
supertypeSupplier: SupertypeSupplier
|
||||
): Pair<ConeKotlinType, ConeDiagnostic?> {
|
||||
return when (typeRef) {
|
||||
is FirResolvedTypeRef -> error("Do not resolve, resolved type-refs")
|
||||
is FirUserTypeRef -> {
|
||||
val result = resolveUserTypeToSymbol(typeRef, scopeClassDeclaration, useSiteFile, supertypeSupplier)
|
||||
val result = resolveUserTypeToSymbol(typeRef, scopeClassDeclaration, useSiteFile, supertypeSupplier, resolveDeprecations)
|
||||
resolveUserType(
|
||||
typeRef,
|
||||
result,
|
||||
|
||||
+3
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
class FirSpecificTypeResolverTransformer(
|
||||
override val session: FirSession,
|
||||
private val errorTypeAsResolved: Boolean = true,
|
||||
private val resolveDeprecations: Boolean = true,
|
||||
private val supertypeSupplier: SupertypeSupplier = SupertypeSupplier.Default
|
||||
) : FirAbstractTreeTransformer<ScopeClassDeclaration>(phase = FirResolvePhase.SUPER_TYPES) {
|
||||
private val typeResolver = session.typeResolver
|
||||
@@ -85,6 +86,7 @@ class FirSpecificTypeResolverTransformer(
|
||||
data,
|
||||
areBareTypesAllowed,
|
||||
isOperandOfIsOperator,
|
||||
resolveDeprecations,
|
||||
currentFile,
|
||||
supertypeSupplier
|
||||
)
|
||||
@@ -104,6 +106,7 @@ class FirSpecificTypeResolverTransformer(
|
||||
data,
|
||||
areBareTypesAllowed,
|
||||
isOperandOfIsOperator,
|
||||
resolveDeprecations,
|
||||
currentFile,
|
||||
supertypeSupplier
|
||||
)
|
||||
|
||||
+44
-2
@@ -7,7 +7,9 @@ package org.jetbrains.kotlin.fir.resolve.transformers
|
||||
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.correspondingProperty
|
||||
import org.jetbrains.kotlin.fir.copyWithNewSourceKind
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isFromVararg
|
||||
@@ -115,8 +117,18 @@ open class FirTypeResolveTransformer(
|
||||
override fun transformConstructor(constructor: FirConstructor, data: Any?): FirConstructor = whileAnalysing(session, constructor) {
|
||||
return withScopeCleanup {
|
||||
constructor.addTypeParametersScope()
|
||||
transformDeclaration(constructor, data)
|
||||
} as FirConstructor
|
||||
val result = transformDeclaration(constructor, data) as FirConstructor
|
||||
|
||||
if (result.isPrimary) {
|
||||
for (valueParameter in result.valueParameters) {
|
||||
if (valueParameter.correspondingProperty != null) {
|
||||
valueParameter.removeDuplicateAnnotationsOfPrimaryConstructorElement()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
override fun transformTypeAlias(typeAlias: FirTypeAlias, data: Any?): FirTypeAlias = whileAnalysing(session, typeAlias) {
|
||||
@@ -170,6 +182,10 @@ open class FirTypeResolveTransformer(
|
||||
|
||||
unboundCyclesInTypeParametersSupertypes(property)
|
||||
|
||||
if (property.source?.kind == KtFakeSourceElementKind.PropertyFromParameter) {
|
||||
property.removeDuplicateAnnotationsOfPrimaryConstructorElement()
|
||||
}
|
||||
|
||||
property
|
||||
}
|
||||
}
|
||||
@@ -380,4 +396,30 @@ open class FirTypeResolveTransformer(
|
||||
scopes.add(FirMemberTypeParameterScope(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In a scenario like
|
||||
*
|
||||
* ```
|
||||
* annotation class Ann
|
||||
* class Foo(@Ann val x: String)
|
||||
* ```
|
||||
*
|
||||
* both, the primary ctor value parameter and the property `x` will be annotated with `@Ann`. This is due to the fact, that the
|
||||
* annotation needs to be resolved in order to determine its annotation targets. We remove annotations from the wrong target if they
|
||||
* don't explicitly specify the use-site target (in which case they shouldn't have been added to the element in the raw FIR).
|
||||
*
|
||||
* For value parameters, we remove the annotation if the targets don't include [AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER].
|
||||
* For properties, we remove the annotation, if the targets include [AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER].
|
||||
*/
|
||||
private fun FirVariable.removeDuplicateAnnotationsOfPrimaryConstructorElement() {
|
||||
val isParameter = this is FirValueParameter
|
||||
replaceAnnotations(annotations.filter {
|
||||
it.useSiteTarget != null ||
|
||||
// equivalent to
|
||||
// CONSTRUCTOR_PARAMETER in targets && isParameter ||
|
||||
// CONSTRUCTOR_PARAMETER !in targets && !isParameter
|
||||
AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER in it.useSiteTargetsFromMetaAnnotation(session) == isParameter
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+129
-18
@@ -7,41 +7,48 @@ package org.jetbrains.kotlin.fir.resolve.transformers.plugin
|
||||
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.PrivateForInline
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationResolvePhase
|
||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildResolvedQualifier
|
||||
import org.jetbrains.kotlin.fir.extensions.*
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.DesignationState
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirSpecificTypeResolverTransformer
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.ScopeClassDeclaration
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.CompilerRequiredAnnotationsHelper.REQUIRED_ANNOTATIONS
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.CompilerRequiredAnnotationsHelper.REQUIRED_ANNOTATIONS_WITH_ARGUMENTS
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.withClassDeclarationCleanup
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.scopes.createImportingScopes
|
||||
import org.jetbrains.kotlin.fir.scopes.getProperties
|
||||
import org.jetbrains.kotlin.fir.scopes.getSingleClassifier
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractImportingScope
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirEnumEntrySymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildPlaceholderProjection
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildStarProjection
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildTypeProjectionWithVariance
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildUserTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.builder.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirQualifierPartImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirTypeArgumentListImpl
|
||||
import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
|
||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.Deprecated
|
||||
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.DeprecatedSinceKotlin
|
||||
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.JvmRecord
|
||||
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.WasExperimental
|
||||
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.Target
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
|
||||
|
||||
/**
|
||||
@@ -50,6 +57,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
|
||||
object CompilerRequiredAnnotationsHelper {
|
||||
internal val REQUIRED_ANNOTATIONS_WITH_ARGUMENTS: Set<ClassId> = setOf(
|
||||
Deprecated,
|
||||
Target,
|
||||
)
|
||||
|
||||
val REQUIRED_ANNOTATIONS: Set<ClassId> = REQUIRED_ANNOTATIONS_WITH_ARGUMENTS + setOf(
|
||||
@@ -69,6 +77,112 @@ internal abstract class AbstractFirSpecificAnnotationResolveTransformer(
|
||||
private val REQUIRED_ANNOTATION_NAMES: Set<Name> = REQUIRED_ANNOTATIONS.mapTo(mutableSetOf()) { it.shortClassName }
|
||||
}
|
||||
|
||||
inner class FirEnumAnnotationArgumentsTransformerDispatcher : FirAbstractBodyResolveTransformerDispatcher(
|
||||
session,
|
||||
FirResolvePhase.COMPILER_REQUIRED_ANNOTATIONS,
|
||||
scopeSession = scopeSession,
|
||||
implicitTypeOnly = false,
|
||||
) {
|
||||
override val expressionsTransformer: FirExpressionsResolveTransformer = FirEnumAnnotationArgumentsTransformer(this)
|
||||
override val declarationsTransformer: FirDeclarationsResolveTransformer get() = throw NotImplementedError()
|
||||
}
|
||||
|
||||
/**
|
||||
* Special transformer that resolves qualified expressions exclusively to enums from import scope. This doesn't
|
||||
* trigger body resolve.
|
||||
*/
|
||||
private inner class FirEnumAnnotationArgumentsTransformer(transformer: FirAbstractBodyResolveTransformerDispatcher) :
|
||||
AbstractFirExpressionsResolveTransformerForAnnotations(transformer) {
|
||||
|
||||
override fun transformFunctionCall(functionCall: FirFunctionCall, data: ResolutionMode): FirStatement {
|
||||
// transform arrayOf arguments to handle `@Foo(bar = arrayOf(X))`
|
||||
functionCall.transformChildren(transformer, data)
|
||||
return super.transformFunctionCall(functionCall, data)
|
||||
}
|
||||
|
||||
override fun resolveQualifiedAccessAndSelectCandidate(
|
||||
qualifiedAccessExpression: FirQualifiedAccessExpression,
|
||||
isUsedAsReceiver: Boolean,
|
||||
callSite: FirElement
|
||||
): FirStatement {
|
||||
qualifiedAccessExpression.resolveFromImportScope()
|
||||
return qualifiedAccessExpression
|
||||
}
|
||||
|
||||
private fun FirQualifiedAccessExpression.resolveFromImportScope() {
|
||||
val calleeReference = calleeReference as? FirSimpleNamedReference ?: return
|
||||
val receiver = explicitReceiver as? FirQualifiedAccessExpression
|
||||
|
||||
if (receiver != null) {
|
||||
// Simple case X.Y or fully qualified case a.b.X.Y
|
||||
// Resolve receiver from import scope.
|
||||
|
||||
val receiverCalleeReference = receiver.calleeReference as? FirSimpleNamedReference ?: return
|
||||
val receiverName = receiverCalleeReference.name.takeIf { !it.isSpecial } ?: return
|
||||
|
||||
val symbol = scopes.filterIsInstance<FirAbstractImportingScope>().firstNotNullOfOrNull {
|
||||
it.getSingleClassifier(receiverName) as? FirClassSymbol<*>
|
||||
} ?: return
|
||||
|
||||
// If fully qualified, check that given package name matches the resolved one.
|
||||
val segments = generateSequence(receiver.explicitReceiver) { (it as? FirQualifiedAccessExpression)?.explicitReceiver }
|
||||
.mapNotNull { (it.calleeReference as? FirSimpleNamedReference)?.name?.identifier }
|
||||
.toList()
|
||||
|
||||
if (segments.isNotEmpty() && FqName.fromSegments(segments.asReversed()) != symbol.classId.packageFqName) {
|
||||
return
|
||||
}
|
||||
|
||||
val resolvedReceiver = buildResolvedQualifier {
|
||||
source = receiver.source
|
||||
packageFqName = symbol.classId.packageFqName
|
||||
relativeClassFqName = symbol.classId.relativeClassName
|
||||
typeRef = FirImplicitUnitTypeRef(receiver.typeRef.source)
|
||||
this.symbol = symbol
|
||||
}
|
||||
|
||||
// Resolve enum entry by name from the declarations of the receiver.
|
||||
val calleeSymbol = symbol.fir.declarations.firstOrNull {
|
||||
it is FirEnumEntry && it.name == calleeReference.name
|
||||
}?.symbol as? FirEnumEntrySymbol ?: return
|
||||
|
||||
updateCallee(calleeReference, calleeSymbol)
|
||||
|
||||
replaceExplicitReceiver(resolvedReceiver)
|
||||
replaceDispatchReceiver(resolvedReceiver)
|
||||
} else {
|
||||
// Case where enum entry is explicitly imported.
|
||||
val calleeSymbol = scopes.firstNotNullOfOrNull {
|
||||
it.getProperties(calleeReference.name).firstOrNull()
|
||||
} as? FirEnumEntrySymbol ?: return
|
||||
|
||||
updateCallee(calleeReference, calleeSymbol)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirQualifiedAccessExpression.updateCallee(
|
||||
calleeReference: FirSimpleNamedReference,
|
||||
calleeSymbol: FirEnumEntrySymbol
|
||||
) {
|
||||
session.lookupTracker?.recordLookup(
|
||||
calleeReference.name,
|
||||
calleeSymbol.dispatchReceiverType?.classId?.asFqNameString() ?: calleeSymbol.callableId.packageName.asString(),
|
||||
this.source,
|
||||
context.file.source,
|
||||
)
|
||||
|
||||
replaceCalleeReference(buildResolvedNamedReference {
|
||||
source = calleeReference.source
|
||||
name = calleeReference.name
|
||||
resolvedSymbol = calleeSymbol
|
||||
})
|
||||
|
||||
calleeSymbol.containingClassLookupTag()
|
||||
?.let { ConeClassLikeTypeImpl(it, emptyArray(), false) }
|
||||
?.let { replaceTypeRef(typeRef.resolvedTypeFromPrototype(it)) }
|
||||
}
|
||||
}
|
||||
|
||||
private val predicateBasedProvider = session.predicateBasedProvider
|
||||
|
||||
private val annotationsFromPlugins: Set<AnnotationFqn> = session.registeredPluginAnnotations.annotations
|
||||
@@ -80,15 +194,12 @@ internal abstract class AbstractFirSpecificAnnotationResolveTransformer(
|
||||
@PrivateForInline
|
||||
val typeResolverTransformer: FirSpecificTypeResolverTransformer = FirSpecificTypeResolverTransformer(
|
||||
session,
|
||||
errorTypeAsResolved = false
|
||||
errorTypeAsResolved = false,
|
||||
resolveDeprecations = false,
|
||||
)
|
||||
|
||||
@PrivateForInline
|
||||
val argumentsTransformer = FirAnnotationArgumentsResolveTransformer(
|
||||
session,
|
||||
scopeSession,
|
||||
FirResolvePhase.COMPILER_REQUIRED_ANNOTATIONS
|
||||
)
|
||||
val argumentsTransformer = FirEnumAnnotationArgumentsTransformerDispatcher()
|
||||
|
||||
private var owners: PersistentList<FirDeclaration> = persistentListOf()
|
||||
private val classDeclarationsStack = ArrayDeque<FirClass>().apply {
|
||||
|
||||
+17
-7
@@ -139,9 +139,8 @@ private class FirDeclarationsResolveTransformerForArgumentAnnotations(
|
||||
}
|
||||
}
|
||||
|
||||
private class FirExpressionsResolveTransformerForSpecificAnnotations(
|
||||
transformer: FirAbstractBodyResolveTransformerDispatcher
|
||||
) : FirExpressionsResolveTransformer(transformer) {
|
||||
abstract class AbstractFirExpressionsResolveTransformerForAnnotations(transformer: FirAbstractBodyResolveTransformerDispatcher) :
|
||||
FirExpressionsResolveTransformer(transformer) {
|
||||
|
||||
override fun transformAnnotation(annotation: FirAnnotation, data: ResolutionMode): FirStatement {
|
||||
dataFlowAnalyzer.enterAnnotation()
|
||||
@@ -166,13 +165,11 @@ private class FirExpressionsResolveTransformerForSpecificAnnotations(
|
||||
return calleeReference !is FirErrorNamedReference
|
||||
}
|
||||
|
||||
override fun resolveQualifiedAccessAndSelectCandidate(
|
||||
abstract override fun resolveQualifiedAccessAndSelectCandidate(
|
||||
qualifiedAccessExpression: FirQualifiedAccessExpression,
|
||||
isUsedAsReceiver: Boolean,
|
||||
callSite: FirElement,
|
||||
): FirStatement {
|
||||
return callResolver.resolveOnlyEnumOrQualifierAccessAndSelectCandidate(qualifiedAccessExpression, isUsedAsReceiver)
|
||||
}
|
||||
): FirStatement
|
||||
|
||||
override fun transformFunctionCall(functionCall: FirFunctionCall, data: ResolutionMode): FirStatement {
|
||||
return functionCall
|
||||
@@ -254,3 +251,16 @@ private class FirExpressionsResolveTransformerForSpecificAnnotations(
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private class FirExpressionsResolveTransformerForSpecificAnnotations(transformer: FirAbstractBodyResolveTransformerDispatcher) :
|
||||
AbstractFirExpressionsResolveTransformerForAnnotations(transformer) {
|
||||
|
||||
override fun resolveQualifiedAccessAndSelectCandidate(
|
||||
qualifiedAccessExpression: FirQualifiedAccessExpression,
|
||||
isUsedAsReceiver: Boolean,
|
||||
callSite: FirElement,
|
||||
): FirStatement {
|
||||
return callResolver.resolveOnlyEnumOrQualifierAccessAndSelectCandidate(qualifiedAccessExpression, isUsedAsReceiver)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -128,6 +128,16 @@ fun <T : FirStatement> FirBlock.replaceFirstStatement(factory: (T) -> FirStateme
|
||||
|
||||
fun FirExpression.unwrapArgument(): FirExpression = (this as? FirWrappedArgumentExpression)?.expression ?: this
|
||||
|
||||
fun FirExpression.unwrapAndFlattenArgument(): List<FirExpression> = buildList { unwrapAndFlattenArgumentTo(this) }
|
||||
|
||||
private fun FirExpression.unwrapAndFlattenArgumentTo(list: MutableList<FirExpression>) {
|
||||
when (val unwrapped = unwrapArgument()) {
|
||||
is FirArrayOfCall, is FirFunctionCall -> (unwrapped as FirCall).arguments.forEach { it.unwrapAndFlattenArgumentTo(list) }
|
||||
is FirVarargArgumentsExpression -> unwrapped.arguments.forEach { it.unwrapAndFlattenArgumentTo(list) }
|
||||
else -> list.add(unwrapped)
|
||||
}
|
||||
}
|
||||
|
||||
val FirVariableAssignment.explicitReceiver: FirExpression? get() = unwrapLValue()?.explicitReceiver
|
||||
|
||||
val FirVariableAssignment.dispatchReceiver: FirExpression get() = unwrapLValue()?.dispatchReceiver ?: FirNoReceiverExpression
|
||||
|
||||
Reference in New Issue
Block a user