FIR checkers: properly handle annotations with more than one argument
This commit is contained in:
+12
-7
@@ -32,7 +32,7 @@ fun FirAnnotationCall.getRetention(session: FirSession): AnnotationRetention {
|
||||
|
||||
fun FirRegularClass.getRetention(): AnnotationRetention {
|
||||
val retentionAnnotation = getRetentionAnnotation() ?: return AnnotationRetention.RUNTIME
|
||||
val retentionArgument = retentionAnnotation.findSingleArgumentByName(RETENTION_PARAMETER_NAME) as? FirQualifiedAccessExpression
|
||||
val retentionArgument = retentionAnnotation.findArgumentByName(RETENTION_PARAMETER_NAME) as? FirQualifiedAccessExpression
|
||||
?: return AnnotationRetention.RUNTIME
|
||||
val retentionName = (retentionArgument.calleeReference as? FirResolvedNamedReference)?.name?.asString()
|
||||
?: return AnnotationRetention.RUNTIME
|
||||
@@ -51,7 +51,7 @@ fun FirAnnotationCall.getAllowedAnnotationTargets(session: FirSession): Set<Kotl
|
||||
fun FirRegularClass.getAllowedAnnotationTargets(): Set<KotlinTarget> {
|
||||
val targetAnnotation = getTargetAnnotation() ?: return defaultAnnotationTargets
|
||||
if (targetAnnotation.argumentList.arguments.isEmpty()) return emptySet()
|
||||
val arguments = targetAnnotation.findSingleArgumentByName(TARGET_PARAMETER_NAME)?.unfoldArrayOrVararg().orEmpty()
|
||||
val arguments = targetAnnotation.findArgumentByName(TARGET_PARAMETER_NAME)?.unfoldArrayOrVararg().orEmpty()
|
||||
|
||||
return arguments.mapNotNullTo(mutableSetOf()) { argument ->
|
||||
val targetExpression = argument as? FirQualifiedAccessExpression
|
||||
@@ -74,15 +74,20 @@ fun FirAnnotationContainer.getAnnotationByFqName(fqName: FqName): FirAnnotationC
|
||||
}
|
||||
}
|
||||
|
||||
fun FirAnnotationCall.findSingleArgumentByName(name: Name): FirExpression? {
|
||||
fun FirAnnotationCall.findArgumentByName(name: Name): FirExpression? {
|
||||
val argumentMapping = argumentMapping
|
||||
if (argumentMapping != null) {
|
||||
return argumentMapping.keys.firstOrNull()?.takeIf { argumentMapping[it]?.name == name }?.unwrapArgument()
|
||||
return argumentMapping.keys.find { argumentMapping[it]?.name == name }?.unwrapArgument()
|
||||
}
|
||||
// NB: we have to consider both cases, because deserializer does not create argument mapping
|
||||
val arguments = argumentList.arguments
|
||||
val firstArgument = arguments.firstOrNull() as? FirNamedArgumentExpression ?: return arguments.singleOrNull()
|
||||
return firstArgument.takeIf { it.name == name }?.expression
|
||||
for (argument in arguments) {
|
||||
if (argument is FirNamedArgumentExpression && argument.name == name) {
|
||||
return argument.expression
|
||||
}
|
||||
}
|
||||
// I'm lucky today!
|
||||
// TODO: this line is still needed. However it should be replaced with 'return null'
|
||||
return arguments.singleOrNull()
|
||||
}
|
||||
|
||||
fun FirExpression.extractClassesFromArgument(): List<FirRegularClassSymbol> {
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ private fun FirAnnotatedDeclaration.getOwnSinceKotlinVersion(session: FirSession
|
||||
|
||||
private fun FirAnnotatedDeclaration.loadWasExperimentalMarkerClasses(): List<FirRegularClassSymbol> {
|
||||
val wasExperimental = getAnnotationByFqName(OptInNames.WAS_EXPERIMENTAL_FQ_NAME) ?: return emptyList()
|
||||
val annotationClasses = wasExperimental.findSingleArgumentByName(OptInNames.WAS_EXPERIMENTAL_ANNOTATION_CLASS) ?: return emptyList()
|
||||
val annotationClasses = wasExperimental.findArgumentByName(OptInNames.WAS_EXPERIMENTAL_ANNOTATION_CLASS) ?: return emptyList()
|
||||
return annotationClasses.extractClassesFromArgument()
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.ConstantArgumentKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.checkConstantArguments
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.findSingleArgumentByName
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.findArgumentByName
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticFactory0
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
@@ -136,7 +136,7 @@ object FirAnnotationArgumentChecker : FirAnnotationCallChecker() {
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
if (!annotationFqNamesWithVersion.contains(fqName)) return
|
||||
val versionExpression = annotationCall.findSingleArgumentByName(versionArgumentName) ?: return
|
||||
val versionExpression = annotationCall.findArgumentByName(versionArgumentName) ?: return
|
||||
val version = parseVersionExpressionOrReport(versionExpression, context, reporter) ?: return
|
||||
if (fqName == sinceKotlinFqName) {
|
||||
val specified = context.session.languageVersionSettings.apiVersion
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.config.AnalysisFlags
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.extractClassesFromArgument
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.findSingleArgumentByName
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.findArgumentByName
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
@@ -32,7 +32,7 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
|
||||
if (arguments.isEmpty()) {
|
||||
reporter.reportOn(expression.source, FirErrors.USE_EXPERIMENTAL_WITHOUT_ARGUMENTS, context)
|
||||
} else {
|
||||
val annotationClasses = expression.findSingleArgumentByName(OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS)
|
||||
val annotationClasses = expression.findArgumentByName(OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS)
|
||||
for (classSymbol in annotationClasses?.extractClassesFromArgument().orEmpty()) {
|
||||
with(FirOptInUsageBaseChecker) {
|
||||
if (classSymbol.fir.loadExperimentalityForMarkerAnnotation() == null) {
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.findSingleArgumentByName
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.findArgumentByName
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationByFqName
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
||||
@@ -19,10 +19,10 @@ object FirOptInUsageBaseChecker {
|
||||
internal fun FirRegularClass.loadExperimentalityForMarkerAnnotation(): Experimentality? {
|
||||
val experimental = getAnnotationByFqName(OptInNames.REQUIRES_OPT_IN_FQ_NAME) ?: return null
|
||||
|
||||
val levelArgument = experimental.findSingleArgumentByName(LEVEL) as? FirQualifiedAccessExpression
|
||||
val levelArgument = experimental.findArgumentByName(LEVEL) as? FirQualifiedAccessExpression
|
||||
val levelName = (levelArgument?.calleeReference as? FirResolvedNamedReference)?.name?.asString()
|
||||
val level = OptInLevel.values().firstOrNull { it.name == levelName } ?: OptInLevel.DEFAULT
|
||||
val message = (experimental.findSingleArgumentByName(MESSAGE) as? FirConstExpression<*>)?.value as? String
|
||||
val message = (experimental.findArgumentByName(MESSAGE) as? FirConstExpression<*>)?.value as? String
|
||||
return Experimentality(symbol.classId.asSingleFqName(), level.severity, message)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user