diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt index 65a15946d08..d310ff71921 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt @@ -90,16 +90,22 @@ fun FirClassLikeSymbol<*>.getTargetAnnotation(): FirAnnotation? { return getAnnotationByClassId(StandardClassIds.Annotations.Target) } -fun FirExpression.extractClassesFromArgument(): List { +fun FirExpression.extractClassesFromArgument(session: FirSession): List { return unfoldArrayOrVararg().mapNotNull { - it.extractClassFromArgument() + it.extractClassFromArgument(session) } } -fun FirExpression.extractClassFromArgument(): FirRegularClassSymbol? { +fun FirExpression.extractClassFromArgument(session: FirSession): FirRegularClassSymbol? { if (this !is FirGetClassCall) return null - val qualifier = argument as? FirResolvedQualifier ?: return null - return qualifier.symbol as? FirRegularClassSymbol + return when (val argument = argument) { + is FirResolvedQualifier -> + argument.symbol as? FirRegularClassSymbol + is FirClassReferenceExpression -> + argument.classTypeRef.coneTypeSafe()?.fullyExpandedType(session)?.toRegularClassSymbol(session) + else -> + null + } } fun checkRepeatedAnnotation( diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirSinceKotlinHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirSinceKotlinHelpers.kt index c80ba34f924..d9b9fa7762d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirSinceKotlinHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirSinceKotlinHelpers.kt @@ -69,7 +69,7 @@ private fun FirDeclaration.getOwnSinceKotlinVersion(session: FirSession): FirSin if (apiVersion != null) { // TODO: combine wasExperimentalMarkerClasses in case of several associated declarations with the same maximal API version if (result == null || apiVersion > result!!.apiVersion) { - result = FirSinceKotlinValue(apiVersion, loadWasExperimentalMarkerClasses()) + result = FirSinceKotlinValue(apiVersion, loadWasExperimentalMarkerClasses(session)) } } } @@ -96,9 +96,9 @@ private fun FirDeclaration.getOwnSinceKotlinVersion(session: FirSession): FirSin return result } -private fun FirDeclaration.loadWasExperimentalMarkerClasses(): List { +private fun FirDeclaration.loadWasExperimentalMarkerClasses(session: FirSession): List { val wasExperimental = getAnnotationByClassId(OptInNames.WAS_EXPERIMENTAL_CLASS_ID) ?: return emptyList() val annotationClasses = wasExperimental.findArgumentByName(OptInNames.WAS_EXPERIMENTAL_ANNOTATION_CLASS) ?: return emptyList() - return annotationClasses.extractClassesFromArgument() + return annotationClasses.extractClassesFromArgument(session) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInAnnotationCallChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInAnnotationCallChecker.kt index f835776e181..60b3af0d762 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInAnnotationCallChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInAnnotationCallChecker.kt @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.fir.analysis.checkers.extractClassesFromArgument import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.diagnostics.reportOn -import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker.loadExperimentalityForMarkerAnnotation import org.jetbrains.kotlin.fir.analysis.checkers.extractClassFromArgument import org.jetbrains.kotlin.fir.analysis.checkers.modality import org.jetbrains.kotlin.fir.declarations.FirClass @@ -46,7 +45,7 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() { reporter.reportOn(expression.source, FirErrors.OPT_IN_WITHOUT_ARGUMENTS, context) } else { val annotationClasses = expression.findArgumentByName(OPT_IN_ANNOTATION_CLASS) - for (classSymbol in annotationClasses?.extractClassesFromArgument().orEmpty()) { + for (classSymbol in annotationClasses?.extractClassesFromArgument(context.session).orEmpty()) { checkOptInArgumentIsMarker(classSymbol, expression.source, reporter, context) } } @@ -73,7 +72,7 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() { return } } - val classSymbol = expression.findArgumentByName(OPT_IN_ANNOTATION_CLASS)?.extractClassFromArgument() ?: return + val classSymbol = expression.findArgumentByName(OPT_IN_ANNOTATION_CLASS)?.extractClassFromArgument(context.session) ?: return checkOptInArgumentIsMarker(classSymbol, expression.source, reporter, context) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt index 034645c3a3a..2a66eaf9e80 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt @@ -95,7 +95,8 @@ object FirOptInUsageBaseChecker { if (annotationType.lookupTag.classId == OptInNames.SUBCLASS_OPT_IN_REQUIRED_CLASS_ID) { val annotationClass = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue result.addIfNotNull( - annotationClass.extractClassFromArgument()?.loadExperimentalityForMarkerAnnotation()?.copy(fromSupertype = true) + annotationClass.extractClassFromArgument(session) + ?.loadExperimentalityForMarkerAnnotation()?.copy(fromSupertype = true) ) } } @@ -256,7 +257,7 @@ object FirOptInUsageBaseChecker { reporter: DiagnosticReporter ) { for ((annotationClassId, severity, markerMessage, supertypeName) in experimentalities) { - if (!symbol.fir.isExperimentalityAcceptable(annotationClassId, fromSupertype = false) && + if (!symbol.fir.isExperimentalityAcceptable(context.session, annotationClassId, fromSupertype = false) && !isExperimentalityAcceptableInContext(annotationClassId, context, fromSupertype = false) ) { val (diagnostic, verb) = when (severity) { @@ -285,40 +286,47 @@ object FirOptInUsageBaseChecker { return true } for (annotationContainer in context.annotationContainers) { - if (annotationContainer.isExperimentalityAcceptable(annotationClassId, fromSupertype)) { + if (annotationContainer.isExperimentalityAcceptable(context.session, annotationClassId, fromSupertype)) { return true } } return false } - private fun FirAnnotationContainer.isExperimentalityAcceptable(annotationClassId: ClassId, fromSupertype: Boolean): Boolean { - return getAnnotationByClassId(annotationClassId) != null || isAnnotatedWithOptIn(annotationClassId) || - fromSupertype && isAnnotatedWithSubclassOptInRequired(annotationClassId) + private fun FirAnnotationContainer.isExperimentalityAcceptable( + session: FirSession, + annotationClassId: ClassId, + fromSupertype: Boolean + ): Boolean { + return getAnnotationByClassId(annotationClassId) != null || isAnnotatedWithOptIn(session, annotationClassId) || + fromSupertype && isAnnotatedWithSubclassOptInRequired(session, annotationClassId) } - private fun FirAnnotationContainer.isAnnotatedWithOptIn(annotationClassId: ClassId): Boolean { + private fun FirAnnotationContainer.isAnnotatedWithOptIn(session: FirSession, annotationClassId: ClassId): Boolean { for (annotation in annotations) { val coneType = annotation.annotationTypeRef.coneType as? ConeClassLikeType if (coneType?.lookupTag?.classId != OptInNames.OPT_IN_CLASS_ID) { continue } val annotationClasses = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue - if (annotationClasses.extractClassesFromArgument().any { it.classId == annotationClassId }) { + if (annotationClasses.extractClassesFromArgument(session).any { it.classId == annotationClassId }) { return true } } return false } - private fun FirAnnotationContainer.isAnnotatedWithSubclassOptInRequired(annotationClassId: ClassId): Boolean { + private fun FirAnnotationContainer.isAnnotatedWithSubclassOptInRequired( + session: FirSession, + annotationClassId: ClassId + ): Boolean { for (annotation in annotations) { val coneType = annotation.annotationTypeRef.coneType as? ConeClassLikeType if (coneType?.lookupTag?.classId != OptInNames.SUBCLASS_OPT_IN_REQUIRED_CLASS_ID) { continue } val annotationClass = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue - if (annotationClass.extractClassFromArgument()?.classId == annotationClassId) { + if (annotationClass.extractClassFromArgument(session)?.classId == annotationClassId) { return true } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/duration.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/duration.fir.kt index 88d113980e5..dc58fa85090 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/duration.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/duration.fir.kt @@ -9,6 +9,6 @@ data class Some(val duration: Duration = Duration.INFINITE) fun foo(duration: Duration = Duration.INFINITE) {} fun test() { - Some() - foo() + Some() + foo() }