diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOverrideChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOverrideChecker.kt index 8b550d44b9c..fbbe7d09d0d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOverrideChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOverrideChecker.kt @@ -5,13 +5,16 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker.Experimentality +import org.jetbrains.kotlin.fir.analysis.checkers.outerClassSymbol import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors @@ -24,6 +27,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isOverride import org.jetbrains.kotlin.fir.declarations.utils.modality import org.jetbrains.kotlin.fir.declarations.utils.visibility import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap +import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.scopes.FirTypeScope import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenFunctions @@ -32,10 +36,8 @@ import org.jetbrains.kotlin.fir.scopes.impl.toConeType import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.ensureResolved import org.jetbrains.kotlin.fir.symbols.impl.* -import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType -import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.coneType -import org.jetbrains.kotlin.fir.types.upperBoundIfFlexible +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.TypeCheckerState @@ -318,16 +320,45 @@ object FirOverrideChecker : FirClassChecker() { reporter: DiagnosticReporter ) { with(FirOptInUsageBaseChecker) { - val experimentalities = mutableSetOf() + val overriddenExperimentalities = mutableSetOf() + val session = context.session for (overriddenMemberSymbol in overriddenMemberSymbols) { - overriddenMemberSymbol.loadExperimentalitiesFromAnnotationTo(context.session, experimentalities) + overriddenMemberSymbol.loadExperimentalitiesFromAnnotationTo(session, overriddenExperimentalities) } reportNotAcceptedOverrideExperimentalities( - experimentalities, memberSymbol, context, reporter + overriddenExperimentalities, memberSymbol, context, reporter ) + for (annotation in memberSymbol.fir.annotations) { + val annotationType = annotation.annotationTypeRef.coneTypeSafe() + val lookupTag = annotationType?.lookupTag ?: continue + withSuppressedDiagnostics(annotation, context) { + val useSiteTarget = annotation.useSiteTarget + if (useSiteTarget == null || useSiteTarget == AnnotationUseSiteTarget.PROPERTY) { + val experimentality = lookupTag.toFirRegularClassSymbol(session)?.loadExperimentalityForMarkerAnnotation() + if (experimentality != null && experimentality !in overriddenExperimentalities && overriddenMemberSymbols.all { + val ownerClassSymbol = it.dispatchReceiverClassOrNull()?.toFirRegularClassSymbol(session) + ownerClassSymbol == null || !ownerClassSymbol.hasAnnotationItselfOrInParent(context, lookupTag.classId) + } + ) { + val factory = if (session.languageVersionSettings.supportsFeature(LanguageFeature.OptInOnOverrideForbidden)) { + FirErrors.OPT_IN_MARKER_ON_OVERRIDE + } else { + FirErrors.OPT_IN_MARKER_ON_OVERRIDE_WARNING + } + reporter.reportOn(annotation.source, factory, context) + } + } + } + } } } + private tailrec fun FirRegularClassSymbol.hasAnnotationItselfOrInParent(context: CheckerContext, classId: ClassId): Boolean { + if (getAnnotationByClassId(classId) != null) return true + val outerClassSymbol = outerClassSymbol(context) as? FirRegularClassSymbol ?: return false + return outerClassSymbol.hasAnnotationItselfOrInParent(context, classId) + } + private fun DiagnosticReporter.reportNothingToOverride(declaration: FirCallableSymbol<*>, context: CheckerContext) { reportOn(declaration.source, FirErrors.NOTHING_TO_OVERRIDE, declaration, context) } diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalOnOverrideWarning.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalOnOverrideWarning.fir.kt deleted file mode 100644 index cfcde0a63e6..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalOnOverrideWarning.fir.kt +++ /dev/null @@ -1,12 +0,0 @@ -// !OPT_IN: kotlin.RequiresOptIn -// !LANGUAGE: -OptInOnOverrideForbidden - -@RequiresOptIn(level = RequiresOptIn.Level.WARNING) -@Retention(AnnotationRetention.BINARY) -annotation class E - -class My { - @E - override fun hashCode() = 0 -} - diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalOnOverrideWarning.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalOnOverrideWarning.kt index 0b692995ae0..24b1a125a43 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalOnOverrideWarning.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalOnOverrideWarning.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !OPT_IN: kotlin.RequiresOptIn // !LANGUAGE: -OptInOnOverrideForbidden diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.fir.kt deleted file mode 100644 index 9f20ba0f013..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.fir.kt +++ /dev/null @@ -1,144 +0,0 @@ -// !OPT_IN: kotlin.RequiresOptIn -// !LANGUAGE: +OptInOnOverrideForbidden -// FILE: api.kt - -package api - -import kotlin.annotation.AnnotationTarget.* - -@RequiresOptIn(level = RequiresOptIn.Level.WARNING) -@Target(CLASS, ANNOTATION_CLASS, PROPERTY, FIELD, LOCAL_VARIABLE, VALUE_PARAMETER, CONSTRUCTOR, FUNCTION, - PROPERTY_SETTER, TYPEALIAS) -@Retention(AnnotationRetention.BINARY) -annotation class E1 - -@RequiresOptIn(level = RequiresOptIn.Level.WARNING) -@Target(FILE) -annotation class E2 - -@RequiresOptIn(level = RequiresOptIn.Level.WARNING) -@Target(EXPRESSION) -@Retention(AnnotationRetention.SOURCE) -annotation class E3 - -@RequiresOptIn(level = RequiresOptIn.Level.WARNING) -@Target(TYPE_PARAMETER) -@Retention(AnnotationRetention.BINARY) -annotation class E3A - -@RequiresOptIn(level = RequiresOptIn.Level.WARNING) -@Target(TYPE) -@Retention(AnnotationRetention.BINARY) -annotation class E3B - -@RequiresOptIn(level = RequiresOptIn.Level.WARNING) -@Target(PROPERTY_GETTER) -@Retention(AnnotationRetention.BINARY) -annotation class E4 - -@RequiresOptIn(level = RequiresOptIn.Level.WARNING) -@Target(PROPERTY_SETTER) -@Retention(AnnotationRetention.BINARY) -annotation class E5 - -@RequiresOptIn(level = RequiresOptIn.Level.WARNING) -@Target(PROPERTY, FUNCTION, PROPERTY_SETTER, VALUE_PARAMETER, FIELD, LOCAL_VARIABLE, CLASS) -@Retention(AnnotationRetention.BINARY) -annotation class E6 - -var some: Int - @E4 - get() = 42 - @E5 - set(value) {} - -@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() {} -} - -@E6 -interface BaseMarked { - val bar: Int -} - -@E6 -class Outer { - interface Nested { - val baz: Int - } -} - -@OptIn(E6::class) -class DerivedOptIn : BaseMarked, Outer.Nested { - @E6 - override val bar: Int = 42 // Ok - - @E6 - override val baz: Int = 24 // Ok -} - -abstract class Another(@param:E6 val x: String) : Base { - @delegate:E6 - override val bar: Int by lazy { 42 } - - fun baz(@E6 param: Int) { - @E6 val x = param - } -} - -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 0f2cd36d4e1..59f7c7c826e 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !OPT_IN: kotlin.RequiresOptIn // !LANGUAGE: +OptInOnOverrideForbidden // FILE: api.kt