Forbid using experimental markers on override declarations

#KT-45844 Fixed
This commit is contained in:
Mikhail Glukhikh
2021-05-21 15:30:51 +03:00
committed by teamcityserver
parent bb9efab3c4
commit 603afe89a2
10 changed files with 294 additions and 3 deletions
@@ -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<ClassDescriptor> {
if (type.sort != Type.OBJECT) return emptyList()
@@ -287,6 +287,7 @@ public interface Errors {
DiagnosticFactory0<PsiElement> EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> EXPERIMENTAL_ANNOTATION_ON_GETTER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> EXPERIMENTAL_ANNOTATION_ON_OVERRIDE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, String> EXPERIMENTAL_UNSIGNED_LITERALS = DiagnosticFactory1.create(WARNING);
DiagnosticFactory1<PsiElement, String> EXPERIMENTAL_UNSIGNED_LITERALS_ERROR = DiagnosticFactory1.create(ERROR);
@@ -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);
@@ -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<KtAnnotated>() ?: 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<CallableMemberDescriptor> = 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<ConstantValue<*>>, trace: BindingTrace, entry: KtAnnotationEntry) {
if (annotationClasses.isEmpty()) {
trace.report(Errors.USE_EXPERIMENTAL_WITHOUT_ARGUMENTS.on(entry))
@@ -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"
@@ -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
@@ -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}")
@@ -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() {}
}
@@ -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
<!EXPERIMENTAL_ANNOTATION_ON_GETTER!>@E4<!>
get() = 42
@@ -39,3 +44,66 @@ var some: Int
<!EXPERIMENTAL_ANNOTATION_ON_GETTER!>@get:E4<!>
val another: Int = 42
class My {
<!EXPERIMENTAL_ANNOTATION_ON_OVERRIDE!>@E6<!>
override fun hashCode() = 0
}
interface Base {
val bar: Int
val baz: Int
@E6
fun foo()
fun String.withReceiver()
}
class Derived : Base {
<!EXPERIMENTAL_ANNOTATION_ON_OVERRIDE!>@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() {}
}
@@ -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
}
}