diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDeprecationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDeprecationChecker.kt index a351106fbbe..90c62240c1b 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDeprecationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDeprecationChecker.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression import org.jetbrains.kotlin.KtFakeSourceElementKind -import org.jetbrains.kotlin.KtRealSourceElementKind import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.config.KotlinCompilerVersion import org.jetbrains.kotlin.diagnostics.DiagnosticReporter @@ -29,6 +28,7 @@ import org.jetbrains.kotlin.fir.references.resolved import org.jetbrains.kotlin.fir.resolve.firClassLike import org.jetbrains.kotlin.fir.resolve.typeAliasForConstructor import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol @@ -51,6 +51,9 @@ object FirDeprecationChecker : FirBasicExpressionChecker() { val calleeReference = expression.calleeReference ?: return val resolvedReference = calleeReference.resolved ?: return val referencedSymbol = resolvedReference.resolvedSymbol + + if (expression.isDelegatedPropertySelfAccess(context, referencedSymbol)) return + val source = resolvedReference.source ?: expression.source if (expression is FirDelegatedConstructorCall) { @@ -67,6 +70,19 @@ object FirDeprecationChecker : FirBasicExpressionChecker() { } } + /** Checks if this is an access to a delegated property inside the delegated property itself. + * Deprecations shouldn't be reported here. */ + @OptIn(SymbolInternals::class) + private fun FirStatement.isDelegatedPropertySelfAccess(context: CheckerContext, referencedSymbol: FirBasedSymbol<*>): Boolean { + if (source?.kind != KtFakeSourceElementKind.DelegatedPropertyAccessor) return false + val containers = context.containingDeclarations + val size = containers.size + val fir = referencedSymbol.fir + + return containers.getOrNull(size - 1) == fir // For `provideDelegate`, the call will be in the initializer + || containers.getOrNull(size - 2) == fir // For `getValue`, the call will be in the accessor + } + internal fun reportApiStatusIfNeeded( source: KtSourceElement?, referencedSymbol: FirBasedSymbol<*>, diff --git a/compiler/testData/diagnostics/tests/deprecated/propertyUsage.fir.kt b/compiler/testData/diagnostics/tests/deprecated/propertyUsage.fir.kt index c90d5b097a2..92cd7eeb35a 100644 --- a/compiler/testData/diagnostics/tests/deprecated/propertyUsage.fir.kt +++ b/compiler/testData/diagnostics/tests/deprecated/propertyUsage.fir.kt @@ -13,6 +13,15 @@ class Delegate() { operator fun setValue(instance: Any, property: KProperty<*>, value: Int) {} } +class Delegate2() { + operator fun getValue(instance: Any, property: KProperty<*>) : Int = 1 + operator fun setValue(instance: Any, property: KProperty<*>, value: Int) {} +} + +class DelegateProvider() { + operator fun provideDelegate(instance: Any, property: KProperty<*>) = Delegate2() +} + class PropertyHolder { @Deprecated("text") val x = 1 @@ -23,6 +32,14 @@ class PropertyHolder { val valDelegate by Delegate() var varDelegate by Delegate() + // no deprecation caused by access to itself + @Deprecated("text") + var deprecatedDelegated by Delegate2() + + // no deprecation caused by access to itself + @Deprecated("text") + val deprecatedDelegated2 by DelegateProvider() + public val test1: String = "" @Deprecated("val-getter") get diff --git a/compiler/testData/diagnostics/tests/deprecated/propertyUsage.kt b/compiler/testData/diagnostics/tests/deprecated/propertyUsage.kt index 60cccb93031..7128f1df929 100644 --- a/compiler/testData/diagnostics/tests/deprecated/propertyUsage.kt +++ b/compiler/testData/diagnostics/tests/deprecated/propertyUsage.kt @@ -13,6 +13,15 @@ class Delegate() { operator fun setValue(instance: Any, property: KProperty<*>, value: Int) {} } +class Delegate2() { + operator fun getValue(instance: Any, property: KProperty<*>) : Int = 1 + operator fun setValue(instance: Any, property: KProperty<*>, value: Int) {} +} + +class DelegateProvider() { + operator fun provideDelegate(instance: Any, property: KProperty<*>) = Delegate2() +} + class PropertyHolder { @Deprecated("text") val x = 1 @@ -23,6 +32,14 @@ class PropertyHolder { val valDelegate by Delegate() var varDelegate by Delegate() + // no deprecation caused by access to itself + @Deprecated("text") + var deprecatedDelegated by Delegate2() + + // no deprecation caused by access to itself + @Deprecated("text") + val deprecatedDelegated2 by DelegateProvider() + public val test1: String = "" @Deprecated("val-getter") get diff --git a/compiler/testData/diagnostics/tests/deprecated/propertyUsage.txt b/compiler/testData/diagnostics/tests/deprecated/propertyUsage.txt index fd390ffe26d..62933029dfe 100644 --- a/compiler/testData/diagnostics/tests/deprecated/propertyUsage.txt +++ b/compiler/testData/diagnostics/tests/deprecated/propertyUsage.txt @@ -13,8 +13,27 @@ public final class Delegate { public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } +public final class Delegate2 { + public constructor Delegate2() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ instance: kotlin.Any, /*1*/ property: kotlin.reflect.KProperty<*>): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun setValue(/*0*/ instance: kotlin.Any, /*1*/ property: kotlin.reflect.KProperty<*>, /*2*/ value: kotlin.Int): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class DelegateProvider { + public constructor DelegateProvider() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun provideDelegate(/*0*/ instance: kotlin.Any, /*1*/ property: kotlin.reflect.KProperty<*>): Delegate2 + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + public final class PropertyHolder { public constructor PropertyHolder() + @kotlin.Deprecated(message = "text") public final var deprecatedDelegated: kotlin.Int + @kotlin.Deprecated(message = "text") public final val deprecatedDelegated2: kotlin.Int @kotlin.Deprecated(message = "text") public final var name: kotlin.String @get:kotlin.Deprecated(message = "val-getter") public final val test1: kotlin.String = "" @get:kotlin.Deprecated(message = "var-getter") @set:kotlin.Deprecated(message = "var-setter") public final var test2: kotlin.String