[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)
}
}
}
@@ -0,0 +1,29 @@
// TARGET_BACKEND: JVM
// WITH_STDLIB
// MODULE: lib
// FILE: A.kt
package kotlin.test
@JvmName("contentEqualsNullable")
public inline infix fun <T> Array<out T>?.contentEqualsMy(other: Array<out T>?): Boolean {
return java.util.Arrays.equals(this, other)
}
@Deprecated("Use Kotlin compiler 1.4 to avoid deprecation warning.")
@DeprecatedSinceKotlin(hiddenSince = "1.4")
public inline infix fun <T> Array<out T>.contentEqualsMy(other: Array<out T>): Boolean {
return this.contentEqualsMy(other)
}
// MODULE: main(lib)
// FILE: B.kt
import kotlin.test.*
fun box(): String {
val arr = arrayOf(1, 2, 3)
return if (arr contentEqualsMy arr) "OK" else "fail"
}
@@ -1,26 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION
package kotlin
object Scope {
fun foo(): Int = 0
object Nested {
@Deprecated("err")
@DeprecatedSinceKotlin(hiddenSince = "1.0")
fun foo(): String = ""
fun <T> take(f: () -> T): T = f()
fun test() {
val r1 = take(::foo)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>r1<!>
val r2 = ::foo
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.reflect.KFunction0<kotlin.String>")!>r2<!>
val r3 = foo()
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>r3<!>
}
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION
package kotlin
@@ -28,7 +28,7 @@ val valNext = Unit
fun usage() {
ClassCur()
funCur()
<!INVISIBLE_REFERENCE!>funCur<!>()
valCur
ClassNext()
@@ -13893,6 +13893,22 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@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, 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")
@@ -13893,6 +13893,22 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
}
}
@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")
@@ -11310,6 +11310,24 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
}
}
@TestMetadata("compiler/testData/codegen/box/deprecated")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Deprecated extends AbstractLightAnalysisModeTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInDeprecated() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/deprecated"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("deprecatedSinceKotlin.kt")
public void testDeprecatedSinceKotlin() throws Exception {
runTest("compiler/testData/codegen/box/deprecated/deprecatedSinceKotlin.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -10044,6 +10044,19 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
}
}
@TestMetadata("compiler/testData/codegen/box/deprecated")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Deprecated extends AbstractIrJsCodegenBoxES6Test {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath);
}
public void testAllFilesPresentInDeprecated() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/deprecated"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
}
}
@TestMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -9450,6 +9450,19 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/deprecated")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Deprecated extends AbstractIrJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
}
public void testAllFilesPresentInDeprecated() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/deprecated"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
}
@TestMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -9450,6 +9450,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/deprecated")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Deprecated extends AbstractJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
}
public void testAllFilesPresentInDeprecated() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/deprecated"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
}
@TestMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -4458,6 +4458,19 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
}
}
@TestMetadata("compiler/testData/codegen/box/deprecated")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Deprecated extends AbstractIrCodegenBoxWasmTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath);
}
public void testAllFilesPresentInDeprecated() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/deprecated"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
}
@TestMetadata("compiler/testData/codegen/box/destructuringDeclInLambdaParam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)