[K2] Disappeared OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN (3)
Added checking usages of markers as qualifiers in imports. ^KT-59998
This commit is contained in:
committed by
Space Team
parent
97f09acc92
commit
72d69e16c4
+1
@@ -151,6 +151,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
override val fileCheckers: Set<FirFileChecker>
|
||||
get() = setOf(
|
||||
FirImportsChecker,
|
||||
FirOptInImportsChecker,
|
||||
FirUnresolvedInMiddleOfImportChecker,
|
||||
FirTopLevelPropertiesChecker,
|
||||
)
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.KtSourceElement
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker.isExperimentalMarker
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.getContainingClassLookupTag
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
|
||||
object FirOptInImportsChecker : FirFileChecker() {
|
||||
override fun check(declaration: FirFile, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
declaration.imports.forEach { import ->
|
||||
if (import !is FirResolvedImport) return@forEach
|
||||
val source = import.source ?: return@forEach
|
||||
// 'import package.Class.Marker' is OK, but 'import package.Class.Marker.NestedClass' is not.
|
||||
val resolvedParentClassId = import.resolvedParentClassId ?: return@forEach
|
||||
val parentClassSymbol = context.session.symbolProvider.getClassLikeSymbolByClassId(resolvedParentClassId) ?: return@forEach
|
||||
|
||||
when (parentClassSymbol) {
|
||||
is FirRegularClassSymbol -> parentClassSymbol.checkContainingClasses(source, context, reporter)
|
||||
is FirTypeAliasSymbol -> {
|
||||
val expandedClassSymbol = parentClassSymbol.fullyExpandedClass(context.session) ?: return@forEach
|
||||
expandedClassSymbol.checkContainingClasses(source, context, reporter)
|
||||
}
|
||||
else -> return@forEach
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private tailrec fun FirClassLikeSymbol<*>.checkContainingClasses(
|
||||
source: KtSourceElement,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter,
|
||||
) {
|
||||
if (isExperimentalMarker(context.session)) {
|
||||
reporter.reportOn(source, FirErrors.OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN, context)
|
||||
}
|
||||
val containingClassSymbol = this.getContainingClassLookupTag()?.toSymbol(context.session) ?: return
|
||||
containingClassSymbol.checkContainingClasses(source, context, reporter)
|
||||
}
|
||||
|
||||
}
|
||||
+4
@@ -150,6 +150,10 @@ object FirOptInUsageBaseChecker {
|
||||
fromSetter = false, dispatchReceiverType = null, fromSupertype = true
|
||||
)
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
fun FirClassLikeSymbol<*>.isExperimentalMarker(session: FirSession) =
|
||||
this is FirRegularClassSymbol && fir.getAnnotationByClassId(OptInNames.REQUIRES_OPT_IN_CLASS_ID, session) != null
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun FirBasedSymbol<*>.loadExperimentalities(
|
||||
context: CheckerContext,
|
||||
|
||||
+4
-11
@@ -14,32 +14,28 @@ 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.expression.FirOptInUsageBaseChecker.loadExperimentalities
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker.loadExperimentalitiesFromSupertype
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker.isExperimentalMarker
|
||||
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.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.getContainingClassLookupTag
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
object FirOptInUsageTypeRefChecker : FirTypeRefChecker() {
|
||||
override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val source = typeRef.source
|
||||
val delegatedTypeRef = (typeRef as? FirResolvedTypeRef)?.delegatedTypeRef
|
||||
if (source?.kind !is KtRealSourceElementKind) return
|
||||
val coneType = typeRef.coneTypeSafe<ConeClassLikeType>() ?: return
|
||||
val symbol = typeRef.findSymbol(context.session) ?: return
|
||||
|
||||
val typeAliasExpandedSymbol = (symbol as? FirTypeAliasSymbol)?.resolvedExpandedTypeRef?.findSymbol(context.session)
|
||||
val processedSymbol = typeAliasExpandedSymbol ?: symbol
|
||||
val resolvedTypeRef = typeRef as? FirResolvedTypeRef
|
||||
val qualifier = (resolvedTypeRef?.delegatedTypeRef as? FirUserTypeRef)?.qualifier
|
||||
|
||||
val classId = processedSymbol.classId
|
||||
val lastAnnotationCall = context.callsOrAssignments.lastOrNull() as? FirAnnotation
|
||||
@@ -49,8 +45,8 @@ object FirOptInUsageTypeRefChecker : FirTypeRefChecker() {
|
||||
reporter.reportOn(source, OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION, context)
|
||||
processedSymbol.isExperimentalMarker(context.session) ->
|
||||
reporter.reportOn(source, OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN, context)
|
||||
!qualifier.isNullOrEmpty() -> {
|
||||
processedSymbol.checkContainingClasses(source, qualifier, context, reporter)
|
||||
delegatedTypeRef is FirUserTypeRef && delegatedTypeRef.qualifier.isNotEmpty() -> {
|
||||
processedSymbol.checkContainingClasses(source, delegatedTypeRef.qualifier, context, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,9 +80,6 @@ object FirOptInUsageTypeRefChecker : FirTypeRefChecker() {
|
||||
return coneType.lookupTag.toSymbol(session)
|
||||
}
|
||||
|
||||
private fun FirClassLikeSymbol<*>.isExperimentalMarker(session: FirSession) =
|
||||
this is FirRegularClassSymbol && fir.getAnnotationByClassId(OptInNames.REQUIRES_OPT_IN_CLASS_ID, session) != null
|
||||
|
||||
private tailrec fun FirClassLikeSymbol<*>.checkContainingClasses(
|
||||
source: KtSourceElement,
|
||||
qualifier: List<FirQualifierPart>,
|
||||
|
||||
+14
-2
@@ -66,6 +66,8 @@ fun f8(): <!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>
|
||||
|
||||
typealias Marker0 = <!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>A.Marker<!>
|
||||
|
||||
typealias Marker1 = <!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker0<!>
|
||||
|
||||
fun f9(m: <!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker0<!>) {}
|
||||
|
||||
|
||||
@@ -83,7 +85,17 @@ fun f11(m: <!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!
|
||||
// Usages of markers in import statements should be OK, but not as qualifiers to import their nested classes
|
||||
|
||||
import test.A.Marker
|
||||
import test.A.Marker.NestedClass
|
||||
import test.A.Marker.Companion
|
||||
<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>import test.A.Marker.NestedClass<!>
|
||||
<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>import test.A.Marker.NestedClass.NestedClass2<!>
|
||||
<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>import test.A.Marker.Companion<!>
|
||||
<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>import test.A.Marker.Companion.value<!>
|
||||
|
||||
// FILE: usage-from-other-file-2.kt
|
||||
import test.Marker0
|
||||
<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>import test.Marker0.NestedClass<!>
|
||||
|
||||
// FILE: usage-from-other-file-3.kt
|
||||
import test.Marker1
|
||||
<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>import test.Marker1.NestedClass<!>
|
||||
|
||||
fun f12(m: <!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>test.A.Marker.NestedClass.NestedClass2<!>) {}
|
||||
|
||||
+12
@@ -66,6 +66,8 @@ fun f8(): test.A.<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_O
|
||||
|
||||
typealias Marker0 = A.<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker<!>
|
||||
|
||||
typealias Marker1 = <!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker0<!>
|
||||
|
||||
fun f9(m: <!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker0<!>) {}
|
||||
|
||||
|
||||
@@ -84,6 +86,16 @@ fun f11(m: A.<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_I
|
||||
|
||||
import test.A.Marker
|
||||
import test.A.<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker<!>.NestedClass
|
||||
import test.A.<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker<!>.NestedClass.NestedClass2
|
||||
import test.A.<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker<!>.Companion
|
||||
import test.A.<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker<!>.Companion.value
|
||||
|
||||
// FILE: usage-from-other-file-2.kt
|
||||
import test.Marker0
|
||||
import test.<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker0<!>.NestedClass
|
||||
|
||||
// FILE: usage-from-other-file-3.kt
|
||||
import test.Marker1
|
||||
import test.<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker1<!>.NestedClass
|
||||
|
||||
fun f12(m: test.A.<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>Marker<!>.NestedClass.NestedClass2) {}
|
||||
|
||||
+1
@@ -68,6 +68,7 @@ package test {
|
||||
}
|
||||
public typealias Experimental0 = kotlin.RequiresOptIn
|
||||
public typealias Marker0 = test.A.Marker
|
||||
public typealias Marker1 = test.Marker0
|
||||
public typealias OptIn0 = kotlin.OptIn
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user