From eded51a0f5188d079893623fd65703cd7d08fdfa Mon Sep 17 00:00:00 2001 From: Brian Norman Date: Mon, 15 Jan 2024 10:36:48 -0600 Subject: [PATCH] [Parcelize] Detect redundant TypeParceler when type aliases are used Make sure redundancies between class and property TypeParceler annotations are detected when a type alias is used. This requires checking the expanded type arguments of the annotation, as just comparing the cone types is not sufficient because the type arguments are not expanded. ^KT-64979 Fixed --- .../fir/declarations/FirAnnotationUtils.kt | 8 ------- .../FirParcelizeAnnotationChecker.kt | 21 ++++++++++++++----- .../testData/box/customSerializerSimple.kt | 6 ++---- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt index 3f88ab0073b..3910b10cb10 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt @@ -122,10 +122,6 @@ private val USE_SITE_TARGET_NAME_MAP = mapOf( private val DEFAULT_USE_SITE_TARGETS: Set = USE_SITE_TARGET_NAME_MAP.values.fold(setOf()) { a, b -> a + b } - setOf(AnnotationUseSiteTarget.FILE) -fun FirDeclaration.hasAnnotation(type: ConeClassLikeType, session: FirSession): Boolean { - return annotations.hasAnnotation(type, session) -} - fun FirDeclaration.hasAnnotation(classId: ClassId, session: FirSession): Boolean { return annotations.hasAnnotation(classId, session) } @@ -142,10 +138,6 @@ fun FirAnnotationContainer.hasAnnotation(classId: ClassId, session: FirSession): return annotations.hasAnnotation(classId, session) } -fun List.hasAnnotation(type: ConeClassLikeType, session: FirSession): Boolean { - return this.any { it.toAnnotationClassLikeType(session) == type } -} - fun List.hasAnnotation(classId: ClassId, session: FirSession): Boolean { return this.any { it.toAnnotationClassId(session) == classId } } diff --git a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt index 6b95dcb515f..98915cc6dd5 100644 --- a/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt +++ b/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/diagnostics/FirParcelizeAnnotationChecker.kt @@ -11,10 +11,7 @@ import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirAnnotationCallChecker import org.jetbrains.kotlin.fir.analysis.checkers.findClosestClassOrObject -import org.jetbrains.kotlin.fir.declarations.FirProperty -import org.jetbrains.kotlin.fir.declarations.hasAnnotation -import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId -import org.jetbrains.kotlin.fir.declarations.toAnnotationClassLikeType +import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.fromPrimaryConstructor import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.resolve.fullyExpandedType @@ -94,13 +91,27 @@ object FirParcelizeAnnotationChecker : FirAnnotationCallChecker() { val enclosingClass = context.findClosestClassOrObject() ?: return val annotationType = annotationCall.toAnnotationClassLikeType(context.session) ?: return - if (enclosingClass.hasAnnotation(annotationType, context.session)) { + if (checkForRedundantTypeParceler(enclosingClass, annotationType, context)) { val reportElement = annotationCall.calleeReference.source ?: annotationCall.source reporter.reportOn(reportElement, KtErrorsParcelize.REDUNDANT_TYPE_PARCELER, enclosingClass.symbol, context) } } } + private fun checkForRedundantTypeParceler( + enclosingClass: FirClass, + annotationType: ConeClassLikeType, + context: CheckerContext, + ): Boolean { + return enclosingClass.annotations + .mapNotNull { it.toAnnotationClassLikeType(context.session) } + .filter { it.classId == annotationType.classId && it.typeArguments.size == annotationType.typeArguments.size } + .any { + it.typeArguments.zip(annotationType.typeArguments) + .all { (first, second) -> first.type?.fullyExpandedType(context.session) == second.type?.fullyExpandedType(context.session) } + } + } + private fun checkWriteWithUsage(annotationCall: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) { checkIfTheContainingClassIsParcelize(annotationCall, context, reporter) diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt index b96bcc143a3..83a725fab07 100644 --- a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt +++ b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt @@ -1,6 +1,4 @@ // WITH_STDLIB -// fir doesn't support annotations with type arguments -// IGNORE_BACKEND_K2: JVM_IR @file:JvmName("TestKt") package test @@ -32,8 +30,8 @@ object Parceler3 : Parceler { data class Test( val a: String, @TypeParceler val b: String, -@TypeParceler val c: CharSequence, -val d: @WriteWith String + @TypeParceler val c: CharSequence, + val d: @WriteWith String, ) : Parcelable fun box() = parcelTest { parcel ->