[FIR] Add REPEATED_ANNOTATION_WITH_CONTAINER, NON_SOURCE_REPEATED_ANNOTATION

REPEATED_ANNOTATION_TARGET6
This commit is contained in:
Ivan Kochurkin
2021-08-31 22:40:30 +03:00
committed by TeamCityServer
parent 2baed77598
commit 97bc079634
16 changed files with 224 additions and 234 deletions
@@ -7,15 +7,16 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.Named
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
@Suppress("UNUSED_VARIABLE", "LocalVariableName", "ClassName", "unused")
@OptIn(PrivateForInline::class)
@@ -111,6 +112,15 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") {
val EXTERNAL_DECLARATION_CANNOT_BE_INLINED by error<KtDeclaration>(PositioningStrategy.DECLARATION_SIGNATURE)
}
val REPEATABLE by object : DiagnosticGroup("Repeatable Annotations") {
val NON_SOURCE_REPEATED_ANNOTATION by error<KtAnnotationEntry>()
val REPEATED_ANNOTATION_TARGET6 by error<KtAnnotationEntry>()
val REPEATED_ANNOTATION_WITH_CONTAINER by error<KtAnnotationEntry> {
parameter<FqName>("name")
parameter<FqName>("explicitContainerName")
}
}
val MISC by object : DiagnosticGroup("Misc") {
val INAPPLICABLE_JVM_FIELD by error<KtAnnotationEntry> {
parameter<String>("message")
@@ -89,6 +89,11 @@ object FirJvmErrors {
val EXTERNAL_DECLARATION_IN_INTERFACE by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
val EXTERNAL_DECLARATION_CANNOT_BE_INLINED by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
// Repeatable Annotations
val NON_SOURCE_REPEATED_ANNOTATION by error0<KtAnnotationEntry>()
val REPEATED_ANNOTATION_TARGET6 by error0<KtAnnotationEntry>()
val REPEATED_ANNOTATION_WITH_CONTAINER by error2<KtAnnotationEntry, FqName, FqName>()
// Misc
val INAPPLICABLE_JVM_FIELD by error1<KtAnnotationEntry, String>()
val INAPPLICABLE_JVM_FIELD_WARNING by warning1<KtAnnotationEntry, String>()
@@ -19,6 +19,7 @@ object JvmDeclarationCheckers : DeclarationCheckers() {
override val annotatedDeclarationCheckers: Set<FirAnnotatedDeclarationChecker>
get() = setOf(
FirJvmStaticChecker,
FirRepeatableAnnotationChecker
)
override val classCheckers: Set<FirClassChecker>
@@ -0,0 +1,106 @@
/*
* 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.analysis.jvm.checkers.declaration
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirAnnotatedDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.fqName
import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirEnumEntrySymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.lowerBoundIfFlexible
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
object FirRepeatableAnnotationChecker : FirAnnotatedDeclarationChecker() {
private val RETENTION_PARAMETER_NAME = Name.identifier("value")
private val REPEATABLE_PARAMETER_NAME = Name.identifier("value")
private val JAVA_REPEATABLE_ANNOTATION = FqName("java.lang.annotation.Repeatable")
override fun check(declaration: FirAnnotatedDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
val annotationsSet = hashSetOf<FqName>()
val session = context.session
val annotations = declaration.annotations
for (annotation in annotations) {
val classId = annotation.classId ?: continue
val annotationClassId = annotation.toAnnotationClassId() ?: continue
if (annotationClassId.isLocal) continue
val annotationClass = session.symbolProvider.getClassLikeSymbolByFqName(annotationClassId) ?: continue
// TODO: consider REPEATED_ANNOTATION ?
if (fqName in annotationsSet &&
annotationClass.isRepeatableAnnotation(session) &&
annotationClass.getAnnotationRetention() != AnnotationRetention.SOURCE
) {
if (session.moduleData.platform.componentPlatforms.any { it.targetName == "1.6" }) {
reporter.reportOn(annotation.source, FirJvmErrors.REPEATED_ANNOTATION_TARGET6, context)
} else if (session.languageVersionSettings.supportsFeature(LanguageFeature.RepeatableAnnotations)) {
// It's not allowed to have both a repeated annotation (applied more than once) and its container
// on the same element.
// See https://docs.oracle.com/javase/specs/jls/se16/html/jls-9.html#jls-9.7.5.
val explicitContainer = annotationClass.resolveContainerAnnotation()
if (explicitContainer != null && annotations.any { it.fqName(session) == explicitContainer }) {
reporter.reportOn(
annotation.source,
FirJvmErrors.REPEATED_ANNOTATION_WITH_CONTAINER,
fqName,
explicitContainer,
context
)
}
} else {
reporter.reportOn(annotation.source, FirJvmErrors.NON_SOURCE_REPEATED_ANNOTATION, context)
}
}
annotationsSet.add(fqName)
}
}
private fun FirClassLikeSymbol<*>.isRepeatableAnnotation(session: FirSession): Boolean {
if (getAnnotationByFqName(StandardNames.FqNames.repeatable) != null) return true
if (getAnnotationByFqName(JAVA_REPEATABLE_ANNOTATION) == null) return false
return session.languageVersionSettings.supportsFeature(LanguageFeature.RepeatableAnnotations) ||
getAnnotationRetention() == AnnotationRetention.SOURCE
}
private fun FirClassLikeSymbol<*>.getAnnotationRetention(): AnnotationRetention? {
val repeatableAnnotation = getAnnotationByFqName(StandardNames.FqNames.retention) ?: return null
val retentionValue = repeatableAnnotation.findArgumentByName(RETENTION_PARAMETER_NAME) ?: return null
val resolvedSymbol = retentionValue.toResolvedCallableSymbol() as? FirEnumEntrySymbol ?: return null
val name = resolvedSymbol.name.asString()
return AnnotationRetention.values().firstOrNull { it.name == name }
}
private fun FirClassLikeSymbol<*>.resolveContainerAnnotation(): FqName? {
val repeatableAnnotation =
getAnnotationByFqName(StandardNames.FqNames.repeatable) ?: getAnnotationByFqName(JAVA_REPEATABLE_ANNOTATION) ?: return null
val value = repeatableAnnotation.findArgumentByName(REPEATABLE_PARAMETER_NAME) ?: return null
val classCallArgument = (value as? FirGetClassCall)?.argument ?: return null
if (classCallArgument is FirResolvedQualifier) {
return classCallArgument.relativeClassFqName
} else if (classCallArgument is FirClassReferenceExpression) {
val type = classCallArgument.classTypeRef.coneType.lowerBoundIfFlexible() as? ConeClassLikeType ?: return null
return type.lookupTag.classId.asSingleFqName()
}
return null
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.jvm.diagnostics
import org.jetbrains.kotlin.diagnostics.rendering.Renderers.NAME
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.STRING
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDefaultErrorMessages
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_TYPE
@@ -47,6 +48,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.LOCAL_JVM_
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.NON_DATA_CLASS_JVM_RECORD
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.NON_FINAL_JVM_RECORD
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.NON_JVM_DEFAULT_OVERRIDES_JAVA_DEFAULT
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.NON_SOURCE_REPEATED_ANNOTATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_ABSTRACT
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_INTERFACE
@@ -54,6 +56,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_PRIVATE
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERRIDE_CANNOT_BE_STATIC
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.REPEATED_ANNOTATION_TARGET6
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.REPEATED_ANNOTATION_WITH_CONTAINER
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.STRICTFP_ON_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SUPER_CALL_WITH_DEFAULT_PARAMETERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZED_IN_INTERFACE
@@ -172,6 +176,34 @@ object FirJvmDefaultErrorMessages {
map.put(INAPPLICABLE_JVM_FIELD_WARNING, "{0}. This warning will become an error in further releases", STRING)
map.put(JVM_SYNTHETIC_ON_DELEGATE, "'@JvmSynthetic' annotation cannot be used on delegated properties")
map.put(
NON_SOURCE_REPEATED_ANNOTATION,
"Repeatable annotations with non-SOURCE retention are only supported starting from Kotlin 1.6"
)
map.put(
REPEATED_ANNOTATION_TARGET6,
"Repeatable annotations with non-SOURCE retention are not supported with JVM target 1.6. Use -jvm-target 1.8"
)
map.put(
REPEATED_ANNOTATION_WITH_CONTAINER,
"Repeated annotation ''@{0}'' cannot be used on a declaration which is annotated with its container annotation ''@{1}''",
TO_STRING,
TO_STRING
)
map.put(
DEFAULT_METHOD_CALL_FROM_JAVA6_TARGET,
"Super calls to Java default methods are prohibited in JVM target 1.6. Recompile with '-jvm-target 1.8'"
)
map.put(
INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET,
"Calls to static methods in Java interfaces are prohibited in JVM target 1.6. Recompile with '-jvm-target 1.8'"
)
map.put(
INTERFACE_CANT_CALL_DEFAULT_METHOD_VIA_SUPER,
"Interfaces can call default methods via super only within @JvmDefault members. Please annotate the containing interface member with @JvmDefault"
)
}
}
}
@@ -50,12 +50,12 @@ fun jr1() {}
@JR
@JR.Container()
@JR
<!NON_SOURCE_REPEATED_ANNOTATION!>@JR<!>
fun jr2() {}
@JR
@JR.Container(JR())
@JR
<!NON_SOURCE_REPEATED_ANNOTATION!>@JR<!>
fun jr3() {}
@JR
@@ -1,142 +0,0 @@
// !LANGUAGE: +RepeatableAnnotations
// FULL_JDK
// FILE: JR.java
import java.lang.annotation.*;
@Repeatable(JR.Container.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface JR {
public @interface Container {
JR[] value();
}
}
// FILE: JS.java
import java.lang.annotation.*;
@Repeatable(JS.Container.class)
@Retention(RetentionPolicy.SOURCE)
public @interface JS {
public @interface Container {
JS[] value();
}
}
// FILE: KR.kt
@java.lang.annotation.Repeatable(KR.Container::class)
@Retention(AnnotationRetention.RUNTIME)
annotation class KR {
annotation class Container(val value: Array<KR>)
}
// FILE: KS.kt
@java.lang.annotation.Repeatable(KS.Container::class)
@Retention(AnnotationRetention.SOURCE)
annotation class KS {
annotation class Container(val value: Array<KS>)
}
// FILE: test.kt
// Java runtime-retained annotation
@JR
@JR.Container()
fun jr1() {}
@JR
@JR.Container()
@JR
fun jr2() {}
@JR
@JR.Container(JR())
@JR
fun jr3() {}
@JR
@JR.Container(JR())
fun jr4() {}
@JR
@JR.Container(JR(), JR())
fun jr5() {}
// Java source-retained annotation
@JS
@JS.Container()
fun js1() {}
@JS
@JS.Container()
@JS
fun js2() {}
@JS
@JS.Container(JS())
@JS
fun js3() {}
@JS
@JS.Container(JS())
fun js4() {}
@JS
@JS.Container(JS(), JS())
fun js5() {}
// Kotlin runtime-retained annotation
@KR.Container([])
@KR
fun kr1() {}
@KR.Container([])
@KR
@KR
fun kr2() {}
@KR
@KR
@KR.Container([KR()])
fun kr3() {}
@KR.Container([KR()])
@KR
fun kr4() {}
@KR
@KR.Container([KR(), KR()])
fun kr5() {}
// Kotlin source-retained annotation
@KS.Container([])
@KS
fun ks1() {}
@KS.Container([])
@KS
@KS
fun ks2() {}
@KS
@KS
@KS.Container([KS()])
fun ks3() {}
@KS.Container([KS()])
@KS
fun ks4() {}
@KS
@KS.Container([KS(), KS()])
fun ks5() {}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: +RepeatableAnnotations
// FULL_JDK
// FILE: JR.java
@@ -1,49 +0,0 @@
// !LANGUAGE: +RepeatableAnnotations
// !JVM_TARGET: 1.6
// FULL_JDK
// FILE: Runtime.java
import java.lang.annotation.*;
@Repeatable(Runtime.Container.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Runtime {
public @interface Container {
Runtime[] value();
}
}
// FILE: Clazz.java
import java.lang.annotation.*;
@Repeatable(Clazz.Container.class)
@Retention(RetentionPolicy.CLASS)
public @interface Clazz {
public @interface Container {
Clazz[] value();
}
}
// FILE: Source.java
import java.lang.annotation.*;
@Repeatable(Source.Container.class)
@Retention(RetentionPolicy.SOURCE)
public @interface Source {
public @interface Container {
Source[] value();
}
}
// FILE: usage.kt
@Runtime @Runtime
class UseRuntime
@Clazz @Clazz
class UseClazz
@Source @Source
class UseSource
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: +RepeatableAnnotations
// !JVM_TARGET: 1.6
// FULL_JDK
@@ -38,10 +38,10 @@ public @interface Source {
// FILE: usage.kt
@Runtime @Runtime
@Runtime <!NON_SOURCE_REPEATED_ANNOTATION!>@Runtime<!>
class UseRuntime
@Clazz @Clazz
@Clazz <!NON_SOURCE_REPEATED_ANNOTATION!>@Clazz<!>
class UseClazz
@Source @Source
@@ -1,33 +0,0 @@
// !LANGUAGE: -RepeatableAnnotations
@Repeatable
annotation class repann
@Retention(AnnotationRetention.SOURCE)
@Repeatable
annotation class repann1(val x: Int)
@Retention(AnnotationRetention.SOURCE)
@Repeatable
annotation class repann2(val f: Boolean)
@Retention(AnnotationRetention.BINARY)
@Repeatable
annotation class binrepann
@Target(AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@Repeatable
annotation class repexpr
@repann @repann class DoubleAnnotated
@repann1(1) @repann1(2) @repann1(3) class TripleAnnotated
@repann2(true) @repann2(false) @repann2(false) @repann2(true) class FourTimesAnnotated
@binrepann @binrepann class BinaryAnnotated
@repann @repann fun foo(@repann @repann x: Int): Int {
@repexpr @repexpr return x
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: -RepeatableAnnotations
@Repeatable
@@ -2520,7 +2520,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
firDiagnostic.b,
firDiagnostic.c.mapKeys { (incompatible, _) ->
incompatible
}.mapValues { (_, collection) ->
}.mapValues { (_, collection) ->
collection.map { firBasedSymbol ->
firSymbolBuilder.buildSymbol(firBasedSymbol.fir)
}
@@ -2534,7 +2534,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
firSymbolBuilder.buildSymbol(firDiagnostic.a.fir),
firDiagnostic.b.mapKeys { (incompatible, _) ->
incompatible
}.mapValues { (_, collection) ->
}.mapValues { (_, collection) ->
collection.map { firBasedSymbol ->
firSymbolBuilder.buildSymbol(firBasedSymbol.fir)
}
@@ -2569,7 +2569,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
firDiagnostic.b.map { pair ->
firSymbolBuilder.buildSymbol(pair.first.fir) to pair.second.mapKeys { (incompatible, _) ->
incompatible
}.mapValues { (_, collection) ->
}.mapValues { (_, collection) ->
collection.map { firBasedSymbol ->
firSymbolBuilder.buildSymbol(firBasedSymbol.fir)
}
@@ -3780,6 +3780,26 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirJvmErrors.NON_SOURCE_REPEATED_ANNOTATION) { firDiagnostic ->
NonSourceRepeatedAnnotationImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirJvmErrors.REPEATED_ANNOTATION_TARGET6) { firDiagnostic ->
RepeatedAnnotationTarget6Impl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirJvmErrors.REPEATED_ANNOTATION_WITH_CONTAINER) { firDiagnostic ->
RepeatedAnnotationWithContainerImpl(
firDiagnostic.a,
firDiagnostic.b,
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirJvmErrors.INAPPLICABLE_JVM_FIELD) { firDiagnostic ->
InapplicableJvmFieldImpl(
firDiagnostic.a,
@@ -2628,6 +2628,20 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = ExternalDeclarationCannotBeInlined::class
}
abstract class NonSourceRepeatedAnnotation : KtFirDiagnostic<KtAnnotationEntry>() {
override val diagnosticClass get() = NonSourceRepeatedAnnotation::class
}
abstract class RepeatedAnnotationTarget6 : KtFirDiagnostic<KtAnnotationEntry>() {
override val diagnosticClass get() = RepeatedAnnotationTarget6::class
}
abstract class RepeatedAnnotationWithContainer : KtFirDiagnostic<KtAnnotationEntry>() {
override val diagnosticClass get() = RepeatedAnnotationWithContainer::class
abstract val name: FqName
abstract val explicitContainerName: FqName
}
abstract class InapplicableJvmField : KtFirDiagnostic<KtAnnotationEntry>() {
override val diagnosticClass get() = InapplicableJvmField::class
abstract val message: String
@@ -4273,6 +4273,29 @@ internal class ExternalDeclarationCannotBeInlinedImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class NonSourceRepeatedAnnotationImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.NonSourceRepeatedAnnotation(), KtAbstractFirDiagnostic<KtAnnotationEntry> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class RepeatedAnnotationTarget6Impl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.RepeatedAnnotationTarget6(), KtAbstractFirDiagnostic<KtAnnotationEntry> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class RepeatedAnnotationWithContainerImpl(
override val name: FqName,
override val explicitContainerName: FqName,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.RepeatedAnnotationWithContainer(), KtAbstractFirDiagnostic<KtAnnotationEntry> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class InapplicableJvmFieldImpl(
override val message: String,
firDiagnostic: FirPsiDiagnostic,