[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>
|
override val fileCheckers: Set<FirFileChecker>
|
||||||
get() = setOf(
|
get() = setOf(
|
||||||
FirImportsChecker,
|
FirImportsChecker,
|
||||||
|
FirOptInImportsChecker,
|
||||||
FirUnresolvedInMiddleOfImportChecker,
|
FirUnresolvedInMiddleOfImportChecker,
|
||||||
FirTopLevelPropertiesChecker,
|
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
|
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)
|
@OptIn(SymbolInternals::class)
|
||||||
private fun FirBasedSymbol<*>.loadExperimentalities(
|
private fun FirBasedSymbol<*>.loadExperimentalities(
|
||||||
context: CheckerContext,
|
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
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker.loadExperimentalities
|
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.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_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.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.FirClass
|
||||||
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
|
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||||
import org.jetbrains.kotlin.fir.getContainingClassLookupTag
|
import org.jetbrains.kotlin.fir.getContainingClassLookupTag
|
||||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
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.FirClassLikeSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||||
|
|
||||||
@OptIn(SymbolInternals::class)
|
|
||||||
object FirOptInUsageTypeRefChecker : FirTypeRefChecker() {
|
object FirOptInUsageTypeRefChecker : FirTypeRefChecker() {
|
||||||
override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) {
|
override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
val source = typeRef.source
|
val source = typeRef.source
|
||||||
|
val delegatedTypeRef = (typeRef as? FirResolvedTypeRef)?.delegatedTypeRef
|
||||||
if (source?.kind !is KtRealSourceElementKind) return
|
if (source?.kind !is KtRealSourceElementKind) return
|
||||||
val coneType = typeRef.coneTypeSafe<ConeClassLikeType>() ?: return
|
val coneType = typeRef.coneTypeSafe<ConeClassLikeType>() ?: return
|
||||||
val symbol = typeRef.findSymbol(context.session) ?: return
|
val symbol = typeRef.findSymbol(context.session) ?: return
|
||||||
|
|
||||||
val typeAliasExpandedSymbol = (symbol as? FirTypeAliasSymbol)?.resolvedExpandedTypeRef?.findSymbol(context.session)
|
val typeAliasExpandedSymbol = (symbol as? FirTypeAliasSymbol)?.resolvedExpandedTypeRef?.findSymbol(context.session)
|
||||||
val processedSymbol = typeAliasExpandedSymbol ?: symbol
|
val processedSymbol = typeAliasExpandedSymbol ?: symbol
|
||||||
val resolvedTypeRef = typeRef as? FirResolvedTypeRef
|
|
||||||
val qualifier = (resolvedTypeRef?.delegatedTypeRef as? FirUserTypeRef)?.qualifier
|
|
||||||
|
|
||||||
val classId = processedSymbol.classId
|
val classId = processedSymbol.classId
|
||||||
val lastAnnotationCall = context.callsOrAssignments.lastOrNull() as? FirAnnotation
|
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)
|
reporter.reportOn(source, OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION, context)
|
||||||
processedSymbol.isExperimentalMarker(context.session) ->
|
processedSymbol.isExperimentalMarker(context.session) ->
|
||||||
reporter.reportOn(source, OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN, context)
|
reporter.reportOn(source, OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN, context)
|
||||||
!qualifier.isNullOrEmpty() -> {
|
delegatedTypeRef is FirUserTypeRef && delegatedTypeRef.qualifier.isNotEmpty() -> {
|
||||||
processedSymbol.checkContainingClasses(source, qualifier, context, reporter)
|
processedSymbol.checkContainingClasses(source, delegatedTypeRef.qualifier, context, reporter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,9 +80,6 @@ object FirOptInUsageTypeRefChecker : FirTypeRefChecker() {
|
|||||||
return coneType.lookupTag.toSymbol(session)
|
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(
|
private tailrec fun FirClassLikeSymbol<*>.checkContainingClasses(
|
||||||
source: KtSourceElement,
|
source: KtSourceElement,
|
||||||
qualifier: List<FirQualifierPart>,
|
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 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<!>) {}
|
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
|
// 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
|
||||||
import test.A.Marker.NestedClass
|
<!OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN!>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.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<!>) {}
|
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 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<!>) {}
|
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.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
|
||||||
|
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
|
||||||
|
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) {}
|
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 Experimental0 = kotlin.RequiresOptIn
|
||||||
public typealias Marker0 = test.A.Marker
|
public typealias Marker0 = test.A.Marker
|
||||||
|
public typealias Marker1 = test.Marker0
|
||||||
public typealias OptIn0 = kotlin.OptIn
|
public typealias OptIn0 = kotlin.OptIn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user