FIR: implement OPT_IN_MARKER_ON_WRONG_TARGET
This commit is contained in:
committed by
TeamCityServer
parent
8bb16d8e7a
commit
3045a5e920
+19
@@ -949,6 +949,25 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.OPT_IN_MARKER_ON_WRONG_TARGET) { firDiagnostic ->
|
||||
OptInMarkerOnWrongTargetImpl(
|
||||
firDiagnostic.a,
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.OPT_IN_MARKER_ON_OVERRIDE) { firDiagnostic ->
|
||||
OptInMarkerOnOverrideImpl(
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.OPT_IN_MARKER_ON_OVERRIDE_WARNING) { firDiagnostic ->
|
||||
OptInMarkerOnOverrideWarningImpl(
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXPOSED_TYPEALIAS_EXPANDED_TYPE) { firDiagnostic ->
|
||||
ExposedTypealiasExpandedTypeImpl(
|
||||
firDiagnostic.a,
|
||||
|
||||
+13
@@ -682,6 +682,19 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = OptInMarkerWithWrongRetention::class
|
||||
}
|
||||
|
||||
abstract class OptInMarkerOnWrongTarget : KtFirDiagnostic<KtAnnotationEntry>() {
|
||||
override val diagnosticClass get() = OptInMarkerOnWrongTarget::class
|
||||
abstract val target: String
|
||||
}
|
||||
|
||||
abstract class OptInMarkerOnOverride : KtFirDiagnostic<KtAnnotationEntry>() {
|
||||
override val diagnosticClass get() = OptInMarkerOnOverride::class
|
||||
}
|
||||
|
||||
abstract class OptInMarkerOnOverrideWarning : KtFirDiagnostic<KtAnnotationEntry>() {
|
||||
override val diagnosticClass get() = OptInMarkerOnOverrideWarning::class
|
||||
}
|
||||
|
||||
abstract class ExposedTypealiasExpandedType : KtFirDiagnostic<KtNamedDeclaration>() {
|
||||
override val diagnosticClass get() = ExposedTypealiasExpandedType::class
|
||||
abstract val elementVisibility: EffectiveVisibility
|
||||
|
||||
+16
@@ -819,6 +819,22 @@ internal class OptInMarkerWithWrongRetentionImpl(
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.OptInMarkerWithWrongRetention(), KtAbstractFirDiagnostic<KtAnnotationEntry>
|
||||
|
||||
internal class OptInMarkerOnWrongTargetImpl(
|
||||
override val target: String,
|
||||
override val firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.OptInMarkerOnWrongTarget(), KtAbstractFirDiagnostic<KtAnnotationEntry>
|
||||
|
||||
internal class OptInMarkerOnOverrideImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.OptInMarkerOnOverride(), KtAbstractFirDiagnostic<KtAnnotationEntry>
|
||||
|
||||
internal class OptInMarkerOnOverrideWarningImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.OptInMarkerOnOverrideWarning(), KtAbstractFirDiagnostic<KtAnnotationEntry>
|
||||
|
||||
internal class ExposedTypealiasExpandedTypeImpl(
|
||||
override val elementVisibility: EffectiveVisibility,
|
||||
override val restrictingDeclaration: KtSymbol,
|
||||
|
||||
+8
@@ -288,9 +288,11 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
parameter<FqName>("optInMarkerFqName")
|
||||
parameter<String>("message")
|
||||
}
|
||||
|
||||
val OPT_IN_IS_NOT_ENABLED by warning<KtAnnotationEntry>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION by error<PsiElement>()
|
||||
val OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN by error<PsiElement>()
|
||||
|
||||
val OPT_IN_WITHOUT_ARGUMENTS by warning<KtAnnotationEntry>()
|
||||
val OPT_IN_ARGUMENT_IS_NOT_MARKER by warning<KtAnnotationEntry> {
|
||||
parameter<FqName>("notMarkerFqName")
|
||||
@@ -299,6 +301,12 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
parameter<String>("target")
|
||||
}
|
||||
val OPT_IN_MARKER_WITH_WRONG_RETENTION by error<KtAnnotationEntry>()
|
||||
|
||||
val OPT_IN_MARKER_ON_WRONG_TARGET by error<KtAnnotationEntry> {
|
||||
parameter<String>("target")
|
||||
}
|
||||
val OPT_IN_MARKER_ON_OVERRIDE by error<KtAnnotationEntry>()
|
||||
val OPT_IN_MARKER_ON_OVERRIDE_WARNING by warning<KtAnnotationEntry>()
|
||||
}
|
||||
|
||||
val EXPOSED_VISIBILITY by object : DiagnosticGroup("Exposed visibility") {
|
||||
|
||||
@@ -251,6 +251,9 @@ object FirErrors {
|
||||
val OPT_IN_ARGUMENT_IS_NOT_MARKER by warning1<KtAnnotationEntry, FqName>()
|
||||
val OPT_IN_MARKER_WITH_WRONG_TARGET by error1<KtAnnotationEntry, String>()
|
||||
val OPT_IN_MARKER_WITH_WRONG_RETENTION by error0<KtAnnotationEntry>()
|
||||
val OPT_IN_MARKER_ON_WRONG_TARGET by error1<KtAnnotationEntry, String>()
|
||||
val OPT_IN_MARKER_ON_OVERRIDE by error0<KtAnnotationEntry>()
|
||||
val OPT_IN_MARKER_ON_OVERRIDE_WARNING by warning0<KtAnnotationEntry>()
|
||||
|
||||
// Exposed visibility
|
||||
val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3<KtNamedDeclaration, EffectiveVisibility, FirBasedSymbol<*>, EffectiveVisibility>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
|
||||
+1
@@ -140,6 +140,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
get() = setOf(
|
||||
FirAnnotationChecker,
|
||||
FirPublishedApiChecker,
|
||||
FirOptInMarkedDeclarationChecker,
|
||||
)
|
||||
|
||||
override val typeAliasCheckers: Set<FirTypeAliasChecker>
|
||||
|
||||
+11
-13
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
@@ -26,7 +25,6 @@ import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
@@ -34,20 +32,11 @@ import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.ParameterNames
|
||||
import org.jetbrains.kotlin.resolve.UseSiteTargetsList
|
||||
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
fun FirAnnotation.getRetention(session: FirSession): AnnotationRetention {
|
||||
val annotationClassSymbol =
|
||||
(this.annotationTypeRef.coneType as? ConeClassLikeType)?.lookupTag?.toSymbol(session) as? FirRegularClassSymbol
|
||||
?: return AnnotationRetention.RUNTIME
|
||||
annotationClassSymbol.ensureResolved(FirResolvePhase.BODY_RESOLVE)
|
||||
val annotationClass = annotationClassSymbol.fir
|
||||
return annotationClass.getRetention()
|
||||
}
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
|
||||
fun FirRegularClass.getRetention(): AnnotationRetention {
|
||||
return getRetentionAnnotation()?.getRetention() ?: AnnotationRetention.RUNTIME
|
||||
@@ -71,6 +60,15 @@ fun FirAnnotation.getAllowedAnnotationTargets(session: FirSession): Set<KotlinTa
|
||||
return annotationClassSymbol.getAllowedAnnotationTargets()
|
||||
}
|
||||
|
||||
internal fun FirAnnotation.getAnnotationClassForOptInMarker(session: FirSession): FirRegularClassSymbol? {
|
||||
val lookupTag = annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.lookupTag ?: return null
|
||||
val annotationClassSymbol = lookupTag.toSymbol(session) as? FirRegularClassSymbol ?: return null
|
||||
if (annotationClassSymbol.getAnnotationByClassId(OptInNames.REQUIRES_OPT_IN_CLASS_ID) == null) {
|
||||
return null
|
||||
}
|
||||
return annotationClassSymbol
|
||||
}
|
||||
|
||||
fun FirRegularClass.getAllowedAnnotationTargets(): Set<KotlinTarget> {
|
||||
return symbol.getAllowedAnnotationTargets()
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getAllowedAnnotationTargets
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationClassForOptInMarker
|
||||
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.analysis.diagnostics.withSuppressedDiagnostics
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnnotatedDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
|
||||
object FirOptInMarkedDeclarationChecker : FirAnnotatedDeclarationChecker() {
|
||||
override fun check(declaration: FirAnnotatedDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
for (annotation in declaration.annotations) {
|
||||
val annotationClass = annotation.getAnnotationClassForOptInMarker(context.session) ?: continue
|
||||
withSuppressedDiagnostics(annotation, context) {
|
||||
val useSiteTarget = annotation.useSiteTarget
|
||||
if ((declaration is FirPropertyAccessor && declaration.isGetter) || useSiteTarget == PROPERTY_GETTER) {
|
||||
reporter.reportOn(annotation.source, FirErrors.OPT_IN_MARKER_ON_WRONG_TARGET, "getter", context)
|
||||
}
|
||||
if (useSiteTarget == SETTER_PARAMETER ||
|
||||
declaration is FirValueParameter && KotlinTarget.VALUE_PARAMETER in annotationClass.getAllowedAnnotationTargets()
|
||||
) {
|
||||
reporter.reportOn(annotation.source, FirErrors.OPT_IN_MARKER_ON_WRONG_TARGET, "parameter", context)
|
||||
}
|
||||
if (declaration is FirProperty && declaration.isLocal) {
|
||||
reporter.reportOn(annotation.source, FirErrors.OPT_IN_MARKER_ON_WRONG_TARGET, "variable", context)
|
||||
}
|
||||
if (useSiteTarget == FIELD || useSiteTarget == PROPERTY_DELEGATE_FIELD) {
|
||||
reporter.reportOn(annotation.source, FirErrors.OPT_IN_MARKER_ON_WRONG_TARGET, "field", context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -5,13 +5,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.type
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.fir.FirRealSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationClassForOptInMarker
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
@@ -29,7 +33,19 @@ object FirOptInUsageTypeRefChecker : FirTypeRefChecker() {
|
||||
override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val source = typeRef.source
|
||||
if (source?.kind !is FirRealSourceElementKind) return
|
||||
// coneTypeSafe filters out all delegatedTypeRefs from here
|
||||
val coneType = typeRef.coneTypeSafe<ConeClassLikeType>() ?: return
|
||||
|
||||
for (annotation in typeRef.annotations) {
|
||||
if (annotation.getAnnotationClassForOptInMarker(context.session) != null) {
|
||||
if (annotation.useSiteTarget == AnnotationUseSiteTarget.RECEIVER) {
|
||||
withSuppressedDiagnostics(annotation, context) {
|
||||
reporter.reportOn(annotation.source, FirErrors.OPT_IN_MARKER_ON_WRONG_TARGET, "parameter", context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val symbol = coneType.lookupTag.toSymbol(context.session) ?: return
|
||||
symbol.ensureResolved(FirResolvePhase.STATUS)
|
||||
val classId = symbol.classId
|
||||
|
||||
+12
-1
@@ -476,6 +476,9 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_ELVIS_RIG
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_IS_CHECK
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_VARARG_ON_PARAMETER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_ARGUMENT_IS_NOT_MARKER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_MARKER_ON_OVERRIDE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_MARKER_ON_OVERRIDE_WARNING
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_MARKER_ON_WRONG_TARGET
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OPT_IN_WITHOUT_ARGUMENTS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CANNOT_BE_CLONEABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION
|
||||
@@ -745,7 +748,15 @@ class FirDefaultErrorMessages {
|
||||
OPT_IN_MARKER_WITH_WRONG_RETENTION,
|
||||
"Opt-in requirement marker annotation cannot be used with SOURCE retention. Please replace retention with BINARY"
|
||||
)
|
||||
|
||||
map.put(OPT_IN_MARKER_ON_WRONG_TARGET, "Opt-in requirement marker annotation cannot be used on {0}", STRING)
|
||||
map.put(
|
||||
OPT_IN_MARKER_ON_OVERRIDE,
|
||||
"Opt-in requirement marker annotation on override requires the same marker on base declaration"
|
||||
)
|
||||
map.put(
|
||||
OPT_IN_MARKER_ON_OVERRIDE_WARNING,
|
||||
"Opt-in requirement marker annotation on override makes no sense without the same marker on base declaration"
|
||||
)
|
||||
|
||||
// Exposed visibility group // #
|
||||
map.put(
|
||||
|
||||
+8
-8
@@ -47,12 +47,12 @@ annotation class E5
|
||||
annotation class E6
|
||||
|
||||
var some: Int
|
||||
@E4
|
||||
<!OPT_IN_MARKER_ON_WRONG_TARGET!>@E4<!>
|
||||
get() = 42
|
||||
@E5
|
||||
set(value) {}
|
||||
|
||||
@get:E4
|
||||
<!OPT_IN_MARKER_ON_WRONG_TARGET!>@get:E4<!>
|
||||
val another: Int = 42
|
||||
|
||||
class My {
|
||||
@@ -75,21 +75,21 @@ class Derived : Base {
|
||||
@E6
|
||||
override val bar: Int = 42
|
||||
|
||||
@set:E6 @setparam:E6
|
||||
@set:E6 <!OPT_IN_MARKER_ON_WRONG_TARGET!>@setparam:E6<!>
|
||||
override var baz: Int = 13
|
||||
|
||||
@E6
|
||||
override fun foo() {}
|
||||
|
||||
override fun @receiver:E6 String.withReceiver() {}
|
||||
override fun <!OPT_IN_MARKER_ON_WRONG_TARGET!>@receiver:E6<!> String.withReceiver() {}
|
||||
}
|
||||
|
||||
abstract class Another(@param:E6 val x: String) : Base {
|
||||
@delegate:E6
|
||||
abstract class Another(<!OPT_IN_MARKER_ON_WRONG_TARGET!>@param:E6<!> val x: String) : Base {
|
||||
<!OPT_IN_MARKER_ON_WRONG_TARGET!>@delegate:E6<!>
|
||||
override val bar: Int by lazy { 42 }
|
||||
|
||||
fun baz(@E6 param: Int) {
|
||||
@E6 val x = param
|
||||
fun baz(<!OPT_IN_MARKER_ON_WRONG_TARGET!>@E6<!> param: Int) {
|
||||
<!OPT_IN_MARKER_ON_WRONG_TARGET!>@E6<!> val x = param
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
-25
@@ -1,25 +0,0 @@
|
||||
// !OPT_IN: kotlin.RequiresOptIn
|
||||
|
||||
@RequiresOptIn
|
||||
annotation class SomeOptInMarker
|
||||
|
||||
@RequiresOptIn
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.PROPERTY, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.LOCAL_VARIABLE)
|
||||
annotation class OtherOptInMarker
|
||||
|
||||
class IntWrapper(
|
||||
@SomeOptInMarker
|
||||
@OtherOptInMarker
|
||||
val value: Int
|
||||
) {
|
||||
val isEven: Boolean
|
||||
@SomeOptInMarker
|
||||
@OtherOptInMarker
|
||||
get() = (value % 2) == 0
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
@SomeOptInMarker
|
||||
@OtherOptInMarker
|
||||
val value = 2
|
||||
}
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !OPT_IN: kotlin.RequiresOptIn
|
||||
|
||||
@RequiresOptIn
|
||||
|
||||
Reference in New Issue
Block a user