FIR: report OPT_IN_OVERRIDE(_ERROR) diagnostics
This commit is contained in:
committed by
TeamCityServer
parent
4052befe88
commit
ac3b738d9b
+2
-2
@@ -280,11 +280,11 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
|||||||
parameter<FqName>("optInMarkerFqName")
|
parameter<FqName>("optInMarkerFqName")
|
||||||
parameter<String>("message")
|
parameter<String>("message")
|
||||||
}
|
}
|
||||||
val OPT_IN_OVERRIDE by warning<PsiElement> {
|
val OPT_IN_OVERRIDE by warning<PsiElement>(PositioningStrategy.DECLARATION_NAME) {
|
||||||
parameter<FqName>("optInMarkerFqName")
|
parameter<FqName>("optInMarkerFqName")
|
||||||
parameter<String>("message")
|
parameter<String>("message")
|
||||||
}
|
}
|
||||||
val OPT_IN_OVERRIDE_ERROR by error<PsiElement> {
|
val OPT_IN_OVERRIDE_ERROR by error<PsiElement>(PositioningStrategy.DECLARATION_NAME) {
|
||||||
parameter<FqName>("optInMarkerFqName")
|
parameter<FqName>("optInMarkerFqName")
|
||||||
parameter<String>("message")
|
parameter<String>("message")
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -242,8 +242,8 @@ object FirErrors {
|
|||||||
// OptIn
|
// OptIn
|
||||||
val OPT_IN_USAGE by warning2<PsiElement, FqName, String>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
val OPT_IN_USAGE by warning2<PsiElement, FqName, String>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||||
val OPT_IN_USAGE_ERROR by error2<PsiElement, FqName, String>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
val OPT_IN_USAGE_ERROR by error2<PsiElement, FqName, String>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||||
val OPT_IN_OVERRIDE by warning2<PsiElement, FqName, String>()
|
val OPT_IN_OVERRIDE by warning2<PsiElement, FqName, String>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||||
val OPT_IN_OVERRIDE_ERROR by error2<PsiElement, FqName, String>()
|
val OPT_IN_OVERRIDE_ERROR by error2<PsiElement, FqName, String>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||||
val OPT_IN_IS_NOT_ENABLED by warning0<KtAnnotationEntry>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
val OPT_IN_IS_NOT_ENABLED by warning0<KtAnnotationEntry>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||||
val OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION by error0<PsiElement>()
|
val OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION by error0<PsiElement>()
|
||||||
val OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN by error0<PsiElement>()
|
val OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN by error0<PsiElement>()
|
||||||
|
|||||||
+27
-1
@@ -10,10 +10,13 @@ import org.jetbrains.kotlin.descriptors.Visibilities
|
|||||||
import org.jetbrains.kotlin.fir.*
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker.Experimentality
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics
|
||||||
import org.jetbrains.kotlin.fir.analysis.overridesBackwardCompatibilityHelper
|
import org.jetbrains.kotlin.fir.analysis.overridesBackwardCompatibilityHelper
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
|
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
|
||||||
@@ -47,7 +50,10 @@ object FirOverrideChecker : FirClassChecker() {
|
|||||||
|
|
||||||
for (it in declaration.declarations) {
|
for (it in declaration.declarations) {
|
||||||
if (it is FirSimpleFunction || it is FirProperty) {
|
if (it is FirSimpleFunction || it is FirProperty) {
|
||||||
checkMember((it as FirCallableDeclaration).symbol, declaration, reporter, typeCheckerState, firTypeScope, context)
|
val callable = it as FirCallableDeclaration
|
||||||
|
withSuppressedDiagnostics(callable, context) {
|
||||||
|
checkMember(callable.symbol, declaration, reporter, typeCheckerState, firTypeScope, context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,6 +277,8 @@ object FirOverrideChecker : FirClassChecker() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkOverriddenExperimentalities(member, overriddenMemberSymbols, context, reporter)
|
||||||
|
|
||||||
checkModality(overriddenMemberSymbols)?.let {
|
checkModality(overriddenMemberSymbols)?.let {
|
||||||
reporter.reportOverridingFinalMember(member, it, context)
|
reporter.reportOverridingFinalMember(member, it, context)
|
||||||
}
|
}
|
||||||
@@ -302,6 +310,24 @@ object FirOverrideChecker : FirClassChecker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(SymbolInternals::class)
|
||||||
|
private fun checkOverriddenExperimentalities(
|
||||||
|
memberSymbol: FirCallableSymbol<*>,
|
||||||
|
overriddenMemberSymbols: List<FirCallableSymbol<*>>,
|
||||||
|
context: CheckerContext,
|
||||||
|
reporter: DiagnosticReporter
|
||||||
|
) {
|
||||||
|
with(FirOptInUsageBaseChecker) {
|
||||||
|
val experimentalities = mutableSetOf<Experimentality>()
|
||||||
|
for (overriddenMemberSymbol in overriddenMemberSymbols) {
|
||||||
|
overriddenMemberSymbol.loadExperimentalitiesFromAnnotationTo(context.session, experimentalities)
|
||||||
|
}
|
||||||
|
reportNotAcceptedOverrideExperimentalities(
|
||||||
|
experimentalities, memberSymbol, context, reporter
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun DiagnosticReporter.reportNothingToOverride(declaration: FirCallableSymbol<*>, context: CheckerContext) {
|
private fun DiagnosticReporter.reportNothingToOverride(declaration: FirCallableSymbol<*>, context: CheckerContext) {
|
||||||
reportOn(declaration.source, FirErrors.NOTHING_TO_OVERRIDE, declaration, context)
|
reportOn(declaration.source, FirErrors.NOTHING_TO_OVERRIDE, declaration, context)
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-17
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.fir.types.*
|
|||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||||
@@ -54,6 +55,29 @@ object FirOptInUsageBaseChecker {
|
|||||||
return fir.loadExperimentalityForMarkerAnnotation()
|
return fir.loadExperimentalityForMarkerAnnotation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun FirBasedSymbol<*>.loadExperimentalitiesFromAnnotationTo(session: FirSession, result: MutableCollection<Experimentality>) {
|
||||||
|
ensureResolved(FirResolvePhase.STATUS)
|
||||||
|
@OptIn(SymbolInternals::class)
|
||||||
|
(fir as? FirAnnotatedDeclaration)?.loadExperimentalitiesFromAnnotationTo(session, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirAnnotatedDeclaration.loadExperimentalitiesFromAnnotationTo(
|
||||||
|
session: FirSession,
|
||||||
|
result: MutableCollection<Experimentality>,
|
||||||
|
fromSetter: Boolean = false
|
||||||
|
) {
|
||||||
|
for (annotation in annotations) {
|
||||||
|
val annotationType = annotation.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()
|
||||||
|
if (annotation.useSiteTarget != AnnotationUseSiteTarget.PROPERTY_SETTER || fromSetter) {
|
||||||
|
result.addIfNotNull(
|
||||||
|
annotationType?.lookupTag?.toFirRegularClassSymbol(
|
||||||
|
session
|
||||||
|
)?.loadExperimentalityForMarkerAnnotation()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun loadExperimentalitiesFromTypeArguments(
|
fun loadExperimentalitiesFromTypeArguments(
|
||||||
context: CheckerContext,
|
context: CheckerContext,
|
||||||
typeArguments: List<FirTypeProjection>
|
typeArguments: List<FirTypeProjection>
|
||||||
@@ -133,16 +157,7 @@ object FirOptInUsageBaseChecker {
|
|||||||
parentClassSymbol?.loadExperimentalities(context, result, visited, fromSetter = false, dispatchReceiverType = null)
|
parentClassSymbol?.loadExperimentalities(context, result, visited, fromSetter = false, dispatchReceiverType = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (annotation in fir.annotations) {
|
fir.loadExperimentalitiesFromAnnotationTo(session, result, fromSetter)
|
||||||
val annotationType = annotation.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()
|
|
||||||
if (annotation.useSiteTarget != AnnotationUseSiteTarget.PROPERTY_SETTER || fromSetter) {
|
|
||||||
result.addIfNotNull(
|
|
||||||
annotationType?.lookupTag?.toFirRegularClassSymbol(
|
|
||||||
session
|
|
||||||
)?.loadExperimentalityForMarkerAnnotation()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fir is FirTypeAlias) {
|
if (fir is FirTypeAlias) {
|
||||||
fir.expandedTypeRef.coneType.addExperimentalities(context, result, visited)
|
fir.expandedTypeRef.coneType.addExperimentalities(context, result, visited)
|
||||||
@@ -195,19 +210,39 @@ object FirOptInUsageBaseChecker {
|
|||||||
) {
|
) {
|
||||||
for ((annotationClassId, severity, message) in experimentalities) {
|
for ((annotationClassId, severity, message) in experimentalities) {
|
||||||
if (!isExperimentalityAcceptableInContext(annotationClassId, context)) {
|
if (!isExperimentalityAcceptableInContext(annotationClassId, context)) {
|
||||||
val diagnostic = when (severity) {
|
val (diagnostic, verb) = when (severity) {
|
||||||
Experimentality.Severity.WARNING -> FirErrors.OPT_IN_USAGE
|
Experimentality.Severity.WARNING -> FirErrors.OPT_IN_USAGE to "should"
|
||||||
Experimentality.Severity.ERROR -> FirErrors.OPT_IN_USAGE_ERROR
|
Experimentality.Severity.ERROR -> FirErrors.OPT_IN_USAGE_ERROR to "must"
|
||||||
}
|
|
||||||
val reportedMessage = message ?: when (severity) {
|
|
||||||
Experimentality.Severity.WARNING -> "This declaration is experimental and its usage should be marked"
|
|
||||||
Experimentality.Severity.ERROR -> "This declaration is experimental and its usage must be marked"
|
|
||||||
}
|
}
|
||||||
|
val reportedMessage = message ?: "This declaration is experimental and its usage $verb be marked"
|
||||||
reporter.reportOn(element.source, diagnostic, annotationClassId.asSingleFqName(), reportedMessage, context)
|
reporter.reportOn(element.source, diagnostic, annotationClassId.asSingleFqName(), reportedMessage, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SymbolInternals
|
||||||
|
fun reportNotAcceptedOverrideExperimentalities(
|
||||||
|
experimentalities: Collection<Experimentality>,
|
||||||
|
symbol: FirCallableSymbol<*>,
|
||||||
|
context: CheckerContext,
|
||||||
|
reporter: DiagnosticReporter
|
||||||
|
) {
|
||||||
|
for ((annotationClassId, severity, _) in experimentalities) {
|
||||||
|
if (!symbol.fir.isExperimentalityAcceptable(annotationClassId) &&
|
||||||
|
!isExperimentalityAcceptableInContext(annotationClassId, context)
|
||||||
|
) {
|
||||||
|
val (diagnostic, verb) = when (severity) {
|
||||||
|
Experimentality.Severity.WARNING -> FirErrors.OPT_IN_OVERRIDE to "should"
|
||||||
|
Experimentality.Severity.ERROR -> FirErrors.OPT_IN_OVERRIDE_ERROR to "must"
|
||||||
|
}
|
||||||
|
val reportedMessage = "This declaration overrides experimental member of supertype " +
|
||||||
|
"'${symbol.callableId.className?.shortName()?.asString()}' and $verb be annotated " +
|
||||||
|
"with '@${annotationClassId.asFqNameString()}'"
|
||||||
|
reporter.reportOn(symbol.source, diagnostic, annotationClassId.asSingleFqName(), reportedMessage, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun isExperimentalityAcceptableInContext(
|
private fun isExperimentalityAcceptableInContext(
|
||||||
annotationClassId: ClassId,
|
annotationClassId: ClassId,
|
||||||
context: CheckerContext
|
context: CheckerContext
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
// !OPT_IN: kotlin.RequiresOptIn
|
|
||||||
// FILE: api.kt
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
@RequiresOptIn
|
|
||||||
@Retention(AnnotationRetention.BINARY)
|
|
||||||
annotation class E
|
|
||||||
|
|
||||||
open class Base {
|
|
||||||
@E
|
|
||||||
open fun foo() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FILE: usage.kt
|
|
||||||
|
|
||||||
package usage
|
|
||||||
|
|
||||||
import api.*
|
|
||||||
|
|
||||||
class Derived : Base() {
|
|
||||||
override fun foo() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun test(b: Base) {
|
|
||||||
b.<!OPT_IN_USAGE_ERROR!>foo<!>()
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !OPT_IN: kotlin.RequiresOptIn
|
// !OPT_IN: kotlin.RequiresOptIn
|
||||||
// FILE: api.kt
|
// FILE: api.kt
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
// !OPT_IN: kotlin.RequiresOptIn
|
|
||||||
// FILE: api.kt
|
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
|
||||||
@Retention(AnnotationRetention.BINARY)
|
|
||||||
annotation class E
|
|
||||||
|
|
||||||
open class Base {
|
|
||||||
@E
|
|
||||||
open fun foo() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DerivedInSameModule : Base() {
|
|
||||||
override fun foo() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FILE: usage-propagate.kt
|
|
||||||
|
|
||||||
package usage1
|
|
||||||
|
|
||||||
import api.*
|
|
||||||
|
|
||||||
open class Derived : Base() {
|
|
||||||
@E
|
|
||||||
override fun foo() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SubDerived : Derived()
|
|
||||||
|
|
||||||
@E
|
|
||||||
class Derived2 : Base() {
|
|
||||||
override fun foo() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FILE: usage-none.kt
|
|
||||||
|
|
||||||
package usage2
|
|
||||||
|
|
||||||
import api.*
|
|
||||||
|
|
||||||
class Derived : Base() {
|
|
||||||
override fun foo() {}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !OPT_IN: kotlin.RequiresOptIn
|
// !OPT_IN: kotlin.RequiresOptIn
|
||||||
// FILE: api.kt
|
// FILE: api.kt
|
||||||
|
|
||||||
|
|||||||
compiler/testData/diagnostics/testsWithStdLib/experimental/overrideDifferentExperimentalities.fir.kt
Vendored
-38
@@ -1,38 +0,0 @@
|
|||||||
// !OPT_IN: kotlin.RequiresOptIn
|
|
||||||
|
|
||||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
|
||||||
@Retention(AnnotationRetention.BINARY)
|
|
||||||
annotation class E1
|
|
||||||
|
|
||||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
|
||||||
@Retention(AnnotationRetention.BINARY)
|
|
||||||
annotation class E3
|
|
||||||
|
|
||||||
interface Base1 {
|
|
||||||
@E1
|
|
||||||
fun foo()
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Base2 {
|
|
||||||
fun foo()
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Base3 {
|
|
||||||
@E3
|
|
||||||
fun foo()
|
|
||||||
}
|
|
||||||
|
|
||||||
class DerivedA : Base1, Base2, Base3 {
|
|
||||||
override fun foo() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DerivedB : Base1, Base3 {
|
|
||||||
@E3
|
|
||||||
override fun foo() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DerivedC : Base1, Base2, Base3 {
|
|
||||||
@E1
|
|
||||||
@E3
|
|
||||||
override fun foo() {}
|
|
||||||
}
|
|
||||||
Vendored
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !OPT_IN: kotlin.RequiresOptIn
|
// !OPT_IN: kotlin.RequiresOptIn
|
||||||
|
|
||||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||||
|
|||||||
Reference in New Issue
Block a user