FIR: report WRONG_ANNOTATION_TARGET(_WITH_USE_SITE_TARGET) on declarations
This commit is contained in:
+2
-2
@@ -12,8 +12,8 @@ annotation class Ann2
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class Ann3(val arg: Int, val s: String)
|
||||
|
||||
@Ann3(
|
||||
<!WRONG_ANNOTATION_TARGET!>@Ann3(
|
||||
<!ANNOTATION_USED_AS_ANNOTATION_ARGUMENT!>@Ann3(
|
||||
<!ANNOTATION_USED_AS_ANNOTATION_ARGUMENT!>@Ann<!> 5, ""
|
||||
)<!> <!ANNOTATION_USED_AS_ANNOTATION_ARGUMENT!>@Ann2<!> 1, ""
|
||||
) val a = 0
|
||||
)<!> val a = 0
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// Should be something like TYPE_MISMATCH here
|
||||
@file:Some(return x)
|
||||
<!WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET!>@file:Some(return x)<!>
|
||||
|
||||
const val x = 42
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ annotation class MyAnn
|
||||
fun bar(x: Int) {}
|
||||
|
||||
fun foo() {
|
||||
@MyAnn
|
||||
<!WRONG_ANNOTATION_TARGET!>@MyAnn<!>
|
||||
val x: Int
|
||||
@MyAnn
|
||||
x = @MyAnn 42
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
FILE: RedundantSetterParameterTypeChecker.kt
|
||||
@R|kotlin/annotation/Target|(vararg(Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.CLASS|)) public final annotation class Ann : R|kotlin/Annotation| {
|
||||
@R|kotlin/annotation/Target|(vararg(Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.VALUE_PARAMETER|)) public final annotation class Ann : R|kotlin/Annotation| {
|
||||
public constructor(): R|Ann| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
compiler/fir/analysis-tests/testData/resolve/extendedCheckers/RedundantSetterParameterTypeChecker.kt
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
annotation class Ann
|
||||
|
||||
<!REDECLARATION!>var x: Int
|
||||
|
||||
@@ -41,6 +41,6 @@ class Second(val y: Char) : @WithInt(0) First() {
|
||||
constructor(): this('\n')
|
||||
}
|
||||
|
||||
@WithInt(24)
|
||||
@VeryComplex(3.14f, 6.67e-11, false, 123456789012345L, null)
|
||||
<!WRONG_ANNOTATION_TARGET!>@WithInt(24)<!>
|
||||
<!WRONG_ANNOTATION_TARGET!>@VeryComplex(3.14f, 6.67e-11, false, 123456789012345L, null)<!>
|
||||
typealias Third = @Simple Second
|
||||
|
||||
+4
@@ -176,6 +176,10 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
||||
val WRONG_ANNOTATION_TARGET by error<KtAnnotationEntry> {
|
||||
parameter<String>("actualTarget")
|
||||
}
|
||||
val WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET by error<KtAnnotationEntry> {
|
||||
parameter<String>("actualTarget")
|
||||
parameter<String>("useSiteTarget")
|
||||
}
|
||||
}
|
||||
|
||||
val EXPOSED_VISIBILITY by object : DiagnosticGroup("Exposed visibility") {
|
||||
|
||||
@@ -180,6 +180,7 @@ object FirErrors {
|
||||
val ANNOTATION_ON_SUPERCLASS by error0<KtAnnotationEntry>()
|
||||
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>()
|
||||
|
||||
// Exposed visibility
|
||||
val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3<KtNamedDeclaration, EffectiveVisibility, FirMemberDeclaration, EffectiveVisibility>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
|
||||
+2
-1
@@ -79,7 +79,8 @@ fun FirAnnotationCall.findSingleArgumentByName(name: Name): FirExpression? {
|
||||
return argumentMapping.keys.firstOrNull()?.takeIf { argumentMapping[it]?.name == name }
|
||||
}
|
||||
// NB: we have to consider both cases, because deserializer does not create argument mapping
|
||||
val firstArgument = argumentList.arguments.firstOrNull() as? FirNamedArgumentExpression ?: return null
|
||||
val arguments = argumentList.arguments
|
||||
val firstArgument = arguments.firstOrNull() as? FirNamedArgumentExpression ?: return arguments.singleOrNull()
|
||||
return firstArgument.takeIf { it.name == name }?.expression
|
||||
}
|
||||
|
||||
|
||||
+157
-28
@@ -5,18 +5,22 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnnotatedDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getAllowedAnnotationTargets
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.argumentMapping
|
||||
import org.jetbrains.kotlin.fir.resolve.fqName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.AnnotationTargetList
|
||||
import org.jetbrains.kotlin.resolve.AnnotationTargetLists
|
||||
|
||||
object FirAnnotationChecker : FirAnnotatedDeclarationChecker() {
|
||||
private val deprecatedClassId = FqName("kotlin.Deprecated")
|
||||
@@ -37,33 +41,158 @@ object FirAnnotationChecker : FirAnnotatedDeclarationChecker() {
|
||||
} else if (fqName == deprecatedSinceKotlinClassId) {
|
||||
deprecatedSinceKotlinCall = annotation
|
||||
}
|
||||
withSuppressedDiagnostics(annotation, context) {
|
||||
checkAnnotationTarget(declaration, annotation, context, reporter)
|
||||
}
|
||||
}
|
||||
if (deprecatedSinceKotlinCall != null) {
|
||||
withSuppressedDiagnostics(deprecatedSinceKotlinCall, context) {
|
||||
checkDeprecatedCalls(deprecatedSinceKotlinCall, deprecatedCall, context, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAnnotationTarget(
|
||||
declaration: FirAnnotatedDeclaration,
|
||||
annotation: FirAnnotationCall,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
if (declaration is FirValueParameter && declaration.source?.hasValOrVar() == true) {
|
||||
// This will be checked later as property
|
||||
return
|
||||
}
|
||||
val actualTargets = getActualTargetList(declaration)
|
||||
val applicableTargets = annotation.getAllowedAnnotationTargets(context.session)
|
||||
val useSiteTarget = annotation.useSiteTarget
|
||||
|
||||
fun check(targets: List<KotlinTarget>) = targets.any {
|
||||
it in applicableTargets && (useSiteTarget == null || KotlinTarget.USE_SITE_MAPPING[useSiteTarget] == it)
|
||||
}
|
||||
|
||||
if (deprecatedSinceKotlinCall != null) {
|
||||
val closestFirFile = context.findClosest<FirFile>()
|
||||
if (closestFirFile != null && !closestFirFile.packageFqName.startsWith(StandardClassIds.BASE_KOTLIN_PACKAGE.shortName())) {
|
||||
reporter.reportOn(
|
||||
deprecatedSinceKotlinCall.source,
|
||||
FirErrors.DEPRECATED_SINCE_KOTLIN_OUTSIDE_KOTLIN_SUBPACKAGE,
|
||||
context
|
||||
)
|
||||
}
|
||||
fun checkWithUseSiteTargets(): Boolean {
|
||||
if (useSiteTarget == null) return false
|
||||
|
||||
if (deprecatedCall == null) {
|
||||
reporter.reportOn(deprecatedSinceKotlinCall.source, FirErrors.DEPRECATED_SINCE_KOTLIN_WITHOUT_DEPRECATED, context)
|
||||
} else {
|
||||
val argumentMapping = deprecatedCall.argumentMapping ?: return
|
||||
for (value in argumentMapping.values) {
|
||||
if (value.name.identifier == "level") {
|
||||
reporter.reportOn(
|
||||
deprecatedSinceKotlinCall.source,
|
||||
FirErrors.DEPRECATED_SINCE_KOTLIN_WITH_DEPRECATED_LEVEL,
|
||||
context
|
||||
)
|
||||
break
|
||||
}
|
||||
val useSiteMapping = KotlinTarget.USE_SITE_MAPPING[useSiteTarget]
|
||||
return actualTargets.onlyWithUseSiteTarget.any { it in applicableTargets && it == useSiteMapping }
|
||||
}
|
||||
|
||||
if (check(actualTargets.defaultTargets) || check(actualTargets.canBeSubstituted) || checkWithUseSiteTargets()) {
|
||||
return
|
||||
}
|
||||
|
||||
val targetDescription = actualTargets.defaultTargets.firstOrNull()?.description ?: "unidentified target"
|
||||
if (useSiteTarget != null) {
|
||||
reporter.reportOn(
|
||||
annotation.source,
|
||||
FirErrors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET,
|
||||
targetDescription,
|
||||
useSiteTarget.renderName,
|
||||
context
|
||||
)
|
||||
} else {
|
||||
reporter.reportOn(
|
||||
annotation.source,
|
||||
FirErrors.WRONG_ANNOTATION_TARGET,
|
||||
targetDescription,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkDeprecatedCalls(
|
||||
deprecatedSinceKotlinCall: FirAnnotationCall,
|
||||
deprecatedCall: FirAnnotationCall?,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val closestFirFile = context.findClosest<FirFile>()
|
||||
if (closestFirFile != null && !closestFirFile.packageFqName.startsWith(StandardClassIds.BASE_KOTLIN_PACKAGE.shortName())) {
|
||||
reporter.reportOn(
|
||||
deprecatedSinceKotlinCall.source,
|
||||
FirErrors.DEPRECATED_SINCE_KOTLIN_OUTSIDE_KOTLIN_SUBPACKAGE,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
if (deprecatedCall == null) {
|
||||
reporter.reportOn(deprecatedSinceKotlinCall.source, FirErrors.DEPRECATED_SINCE_KOTLIN_WITHOUT_DEPRECATED, context)
|
||||
} else {
|
||||
val argumentMapping = deprecatedCall.argumentMapping ?: return
|
||||
for (value in argumentMapping.values) {
|
||||
if (value.name.identifier == "level") {
|
||||
reporter.reportOn(
|
||||
deprecatedSinceKotlinCall.source,
|
||||
FirErrors.DEPRECATED_SINCE_KOTLIN_WITH_DEPRECATED_LEVEL,
|
||||
context
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getActualTargetList(annotated: FirDeclaration): AnnotationTargetList {
|
||||
return when (annotated) {
|
||||
is FirRegularClass -> {
|
||||
AnnotationTargetList(
|
||||
KotlinTarget.classActualTargets(annotated.classKind, annotated.isInner, annotated.isCompanion, annotated.isLocal)
|
||||
)
|
||||
}
|
||||
is KtDestructuringDeclarationEntry -> TargetLists.T_LOCAL_VARIABLE
|
||||
is FirProperty -> {
|
||||
when {
|
||||
annotated.isLocal ->
|
||||
if (annotated.source?.kind == FirFakeSourceElementKind.DesugaredComponentFunctionCall) {
|
||||
TargetLists.T_DESTRUCTURING_DECLARATION
|
||||
} else {
|
||||
TargetLists.T_LOCAL_VARIABLE
|
||||
}
|
||||
annotated.symbol.callableId.classId != null ->
|
||||
if (annotated.source?.kind == FirFakeSourceElementKind.PropertyFromParameter) {
|
||||
TargetLists.T_VALUE_PARAMETER_WITH_VAL
|
||||
} else {
|
||||
TargetLists.T_MEMBER_PROPERTY(annotated.hasBackingField, annotated.delegate != null)
|
||||
}
|
||||
else ->
|
||||
TargetLists.T_TOP_LEVEL_PROPERTY(annotated.hasBackingField, annotated.delegate != null)
|
||||
}
|
||||
}
|
||||
is FirValueParameter -> TargetLists.T_VALUE_PARAMETER_WITHOUT_VAL
|
||||
is FirConstructor -> TargetLists.T_CONSTRUCTOR
|
||||
is FirAnonymousFunction -> {
|
||||
TargetLists.T_FUNCTION_EXPRESSION
|
||||
}
|
||||
is FirSimpleFunction -> {
|
||||
when {
|
||||
annotated.isLocal -> TargetLists.T_LOCAL_FUNCTION
|
||||
annotated.symbol.callableId.classId != null -> TargetLists.T_MEMBER_FUNCTION
|
||||
else -> TargetLists.T_TOP_LEVEL_FUNCTION
|
||||
}
|
||||
}
|
||||
is FirTypeAlias -> TargetLists.T_TYPEALIAS
|
||||
is FirPropertyAccessor -> if (annotated.isGetter) TargetLists.T_PROPERTY_GETTER else TargetLists.T_PROPERTY_SETTER
|
||||
is FirFile -> TargetLists.T_FILE
|
||||
is FirTypeParameter -> TargetLists.T_TYPE_PARAMETER
|
||||
is FirAnonymousInitializer -> TargetLists.T_INITIALIZER
|
||||
is KtDestructuringDeclaration -> TargetLists.T_DESTRUCTURING_DECLARATION
|
||||
is KtLambdaExpression -> TargetLists.T_FUNCTION_LITERAL
|
||||
is FirAnonymousObject ->
|
||||
if (annotated.source?.kind == FirFakeSourceElementKind.EnumInitializer) {
|
||||
AnnotationTargetList(
|
||||
KotlinTarget.classActualTargets(
|
||||
ClassKind.ENUM_ENTRY,
|
||||
isInnerClass = false,
|
||||
isCompanionObject = false,
|
||||
isLocalClass = false
|
||||
)
|
||||
)
|
||||
} else {
|
||||
TargetLists.T_OBJECT_LITERAL
|
||||
}
|
||||
else -> TargetLists.EMPTY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private typealias TargetLists = AnnotationTargetLists
|
||||
|
||||
+3
-3
@@ -51,15 +51,15 @@ class DeclarationCheckersDiagnosticComponent(
|
||||
}
|
||||
|
||||
override fun visitAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: CheckerContext) {
|
||||
checkers.allBasicDeclarationCheckers.check(anonymousFunction, data, reporter)
|
||||
checkers.allAnnotatedDeclarationCheckers.check(anonymousFunction, data, reporter)
|
||||
}
|
||||
|
||||
override fun visitPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: CheckerContext) {
|
||||
checkers.allBasicDeclarationCheckers.check(propertyAccessor, data, reporter)
|
||||
checkers.allAnnotatedDeclarationCheckers.check(propertyAccessor, data, reporter)
|
||||
}
|
||||
|
||||
override fun visitValueParameter(valueParameter: FirValueParameter, data: CheckerContext) {
|
||||
checkers.allBasicDeclarationCheckers.check(valueParameter, data, reporter)
|
||||
checkers.allAnnotatedDeclarationCheckers.check(valueParameter, data, reporter)
|
||||
}
|
||||
|
||||
override fun visitTypeParameter(typeParameter: FirTypeParameter, data: CheckerContext) {
|
||||
|
||||
+7
@@ -329,6 +329,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_ANNOTATION_PA
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_OVERRIDDEN_BY_VAL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_TYPE_MISMATCH_ON_OVERRIDE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_ANNOTATION_TARGET
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_GETTER_RETURN_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_INVOCATION_KIND
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_MODIFIER_TARGET
|
||||
@@ -478,6 +479,12 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
)
|
||||
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_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(
|
||||
|
||||
Reference in New Issue
Block a user