diff --git a/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmBackendClassResolver.kt b/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmBackendClassResolver.kt index 6a2dcc9d4a6..5a8eaf95cca 100644 --- a/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmBackendClassResolver.kt +++ b/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/FirJvmBackendClassResolver.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.org.objectweb.asm.Type class FirJvmBackendClassResolver(val components: Fir2IrComponents) : JvmBackendClassResolver { - @ObsoleteDescriptorBasedAPI + @OptIn(ObsoleteDescriptorBasedAPI::class) override fun resolveToClassDescriptors(type: Type): List { if (type.sort != Type.OBJECT) return emptyList() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index ab1f73af2d9..2fa4497c419 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -287,6 +287,7 @@ public interface Errors { DiagnosticFactory0 EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 EXPERIMENTAL_ANNOTATION_ON_GETTER = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 EXPERIMENTAL_ANNOTATION_ON_OVERRIDE = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 EXPERIMENTAL_UNSIGNED_LITERALS = DiagnosticFactory1.create(WARNING); DiagnosticFactory1 EXPERIMENTAL_UNSIGNED_LITERALS_ERROR = DiagnosticFactory1.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 660d39d8108..221f180da38 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -171,6 +171,7 @@ public class DefaultErrorMessages { MAP.put(EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION, "Opt-in requirement marker annotation cannot be used with SOURCE retention. Please replace retention with BINARY"); MAP.put(EXPERIMENTAL_ANNOTATION_ON_GETTER, "Opt-in requirement marker annotation cannot be used on getter. Please annotate property instead"); + MAP.put(EXPERIMENTAL_ANNOTATION_ON_OVERRIDE, "Opt-in requirement marker annotation on override requires the same marker on base declaration"); MAP.put(EXPERIMENTAL_UNSIGNED_LITERALS, "{0}", STRING); MAP.put(EXPERIMENTAL_UNSIGNED_LITERALS_ERROR, "{0}", STRING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt index f3eab1ed637..24791060e81 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt @@ -6,13 +6,20 @@ package org.jetbrains.kotlin.resolve.checkers import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtAnnotated import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtCallableDeclaration +import org.jetbrains.kotlin.psi.KtPropertyAccessor +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.AdditionalAnnotationChecker import org.jetbrains.kotlin.resolve.AnnotationChecker import org.jetbrains.kotlin.resolve.BindingContext @@ -50,12 +57,27 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD isAnnotatedWithExperimental = true } } - if (annotation.annotationClass?.annotations?.any { it.fqName in ExperimentalUsageChecker.EXPERIMENTAL_FQ_NAMES } == true) { + val annotationClass = annotation.annotationClass ?: continue + if (annotationClass.annotations.any { it.fqName in ExperimentalUsageChecker.EXPERIMENTAL_FQ_NAMES }) { if (KotlinTarget.PROPERTY_GETTER in actualTargets || entry.useSiteTarget?.getAnnotationUseSiteTarget() == AnnotationUseSiteTarget.PROPERTY_GETTER ) { trace.report(Errors.EXPERIMENTAL_ANNOTATION_ON_GETTER.on(entry)) } + val annotated = entry.getStrictParentOfType() ?: continue + val useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget() + if (annotated is KtCallableDeclaration && + annotated !is KtPropertyAccessor && + useSiteTarget == null && + annotated.hasModifier(KtTokens.OVERRIDE_KEYWORD) + ) { + val descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, annotated) + if (descriptor is CallableMemberDescriptor && + !descriptor.hasExperimentalOverriddenDescriptors(annotationClass.fqNameSafe) + ) { + trace.report(Errors.EXPERIMENTAL_ANNOTATION_ON_OVERRIDE.on(entry)) + } + } } } @@ -64,6 +86,25 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD } } + private fun CallableMemberDescriptor.hasExperimentalOverriddenDescriptors( + experimentalFqName: FqName, + visited: MutableSet = mutableSetOf() + ): Boolean { + if (this in visited) return false + visited += this + for (overridden in overriddenDescriptors) { + if (overridden.annotations.any { it.fqName == experimentalFqName }) { + return true + } + if (overridden.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE && + overridden.hasExperimentalOverriddenDescriptors(experimentalFqName) + ) { + return true + } + } + return false + } + private fun checkUseExperimentalUsage(annotationClasses: List>, trace: BindingTrace, entry: KtAnnotationEntry) { if (annotationClasses.isEmpty()) { trace.report(Errors.USE_EXPERIMENTAL_WITHOUT_ARGUMENTS.on(entry)) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt index c7ce6e0d3ab..e287c6d3507 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrSourceCompilerForInline.kt @@ -128,7 +128,7 @@ class IrSourceCompilerForInline( it.finallyDepth = curFinallyDepth } - @ObsoleteDescriptorBasedAPI + @OptIn(ObsoleteDescriptorBasedAPI::class) override fun isCallInsideSameModuleAsDeclared(functionDescriptor: FunctionDescriptor): Boolean { require(functionDescriptor is IrBasedSimpleFunctionDescriptor) { "expected an IrBasedSimpleFunctionDescriptor, got $functionDescriptor" diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrFile.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrFile.kt index cae06805859..909beb8bdba 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrFile.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrFile.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.ir.declarations import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.ir.IrElementBase import org.jetbrains.kotlin.ir.IrFileEntry +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.symbols.IrExternalPackageFragmentSymbol import org.jetbrains.kotlin.ir.symbols.IrFileSymbol import org.jetbrains.kotlin.ir.symbols.IrPackageFragmentSymbol @@ -28,6 +29,7 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ import java.io.File abstract class IrPackageFragment : IrElementBase(), IrDeclarationContainer, IrSymbolOwner { + @ObsoleteDescriptorBasedAPI abstract val packageFragmentDescriptor: PackageFragmentDescriptor abstract override val symbol: IrPackageFragmentSymbol diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/dumpKotlinLike.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/dumpKotlinLike.kt index 99160e57a09..8156fc6da5e 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/dumpKotlinLike.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/dumpKotlinLike.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.util import com.intellij.openapi.util.text.StringUtil import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrClassSymbol @@ -139,6 +140,7 @@ private class KotlinLikeDumper(val p: Printer, val options: KotlinLikeDumpOption declaration.acceptChildren(this, null) } + @OptIn(ObsoleteDescriptorBasedAPI::class) override fun visitFile(declaration: IrFile, data: IrDeclaration?) { if (options.printRegionsPerFile) p.println("//region block: ${declaration.name}") diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.fir.kt index ad889d7b125..12d364e5b30 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.fir.kt @@ -30,6 +30,11 @@ annotation class E4 @Retention(AnnotationRetention.BINARY) annotation class E5 +@RequiresOptIn(level = RequiresOptIn.Level.WARNING) +@Target(PROPERTY, FUNCTION, PROPERTY_SETTER, VALUE_PARAMETER, FIELD) +@Retention(AnnotationRetention.BINARY) +annotation class E6 + var some: Int @E4 get() = 42 @@ -38,3 +43,66 @@ var some: Int @get:E4 val another: Int = 42 + +class My { + @E6 + override fun hashCode() = 0 +} + +interface Base { + val bar: Int + + val baz: Int + + @E6 + fun foo() + + fun String.withReceiver() +} + +class Derived : Base { + @E6 + override val bar: Int = 42 + + @set:E6 @setparam:E6 + override var baz: Int = 13 + + @E6 + override fun foo() {} + + override fun @receiver:E6 String.withReceiver() {} +} + +abstract class Another : Base { + @delegate:E6 + override val bar: Int by lazy { 42 } +} + +interface A { + fun f() +} + +interface B { + @E6 + fun f() +} + +interface C1 : A, B +interface C2 : B, A + +class X1 : C1 { + @E6 // Ok + override fun f() {} +} + +class X2 : C2 { + @E6 // Ok + override fun f() {} +} + +open class Y(val b: B): B by b + +class Z(b: B) : Y(b) { + @E6 + override fun f() {} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.kt index 48828fc6700..1fa824666f7 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.kt @@ -30,6 +30,11 @@ annotation class E4 @Retention(AnnotationRetention.BINARY) annotation class E5 +@RequiresOptIn(level = RequiresOptIn.Level.WARNING) +@Target(PROPERTY, FUNCTION, PROPERTY_SETTER, VALUE_PARAMETER, FIELD) +@Retention(AnnotationRetention.BINARY) +annotation class E6 + var some: Int @E4 get() = 42 @@ -39,3 +44,66 @@ var some: Int @get:E4 val another: Int = 42 +class My { + @E6 + override fun hashCode() = 0 +} + +interface Base { + val bar: Int + + val baz: Int + + @E6 + fun foo() + + fun String.withReceiver() +} + +class Derived : Base { + @E6 + override val bar: Int = 42 + + @set:E6 @setparam:E6 + override var baz: Int = 13 + + @E6 + override fun foo() {} + + override fun @receiver:E6 String.withReceiver() {} +} + +abstract class Another : Base { + @delegate:E6 + override val bar: Int by lazy { 42 } +} + +interface A { + fun f() +} + +interface B { + @E6 + fun f() +} + +interface C1 : A, B +interface C2 : B, A + +class X1 : C1 { + @E6 // Ok + override fun f() {} +} + +class X2 : C2 { + @E6 // Ok + override fun f() {} +} + +open class Y(val b: B): B by b + +class Z(b: B) : Y(b) { + @E6 + override fun f() {} +} + diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.txt b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.txt index a4c71c6b7f8..f52576ff083 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.txt @@ -4,6 +4,66 @@ package api { @get:api.E4 public val another: kotlin.Int = 42 @get:api.E4 @set:api.E5 public var some: kotlin.Int + public interface A { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public abstract class Another : api.Base { + public constructor Another() + @delegate:api.E6 public open override /*1*/ val bar: kotlin.Int + public abstract override /*1*/ /*fake_override*/ val baz: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @api.E6 public abstract override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + public abstract override /*1*/ /*fake_override*/ fun kotlin.String.withReceiver(): kotlin.Unit + } + + public interface B { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @api.E6 public abstract fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface Base { + public abstract val bar: kotlin.Int + public abstract val baz: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @api.E6 public abstract fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + public abstract fun kotlin.String.withReceiver(): kotlin.Unit + } + + public interface C1 : api.A, api.B { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*2*/ /*fake_override*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface C2 : api.B, api.A { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @api.E6 public abstract override /*2*/ /*fake_override*/ fun f(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class Derived : api.Base { + public constructor Derived() + @api.E6 public open override /*1*/ val bar: kotlin.Int = 42 + @set:api.E6 @setparam:api.E6 public open override /*1*/ var baz: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @api.E6 public open override /*1*/ fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + public open override /*1*/ fun @receiver:api.E6 kotlin.String.withReceiver(): kotlin.Unit + } + @kotlin.RequiresOptIn(level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.PROPERTY, AnnotationTarget.FIELD, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.TYPE, AnnotationTarget.TYPEALIAS}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class E1 : kotlin.Annotation { public constructor E1() public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean @@ -38,4 +98,52 @@ package api { public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + + @kotlin.RequiresOptIn(level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FIELD}) @kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class E6 : kotlin.Annotation { + public constructor E6() + 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class My { + public constructor My() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @api.E6 public open override /*1*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class X1 : api.C1 { + public constructor X1() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @api.E6 public open override /*1*/ fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class X2 : api.C2 { + public constructor X2() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @api.E6 public open override /*1*/ fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public open class Y : api.B { + public constructor Y(/*0*/ b: api.B) + public final val b: api.B + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @api.E6 public open override /*1*/ /*delegation*/ fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class Z : api.Y { + public constructor Z(/*0*/ b: api.B) + public final override /*1*/ /*fake_override*/ val b: api.B + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @api.E6 public open override /*1*/ fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } }