K2 opt-in: fix reporting of WasExperimental-bound errors
This commit is contained in:
committed by
Space Team
parent
f62bdb0e3d
commit
3e22f6c052
+11
-5
@@ -90,16 +90,22 @@ fun FirClassLikeSymbol<*>.getTargetAnnotation(): FirAnnotation? {
|
|||||||
return getAnnotationByClassId(StandardClassIds.Annotations.Target)
|
return getAnnotationByClassId(StandardClassIds.Annotations.Target)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FirExpression.extractClassesFromArgument(): List<FirRegularClassSymbol> {
|
fun FirExpression.extractClassesFromArgument(session: FirSession): List<FirRegularClassSymbol> {
|
||||||
return unfoldArrayOrVararg().mapNotNull {
|
return unfoldArrayOrVararg().mapNotNull {
|
||||||
it.extractClassFromArgument()
|
it.extractClassFromArgument(session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FirExpression.extractClassFromArgument(): FirRegularClassSymbol? {
|
fun FirExpression.extractClassFromArgument(session: FirSession): FirRegularClassSymbol? {
|
||||||
if (this !is FirGetClassCall) return null
|
if (this !is FirGetClassCall) return null
|
||||||
val qualifier = argument as? FirResolvedQualifier ?: return null
|
return when (val argument = argument) {
|
||||||
return qualifier.symbol as? FirRegularClassSymbol
|
is FirResolvedQualifier ->
|
||||||
|
argument.symbol as? FirRegularClassSymbol
|
||||||
|
is FirClassReferenceExpression ->
|
||||||
|
argument.classTypeRef.coneTypeSafe<ConeClassLikeType>()?.fullyExpandedType(session)?.toRegularClassSymbol(session)
|
||||||
|
else ->
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkRepeatedAnnotation(
|
fun checkRepeatedAnnotation(
|
||||||
|
|||||||
+3
-3
@@ -69,7 +69,7 @@ private fun FirDeclaration.getOwnSinceKotlinVersion(session: FirSession): FirSin
|
|||||||
if (apiVersion != null) {
|
if (apiVersion != null) {
|
||||||
// TODO: combine wasExperimentalMarkerClasses in case of several associated declarations with the same maximal API version
|
// TODO: combine wasExperimentalMarkerClasses in case of several associated declarations with the same maximal API version
|
||||||
if (result == null || apiVersion > result!!.apiVersion) {
|
if (result == null || apiVersion > result!!.apiVersion) {
|
||||||
result = FirSinceKotlinValue(apiVersion, loadWasExperimentalMarkerClasses())
|
result = FirSinceKotlinValue(apiVersion, loadWasExperimentalMarkerClasses(session))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,9 +96,9 @@ private fun FirDeclaration.getOwnSinceKotlinVersion(session: FirSession): FirSin
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FirDeclaration.loadWasExperimentalMarkerClasses(): List<FirRegularClassSymbol> {
|
private fun FirDeclaration.loadWasExperimentalMarkerClasses(session: FirSession): List<FirRegularClassSymbol> {
|
||||||
val wasExperimental = getAnnotationByClassId(OptInNames.WAS_EXPERIMENTAL_CLASS_ID) ?: return emptyList()
|
val wasExperimental = getAnnotationByClassId(OptInNames.WAS_EXPERIMENTAL_CLASS_ID) ?: return emptyList()
|
||||||
val annotationClasses = wasExperimental.findArgumentByName(OptInNames.WAS_EXPERIMENTAL_ANNOTATION_CLASS) ?: return emptyList()
|
val annotationClasses = wasExperimental.findArgumentByName(OptInNames.WAS_EXPERIMENTAL_ANNOTATION_CLASS) ?: return emptyList()
|
||||||
return annotationClasses.extractClassesFromArgument()
|
return annotationClasses.extractClassesFromArgument(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.fir.analysis.checkers.extractClassesFromArgument
|
|||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker.loadExperimentalityForMarkerAnnotation
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.extractClassFromArgument
|
import org.jetbrains.kotlin.fir.analysis.checkers.extractClassFromArgument
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.modality
|
import org.jetbrains.kotlin.fir.analysis.checkers.modality
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||||
@@ -46,7 +45,7 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
|
|||||||
reporter.reportOn(expression.source, FirErrors.OPT_IN_WITHOUT_ARGUMENTS, context)
|
reporter.reportOn(expression.source, FirErrors.OPT_IN_WITHOUT_ARGUMENTS, context)
|
||||||
} else {
|
} else {
|
||||||
val annotationClasses = expression.findArgumentByName(OPT_IN_ANNOTATION_CLASS)
|
val annotationClasses = expression.findArgumentByName(OPT_IN_ANNOTATION_CLASS)
|
||||||
for (classSymbol in annotationClasses?.extractClassesFromArgument().orEmpty()) {
|
for (classSymbol in annotationClasses?.extractClassesFromArgument(context.session).orEmpty()) {
|
||||||
checkOptInArgumentIsMarker(classSymbol, expression.source, reporter, context)
|
checkOptInArgumentIsMarker(classSymbol, expression.source, reporter, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +72,7 @@ object FirOptInAnnotationCallChecker : FirAnnotationCallChecker() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val classSymbol = expression.findArgumentByName(OPT_IN_ANNOTATION_CLASS)?.extractClassFromArgument() ?: return
|
val classSymbol = expression.findArgumentByName(OPT_IN_ANNOTATION_CLASS)?.extractClassFromArgument(context.session) ?: return
|
||||||
checkOptInArgumentIsMarker(classSymbol, expression.source, reporter, context)
|
checkOptInArgumentIsMarker(classSymbol, expression.source, reporter, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-10
@@ -95,7 +95,8 @@ object FirOptInUsageBaseChecker {
|
|||||||
if (annotationType.lookupTag.classId == OptInNames.SUBCLASS_OPT_IN_REQUIRED_CLASS_ID) {
|
if (annotationType.lookupTag.classId == OptInNames.SUBCLASS_OPT_IN_REQUIRED_CLASS_ID) {
|
||||||
val annotationClass = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue
|
val annotationClass = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue
|
||||||
result.addIfNotNull(
|
result.addIfNotNull(
|
||||||
annotationClass.extractClassFromArgument()?.loadExperimentalityForMarkerAnnotation()?.copy(fromSupertype = true)
|
annotationClass.extractClassFromArgument(session)
|
||||||
|
?.loadExperimentalityForMarkerAnnotation()?.copy(fromSupertype = true)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,7 +257,7 @@ object FirOptInUsageBaseChecker {
|
|||||||
reporter: DiagnosticReporter
|
reporter: DiagnosticReporter
|
||||||
) {
|
) {
|
||||||
for ((annotationClassId, severity, markerMessage, supertypeName) in experimentalities) {
|
for ((annotationClassId, severity, markerMessage, supertypeName) in experimentalities) {
|
||||||
if (!symbol.fir.isExperimentalityAcceptable(annotationClassId, fromSupertype = false) &&
|
if (!symbol.fir.isExperimentalityAcceptable(context.session, annotationClassId, fromSupertype = false) &&
|
||||||
!isExperimentalityAcceptableInContext(annotationClassId, context, fromSupertype = false)
|
!isExperimentalityAcceptableInContext(annotationClassId, context, fromSupertype = false)
|
||||||
) {
|
) {
|
||||||
val (diagnostic, verb) = when (severity) {
|
val (diagnostic, verb) = when (severity) {
|
||||||
@@ -285,40 +286,47 @@ object FirOptInUsageBaseChecker {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for (annotationContainer in context.annotationContainers) {
|
for (annotationContainer in context.annotationContainers) {
|
||||||
if (annotationContainer.isExperimentalityAcceptable(annotationClassId, fromSupertype)) {
|
if (annotationContainer.isExperimentalityAcceptable(context.session, annotationClassId, fromSupertype)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FirAnnotationContainer.isExperimentalityAcceptable(annotationClassId: ClassId, fromSupertype: Boolean): Boolean {
|
private fun FirAnnotationContainer.isExperimentalityAcceptable(
|
||||||
return getAnnotationByClassId(annotationClassId) != null || isAnnotatedWithOptIn(annotationClassId) ||
|
session: FirSession,
|
||||||
fromSupertype && isAnnotatedWithSubclassOptInRequired(annotationClassId)
|
annotationClassId: ClassId,
|
||||||
|
fromSupertype: Boolean
|
||||||
|
): Boolean {
|
||||||
|
return getAnnotationByClassId(annotationClassId) != null || isAnnotatedWithOptIn(session, annotationClassId) ||
|
||||||
|
fromSupertype && isAnnotatedWithSubclassOptInRequired(session, annotationClassId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FirAnnotationContainer.isAnnotatedWithOptIn(annotationClassId: ClassId): Boolean {
|
private fun FirAnnotationContainer.isAnnotatedWithOptIn(session: FirSession, annotationClassId: ClassId): Boolean {
|
||||||
for (annotation in annotations) {
|
for (annotation in annotations) {
|
||||||
val coneType = annotation.annotationTypeRef.coneType as? ConeClassLikeType
|
val coneType = annotation.annotationTypeRef.coneType as? ConeClassLikeType
|
||||||
if (coneType?.lookupTag?.classId != OptInNames.OPT_IN_CLASS_ID) {
|
if (coneType?.lookupTag?.classId != OptInNames.OPT_IN_CLASS_ID) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val annotationClasses = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue
|
val annotationClasses = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue
|
||||||
if (annotationClasses.extractClassesFromArgument().any { it.classId == annotationClassId }) {
|
if (annotationClasses.extractClassesFromArgument(session).any { it.classId == annotationClassId }) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FirAnnotationContainer.isAnnotatedWithSubclassOptInRequired(annotationClassId: ClassId): Boolean {
|
private fun FirAnnotationContainer.isAnnotatedWithSubclassOptInRequired(
|
||||||
|
session: FirSession,
|
||||||
|
annotationClassId: ClassId
|
||||||
|
): Boolean {
|
||||||
for (annotation in annotations) {
|
for (annotation in annotations) {
|
||||||
val coneType = annotation.annotationTypeRef.coneType as? ConeClassLikeType
|
val coneType = annotation.annotationTypeRef.coneType as? ConeClassLikeType
|
||||||
if (coneType?.lookupTag?.classId != OptInNames.SUBCLASS_OPT_IN_REQUIRED_CLASS_ID) {
|
if (coneType?.lookupTag?.classId != OptInNames.SUBCLASS_OPT_IN_REQUIRED_CLASS_ID) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val annotationClass = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue
|
val annotationClass = annotation.findArgumentByName(OptInNames.OPT_IN_ANNOTATION_CLASS) ?: continue
|
||||||
if (annotationClass.extractClassFromArgument()?.classId == annotationClassId) {
|
if (annotationClass.extractClassFromArgument(session)?.classId == annotationClassId) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ data class Some(val duration: Duration = Duration.INFINITE)
|
|||||||
fun foo(duration: Duration = Duration.INFINITE) {}
|
fun foo(duration: Duration = Duration.INFINITE) {}
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
Some()
|
<!OPT_IN_USAGE_ERROR!>Some<!>()
|
||||||
foo()
|
<!OPT_IN_USAGE_ERROR!>foo<!>()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user