From cb847b945db04c9d5b2494f8d885c074cd5d1e73 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 9 Jul 2021 11:27:36 +0300 Subject: [PATCH] FIR: use getAnnotationByClassId instead of getAnnotationByFqName --- .../analysis/checkers/FirAnnotationHelpers.kt | 8 ++++ .../checkers/FirSinceKotlinHelpers.kt | 2 +- .../FirOptInAnnotationClassChecker.kt | 3 +- .../expression/FirClassLiteralChecker.kt | 4 +- .../FirOptInAnnotationCallChecker.kt | 6 +-- .../expression/FirOptInUsageBaseChecker.kt | 44 ++++++++++--------- .../type/FirOptInUsageTypeRefChecker.kt | 8 ++-- .../org/jetbrains/kotlin/name/ClassId.java | 6 +++ .../kotlin/resolve/checkers/OptInNames.kt | 4 ++ 9 files changed, 52 insertions(+), 33 deletions(-) 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 d8c41657e27..93bf9c4a28b 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 @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget +import org.jetbrains.kotlin.fir.FirAnnotationContainer import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirAnnotatedDeclaration import org.jetbrains.kotlin.fir.declarations.FirRegularClass @@ -23,6 +24,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.types.ConeClassLikeType import org.jetbrains.kotlin.fir.types.FirErrorTypeRef import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name private val RETENTION_PARAMETER_NAME = Name.identifier("value") @@ -79,6 +81,12 @@ fun FirAnnotatedDeclaration.getTargetAnnotation(): FirAnnotationCall? { return getAnnotationByFqName(StandardNames.FqNames.target) } +fun FirAnnotationContainer.getAnnotationByClassId(classId: ClassId): FirAnnotationCall? { + return annotations.find { + (it.annotationTypeRef.coneType as? ConeClassLikeType)?.lookupTag?.classId == classId + } +} + fun FirExpression.extractClassesFromArgument(): List { return unfoldArrayOrVararg().mapNotNull { if (it !is FirGetClassCall) return@mapNotNull null 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 62c1b6c9dee..601a749cabf 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 @@ -98,7 +98,7 @@ private fun FirAnnotatedDeclaration.getOwnSinceKotlinVersion(session: FirSession } private fun FirAnnotatedDeclaration.loadWasExperimentalMarkerClasses(): List { - val wasExperimental = getAnnotationByFqName(OptInNames.WAS_EXPERIMENTAL_FQ_NAME) ?: return emptyList() + val wasExperimental = getAnnotationByClassId(OptInNames.WAS_EXPERIMENTAL_CLASS_ID) ?: return emptyList() val annotationClasses = wasExperimental.findArgumentByName(OptInNames.WAS_EXPERIMENTAL_ANNOTATION_CLASS) ?: return emptyList() return annotationClasses.extractClassesFromArgument() } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOptInAnnotationClassChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOptInAnnotationClassChecker.kt index b7fb4b3a4f9..9a8d7d32c2f 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOptInAnnotationClassChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOptInAnnotationClassChecker.kt @@ -13,14 +13,13 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.FirRegularClass -import org.jetbrains.kotlin.fir.declarations.getAnnotationByFqName import org.jetbrains.kotlin.resolve.checkers.Experimentality import org.jetbrains.kotlin.resolve.checkers.OptInNames object FirOptInAnnotationClassChecker : FirRegularClassChecker() { override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) { if (declaration.classKind != ClassKind.ANNOTATION_CLASS) return - if (declaration.getAnnotationByFqName(OptInNames.REQUIRES_OPT_IN_FQ_NAME) == null) return + if (declaration.getAnnotationByClassId(OptInNames.REQUIRES_OPT_IN_CLASS_ID) == null) return if (declaration.getRetention() == AnnotationRetention.SOURCE) { val target = declaration.getRetentionAnnotation() reporter.reportOn(target?.source, FirErrors.EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION, context) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirClassLiteralChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirClassLiteralChecker.kt index b6ee82689ae..ede3083538c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirClassLiteralChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirClassLiteralChecker.kt @@ -31,8 +31,8 @@ object FirClassLiteralChecker : FirGetClassCallChecker() { if (source.kind is FirFakeSourceElementKind) return val argument = expression.argument if (argument is FirResolvedQualifier) { - val fqName = argument.classId?.asSingleFqName() - if (fqName == OptInNames.REQUIRES_OPT_IN_FQ_NAME || fqName == OptInNames.OPT_IN_FQ_NAME) { + val classId = argument.classId + if (classId == OptInNames.REQUIRES_OPT_IN_CLASS_ID || classId == OptInNames.OPT_IN_CLASS_ID) { reporter.reportOn(argument.source, FirErrors.EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION, context) } } 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 c1e3ff7aabb..aa7bc278312 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 @@ -23,9 +23,9 @@ import org.jetbrains.kotlin.resolve.checkers.OptInNames object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() { override fun check(expression: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) { val lookupTag = expression.annotationTypeRef.coneTypeSafe()?.lookupTag ?: return - val fqName = lookupTag.classId.asSingleFqName() - val isMarker = fqName == OptInNames.REQUIRES_OPT_IN_FQ_NAME - val isOptIn = fqName == OptInNames.OPT_IN_FQ_NAME + val classId = lookupTag.classId + val isMarker = classId == OptInNames.REQUIRES_OPT_IN_CLASS_ID + val isOptIn = classId == OptInNames.OPT_IN_CLASS_ID if (isMarker || isOptIn) { checkUsageOfKotlinExperimentalOrUseExperimental(expression.source, context, reporter) if (isOptIn) { 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 955565f6dad..7870ffb70cc 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 @@ -17,7 +17,6 @@ import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isLocal import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.findArgumentByName -import org.jetbrains.kotlin.fir.declarations.getAnnotationByFqName import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.expressions.FirConstExpression import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression @@ -29,17 +28,23 @@ import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.processDirectlyOverriddenFunctions import org.jetbrains.kotlin.fir.scopes.processDirectlyOverriddenProperties import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.ensureResolved import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.checkers.Experimentality import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.addIfNotNull -internal object FirOptInUsageBaseChecker { +object FirOptInUsageBaseChecker { + data class Experimentality(val annotationClassId: ClassId, val severity: Severity, val message: String?) { + enum class Severity { WARNING, ERROR } + companion object { + val DEFAULT_SEVERITY = Severity.ERROR + } + } + fun FirRegularClassSymbol.loadExperimentalityForMarkerAnnotation(): Experimentality? { ensureResolved(FirResolvePhase.BODY_RESOLVE) @OptIn(SymbolInternals::class) @@ -132,7 +137,7 @@ internal object FirOptInUsageBaseChecker { expandedTypeRef.coneType.addExperimentalities(context, result, visited) } - if (getAnnotationByFqName(OptInNames.WAS_EXPERIMENTAL_FQ_NAME) != null) { + if (getAnnotationByClassId(OptInNames.WAS_EXPERIMENTAL_CLASS_ID) != null) { val accessibility = checkSinceKotlinVersionAccessibility(context) if (accessibility is FirSinceKotlinAccessibility.NotAccessibleButWasExperimental) { accessibility.markerClasses.forEach { @@ -178,14 +183,14 @@ internal object FirOptInUsageBaseChecker { } private fun FirRegularClass.loadExperimentalityForMarkerAnnotation(): Experimentality? { - val experimental = getAnnotationByFqName(OptInNames.REQUIRES_OPT_IN_FQ_NAME) + val experimental = getAnnotationByClassId(OptInNames.REQUIRES_OPT_IN_CLASS_ID) ?: return null 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.findArgumentByName(MESSAGE) as? FirConstExpression<*>)?.value as? String - return Experimentality(symbol.classId.asSingleFqName(), level.severity, message) + return Experimentality(symbol.classId, level.severity, message) } fun reportNotAcceptedExperimentalities( @@ -194,8 +199,8 @@ internal object FirOptInUsageBaseChecker { context: CheckerContext, reporter: DiagnosticReporter ) { - for ((annotationFqName, severity, message) in experimentalities) { - if (!isExperimentalityAcceptableInContext(annotationFqName, context)) { + for ((annotationClassId, severity, message) in experimentalities) { + if (!isExperimentalityAcceptableInContext(annotationClassId, context)) { val diagnostic = when (severity) { Experimentality.Severity.WARNING -> FirErrors.EXPERIMENTAL_API_USAGE Experimentality.Severity.ERROR -> FirErrors.EXPERIMENTAL_API_USAGE_ERROR @@ -204,43 +209,40 @@ internal object FirOptInUsageBaseChecker { Experimentality.Severity.WARNING -> "This declaration is experimental and its usage should be marked" Experimentality.Severity.ERROR -> "This declaration is experimental and its usage must be marked" } - reporter.reportOn(element.source, diagnostic, annotationFqName, reportedMessage, context) + reporter.reportOn(element.source, diagnostic, annotationClassId.asSingleFqName(), reportedMessage, context) } } } private fun isExperimentalityAcceptableInContext( - annotationFqName: FqName, + annotationClassId: ClassId, context: CheckerContext ): Boolean { val languageVersionSettings = context.session.languageVersionSettings - val fqNameAsString = annotationFqName.asString() + val fqNameAsString = annotationClassId.asFqNameString() if (fqNameAsString in languageVersionSettings.getFlag(AnalysisFlags.useExperimental)) { return true } for (annotationContainer in context.annotationContainers) { - if (annotationContainer.isExperimentalityAcceptable(annotationFqName)) { + if (annotationContainer.isExperimentalityAcceptable(annotationClassId)) { return true } } return false } - private fun FirAnnotationContainer.isExperimentalityAcceptable(annotationFqName: FqName): Boolean { - return getAnnotationByFqName(annotationFqName) != null || isAnnotatedWithUseExperimentalOf(annotationFqName) + private fun FirAnnotationContainer.isExperimentalityAcceptable(annotationClassId: ClassId): Boolean { + return getAnnotationByClassId(annotationClassId) != null || isAnnotatedWithUseExperimentalOf(annotationClassId) } - private fun FirAnnotationContainer.isAnnotatedWithUseExperimentalOf(annotationFqName: FqName): Boolean { + private fun FirAnnotationContainer.isAnnotatedWithUseExperimentalOf(annotationClassId: ClassId): Boolean { for (annotation in annotations) { val coneType = annotation.annotationTypeRef.coneType as? ConeClassLikeType - if (coneType?.lookupTag?.classId?.asSingleFqName() != OptInNames.OPT_IN_FQ_NAME) { + if (coneType?.lookupTag?.classId != OptInNames.OPT_IN_CLASS_ID) { continue } val annotationClasses = annotation.findArgumentByName(OptInNames.USE_EXPERIMENTAL_ANNOTATION_CLASS) ?: continue - if (annotationClasses.extractClassesFromArgument().any { - it.classId.asSingleFqName() == annotationFqName - } - ) { + if (annotationClasses.extractClassesFromArgument().any { it.classId == annotationClassId }) { return true } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirOptInUsageTypeRefChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirOptInUsageTypeRefChecker.kt index eea32a60a80..ae703b769ef 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirOptInUsageTypeRefChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirOptInUsageTypeRefChecker.kt @@ -8,12 +8,12 @@ package org.jetbrains.kotlin.fir.analysis.checkers.type import org.jetbrains.kotlin.fir.FirRealSourceElementKind import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker +import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationByClassId import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.FirResolvePhase -import org.jetbrains.kotlin.fir.declarations.getAnnotationByFqName import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals @@ -32,12 +32,12 @@ object FirOptInUsageTypeRefChecker : FirTypeRefChecker() { val coneType = typeRef.coneTypeSafe() ?: return val symbol = coneType.lookupTag.toSymbol(context.session) ?: return symbol.ensureResolved(FirResolvePhase.STATUS) - val fqName = symbol.classId.asSingleFqName() + val classId = symbol.classId val lastAnnotationCall = context.qualifiedAccessOrAnnotationCalls.lastOrNull() as? FirAnnotationCall if (lastAnnotationCall == null || lastAnnotationCall.annotationTypeRef !== typeRef) { - if (fqName == OptInNames.REQUIRES_OPT_IN_FQ_NAME || fqName == OptInNames.OPT_IN_FQ_NAME) { + if (classId == OptInNames.REQUIRES_OPT_IN_CLASS_ID || classId == OptInNames.OPT_IN_CLASS_ID) { reporter.reportOn(source, EXPERIMENTAL_CAN_ONLY_BE_USED_AS_ANNOTATION, context) - } else if (symbol is FirRegularClassSymbol && symbol.fir.getAnnotationByFqName(OptInNames.REQUIRES_OPT_IN_FQ_NAME) != null) { + } else if (symbol is FirRegularClassSymbol && symbol.fir.getAnnotationByClassId(OptInNames.REQUIRES_OPT_IN_CLASS_ID) != null) { reporter.reportOn(source, EXPERIMENTAL_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_USE_EXPERIMENTAL, context) } } diff --git a/core/compiler.common/src/org/jetbrains/kotlin/name/ClassId.java b/core/compiler.common/src/org/jetbrains/kotlin/name/ClassId.java index c27da1ad63d..11f0310c4a2 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/name/ClassId.java +++ b/core/compiler.common/src/org/jetbrains/kotlin/name/ClassId.java @@ -127,6 +127,12 @@ public final class ClassId { return packageFqName.asString().replace('.', '/') + "/" + relativeClassName.asString(); } + @NotNull + public String asFqNameString() { + if (packageFqName.isRoot()) return relativeClassName.asString(); + return packageFqName.asString() + "." + relativeClassName.asString(); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/OptInNames.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/OptInNames.kt index 6ba4c707c02..16bf891bf9b 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/OptInNames.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/checkers/OptInNames.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.resolve.checkers +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -12,9 +13,12 @@ object OptInNames { val OLD_EXPERIMENTAL_FQ_NAME = FqName("kotlin.Experimental") val OLD_USE_EXPERIMENTAL_FQ_NAME = FqName("kotlin.UseExperimental") val REQUIRES_OPT_IN_FQ_NAME = FqName("kotlin.RequiresOptIn") + val REQUIRES_OPT_IN_CLASS_ID = ClassId.topLevel(REQUIRES_OPT_IN_FQ_NAME) val OPT_IN_FQ_NAME = FqName("kotlin.OptIn") + val OPT_IN_CLASS_ID = ClassId.topLevel(OPT_IN_FQ_NAME) val WAS_EXPERIMENTAL_FQ_NAME = FqName("kotlin.WasExperimental") + val WAS_EXPERIMENTAL_CLASS_ID = ClassId.topLevel(WAS_EXPERIMENTAL_FQ_NAME) val USE_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass") val WAS_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")