[FIR] Use @DeprecatedSinceKotlin in the resolution

This commit is contained in:
Andrey Zinovyev
2021-06-17 12:26:31 +03:00
committed by teamcityserver
parent b64f7909b8
commit 9fad55d551
22 changed files with 241 additions and 62 deletions
@@ -7,10 +7,11 @@ 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
import org.jetbrains.kotlin.fir.declarations.findArgumentByName
import org.jetbrains.kotlin.fir.declarations.getAnnotationByFqName
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
@@ -19,7 +20,6 @@ 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.FqName
import org.jetbrains.kotlin.name.Name
private val RETENTION_PARAMETER_NAME = Name.identifier("value")
@@ -68,28 +68,6 @@ fun FirAnnotatedDeclaration.getTargetAnnotation(): FirAnnotationCall? {
return getAnnotationByFqName(StandardNames.FqNames.target)
}
fun FirAnnotationContainer.getAnnotationByFqName(fqName: FqName): FirAnnotationCall? {
return annotations.find {
(it.annotationTypeRef.coneType as? ConeClassLikeType)?.lookupTag?.classId?.asSingleFqName() == fqName
}
}
fun FirAnnotationCall.findArgumentByName(name: Name): FirExpression? {
val argumentMapping = argumentMapping
if (argumentMapping != null) {
return argumentMapping.keys.find { argumentMapping[it]?.name == name }?.unwrapArgument()
}
// NB: we have to consider both cases, because deserializer does not create argument mapping
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> {
return unfoldArrayOrVararg().mapNotNull {
if (it !is FirGetClassCall) return@mapNotNull null
@@ -8,9 +8,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.declarations.FirAnnotatedDeclaration
import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.expressions.arguments
import org.jetbrains.kotlin.fir.languageVersionSettings
@@ -13,6 +13,7 @@ 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
@@ -11,12 +11,12 @@ 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.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
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.declarations.findArgumentByName
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.resolve.fqName
@@ -9,10 +9,10 @@ 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.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
import org.jetbrains.kotlin.fir.declarations.findArgumentByName
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
@@ -5,9 +5,9 @@
package org.jetbrains.kotlin.fir.analysis.checkers.expression
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.declarations.findArgumentByName
import org.jetbrains.kotlin.fir.declarations.getAnnotationByFqName
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
@@ -13893,6 +13893,22 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/deprecated")
@TestDataPath("$PROJECT_ROOT")
public class Deprecated {
@Test
public void testAllFilesPresentInDeprecated() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/deprecated"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("deprecatedSinceKotlin.kt")
public void testDeprecatedSinceKotlin() throws Exception {
runTest("compiler/testData/codegen/box/deprecated/deprecatedSinceKotlin.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam")
@TestDataPath("$PROJECT_ROOT")
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
private val RETENTION_CLASS_ID = ClassId.fromString("kotlin/annotation/Retention")
@@ -88,3 +89,25 @@ private val DEFAULT_USE_SITE_TARGETS: Set<AnnotationUseSiteTarget> =
fun FirAnnotatedDeclaration.hasAnnotation(classId: ClassId): Boolean {
return annotations.any { it.toAnnotationClassId() == classId }
}
fun FirAnnotationContainer.getAnnotationByFqName(fqName: FqName): FirAnnotationCall? {
return annotations.find {
(it.annotationTypeRef.coneType as? ConeClassLikeType)?.lookupTag?.classId?.asSingleFqName() == fqName
}
}
fun FirAnnotationCall.findArgumentByName(name: Name): FirExpression? {
val argumentMapping = argumentMapping
if (argumentMapping != null) {
return argumentMapping.keys.find { argumentMapping[it]?.name == name }?.unwrapArgument()
}
// NB: we have to consider both cases, because deserializer does not create argument mapping
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()
}
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.declarations
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private val HIDDEN_SINCE_NAME = Name.identifier("hiddenSince")
private object FirDeprecatedSinceVersionKey : FirDeclarationDataKey()
private var FirCallableDeclaration<*>.deprecatedSinceKotlinCachedValue: DeprecatedSinceValue? by FirDeclarationDataRegistry.data(
FirDeprecatedSinceVersionKey
)
private fun FirAnnotationContainer.getHiddenSinceKotlin(): ApiVersion? =
getAnnotationByFqName(StandardNames.FqNames.deprecatedSinceKotlin)?.getVersionFromArgument(HIDDEN_SINCE_NAME)
fun <T> T.getHiddenSinceKotlinCached(): ApiVersion? where T : FirCallableDeclaration<*>, T : FirAnnotationContainer {
val cached = deprecatedSinceKotlinCachedValue
if (cached != null) return cached.hiddenSince
val calculated = getHiddenSinceKotlin()
deprecatedSinceKotlinCachedValue = DeprecatedSinceValue(calculated)
return calculated
}
private fun FirAnnotationCall.getVersionFromArgument(name: Name): ApiVersion? =
findArgumentByName(name)?.let { expression ->
expression.safeAs<FirConstExpression<*>>()?.value.safeAs<String>()?.let { ApiVersion.parse(it) }
}
private class DeprecatedSinceValue(val hiddenSince: ApiVersion?)
@@ -42,7 +42,8 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
CheckCallModifiers,
EagerResolveOfCallableReferences,
CheckLowPriorityInOverloadResolution,
PostponedVariablesInitializerResolutionStage
PostponedVariablesInitializerResolutionStage,
CheckDeprecatedSinceKotlin
)
object DelegatingConstructorCall : CallKind(
@@ -67,7 +68,8 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
CheckDispatchReceiver,
CheckExtensionReceiver,
CheckCallableReferenceExpectedType,
CheckLowPriorityInOverloadResolution
CheckLowPriorityInOverloadResolution,
CheckDeprecatedSinceKotlin
)
object SyntheticIdForCallableReferencesResolution : CallKind(
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.FirVisibilityChecker
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isInfix
@@ -21,9 +22,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visibilityChecker
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -295,3 +294,13 @@ internal object CheckCallModifiers : CheckerStage() {
}
}
}
internal object CheckDeprecatedSinceKotlin : ResolutionStage() {
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
val fir = (candidate.symbol as? FirFunctionSymbol<*>)?.fir ?: return
val deprecatedSince = fir.getHiddenSinceKotlinCached() ?: return
if (context.session.languageVersionSettings.apiVersion >= deprecatedSince) {
sink.yieldDiagnostic(HiddenCandidate)
}
}
}