Introduce use-site annotation target checks for FIR

This commit is contained in:
Mikhail Glukhikh
2021-05-18 18:39:08 +03:00
parent 9a13ec9b76
commit 6572010eed
25 changed files with 247 additions and 208 deletions
@@ -180,6 +180,19 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
parameter<String>("actualTarget")
parameter<String>("useSiteTarget")
}
val INAPPLICABLE_TARGET_ON_PROPERTY by error<PsiElement> {
parameter<String>("useSiteDescription")
}
val INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE by error<PsiElement> {
parameter<String>("useSiteDescription")
}
val INAPPLICABLE_TARGET_PROPERTY_HAS_NO_DELEGATE by error<PsiElement>()
val INAPPLICABLE_TARGET_PROPERTY_HAS_NO_BACKING_FIELD by error<PsiElement>()
val INAPPLICABLE_PARAM_TARGET by error<PsiElement>()
val REDUNDANT_ANNOTATION_TARGET by warning<PsiElement> {
parameter<String>("useSiteDescription")
}
val INAPPLICABLE_FILE_TARGET by error<PsiElement>()
}
val EXPOSED_VISIBILITY by object : DiagnosticGroup("Exposed visibility") {
@@ -181,6 +181,13 @@ object FirErrors {
val RESTRICTED_RETENTION_FOR_EXPRESSION_ANNOTATION by error0<PsiElement>()
val WRONG_ANNOTATION_TARGET by error1<KtAnnotationEntry, String>()
val WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET by error2<KtAnnotationEntry, String, String>()
val INAPPLICABLE_TARGET_ON_PROPERTY by error1<PsiElement, String>()
val INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE by error1<PsiElement, String>()
val INAPPLICABLE_TARGET_PROPERTY_HAS_NO_DELEGATE by error0<PsiElement>()
val INAPPLICABLE_TARGET_PROPERTY_HAS_NO_BACKING_FIELD by error0<PsiElement>()
val INAPPLICABLE_PARAM_TARGET by error0<PsiElement>()
val REDUNDANT_ANNOTATION_TARGET by warning1<PsiElement, String>()
val INAPPLICABLE_FILE_TARGET by error0<PsiElement>()
// Exposed visibility
val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3<KtNamedDeclaration, EffectiveVisibility, FirMemberDeclaration, EffectiveVisibility>(SourceElementPositioningStrategies.DECLARATION_NAME)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
@@ -77,6 +78,10 @@ object FirAnnotationChecker : FirAnnotatedDeclarationChecker() {
return actualTargets.onlyWithUseSiteTarget.any { it in applicableTargets && it == useSiteMapping }
}
if (useSiteTarget != null) {
checkAnnotationUseSiteTarget(declaration, annotation, useSiteTarget, context, reporter)
}
if (check(actualTargets.defaultTargets) || check(actualTargets.canBeSubstituted) || checkWithUseSiteTargets()) {
return
}
@@ -100,6 +105,64 @@ object FirAnnotationChecker : FirAnnotatedDeclarationChecker() {
}
}
private fun checkAnnotationUseSiteTarget(
annotated: FirAnnotatedDeclaration,
annotation: FirAnnotationCall,
target: AnnotationUseSiteTarget,
context: CheckerContext,
reporter: DiagnosticReporter
) {
when (target) {
AnnotationUseSiteTarget.PROPERTY,
AnnotationUseSiteTarget.PROPERTY_GETTER -> {
}
AnnotationUseSiteTarget.FIELD -> {
if (annotated is FirProperty && annotated.delegateFieldSymbol != null && !annotated.hasBackingField) {
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_TARGET_PROPERTY_HAS_NO_BACKING_FIELD, context)
}
}
AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD -> {
if (annotated is FirProperty && annotated.delegateFieldSymbol == null) {
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_TARGET_PROPERTY_HAS_NO_DELEGATE, context)
}
}
AnnotationUseSiteTarget.PROPERTY_SETTER,
AnnotationUseSiteTarget.SETTER_PARAMETER -> {
if (annotated !is FirProperty || annotated.isLocal) {
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_TARGET_ON_PROPERTY, target.renderName, context)
} else if (!annotated.isVar) {
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE, target.renderName, context)
}
}
AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER -> when {
annotated is FirValueParameter -> {
val container = context.containingDeclarations.lastOrNull()
if (container is FirConstructor && container.isPrimary) {
reporter.reportOn(annotation.source, FirErrors.REDUNDANT_ANNOTATION_TARGET, target.renderName, context)
} else {
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_PARAM_TARGET, context)
}
}
annotated is FirProperty && annotated.source?.kind == FirFakeSourceElementKind.PropertyFromParameter -> {
}
else -> reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_PARAM_TARGET, context)
}
AnnotationUseSiteTarget.FILE -> {
// NB: report once?
if (annotated !is FirFile) {
reporter.reportOn(annotation.source, FirErrors.INAPPLICABLE_FILE_TARGET, context)
}
}
AnnotationUseSiteTarget.RECEIVER -> {
// NB: report once?
// annotation with use-site target `receiver` can be only on type reference, but not on declaration
reporter.reportOn(
annotation.source, FirErrors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET, "declaration", target.renderName, context
)
}
}
}
private fun checkDeprecatedCalls(
deprecatedSinceKotlinCall: FirAnnotationCall,
deprecatedCall: FirAnnotationCall?,
@@ -141,8 +141,14 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_CONST_EXP
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_KOTLIN_VERSION_STRING_VALUE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_UNDERSCORE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_CANDIDATE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_FILE_TARGET
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_INFIX_MODIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_LATEINIT_MODIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_PARAM_TARGET
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_TARGET_ON_PROPERTY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_TARGET_PROPERTY_HAS_NO_BACKING_FIELD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_TARGET_PROPERTY_HAS_NO_DELEGATE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_ENUM_COMPARISON_ERROR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_MODIFIERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_TYPES
@@ -238,6 +244,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RECURSION_IN_IMPL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RECURSION_IN_INLINE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RECURSION_IN_SUPERTYPES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_ANNOTATION_TARGET
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_CALL_OF_CONVERSION_METHOD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_EXPLICIT_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODALITY_MODIFIER
@@ -449,7 +456,7 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
map.put(NULLABLE_TYPE_OF_ANNOTATION_MEMBER, "An annotation parameter cannot be nullable")
map.put(INVALID_TYPE_OF_ANNOTATION_MEMBER, "Invalid type of annotation member")
map.put(VAR_ANNOTATION_PARAMETER, "An annotation parameter cannot be 'var'")
map.put(ANNOTATION_CLASS_CONSTRUCTOR_CALL, "Annotation class cannot be instantiated");
map.put(ANNOTATION_CLASS_CONSTRUCTOR_CALL, "Annotation class cannot be instantiated")
map.put(NOT_AN_ANNOTATION_CLASS, "Illegal annotation class: {0}", NULLABLE_STRING)
map.put(SUPERTYPES_FOR_ANNOTATION_CLASS, "Annotation class cannot have supertypes")
map.put(
@@ -478,13 +485,23 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
"DeprecatedSinceKotlin annotation cannot be used outside 'kotlin' subpackages"
)
map.put(ANNOTATION_ON_SUPERCLASS, "Annotations on superclass are meaningless")
map.put(WRONG_ANNOTATION_TARGET, "This annotation is not applicable to target ''{0}''", TO_STRING);
map.put(WRONG_ANNOTATION_TARGET, "This annotation is not applicable to target ''{0}''", TO_STRING)
map.put(INAPPLICABLE_TARGET_ON_PROPERTY, "''@{0}:'' annotations could be applied only to property declarations", TO_STRING)
map.put(INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE, "''@{0}:'' annotations could be applied only to mutable properties", TO_STRING)
map.put(INAPPLICABLE_TARGET_PROPERTY_HAS_NO_DELEGATE, "'@delegate:' annotations could be applied only to delegated properties")
map.put(
INAPPLICABLE_TARGET_PROPERTY_HAS_NO_BACKING_FIELD,
"'@field:' annotations could be applied only to properties with backing fields"
)
map.put(INAPPLICABLE_PARAM_TARGET, "'@param:' annotations could be applied only to primary constructor parameters")
map.put(REDUNDANT_ANNOTATION_TARGET, "Redundant annotation target ''{0}''", TO_STRING)
map.put(INAPPLICABLE_FILE_TARGET, "'@file:' annotations can only be applied before package declaration")
map.put(
WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET,
"This annotation is not applicable to target ''{0}'' and use site target ''@{1}''",
TO_STRING,
TO_STRING
);
)
// Exposed visibility group // #
map.put(