[FIR] Implement DEFINITELY_NON_NULLABLE_AS_REIFIED diagnostic

#KT-55646 Fixed
This commit is contained in:
Kirill Rakhman
2023-04-20 10:43:55 +02:00
committed by Space Team
parent 509a97a7b5
commit 0a2477585a
9 changed files with 62 additions and 28 deletions
@@ -2074,6 +2074,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.DEFINITELY_NON_NULLABLE_AS_REIFIED) { firDiagnostic ->
DefinitelyNonNullableAsReifiedImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.FINAL_UPPER_BOUND) { firDiagnostic ->
FinalUpperBoundImpl(
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
@@ -1474,6 +1474,10 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val type: KtType
}
abstract class DefinitelyNonNullableAsReified : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = DefinitelyNonNullableAsReified::class
}
abstract class FinalUpperBound : KtFirDiagnostic<KtTypeReference>() {
override val diagnosticClass get() = FinalUpperBound::class
abstract val type: KtType
@@ -1772,6 +1772,11 @@ internal class ReifiedTypeForbiddenSubstitutionImpl(
override val token: KtLifetimeToken,
) : KtFirDiagnostic.ReifiedTypeForbiddenSubstitution(), KtAbstractFirDiagnostic<PsiElement>
internal class DefinitelyNonNullableAsReifiedImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.DefinitelyNonNullableAsReified(), KtAbstractFirDiagnostic<PsiElement>
internal class FinalUpperBoundImpl(
override val type: KtType,
override val firDiagnostic: KtPsiDiagnostic,
@@ -666,6 +666,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val REIFIED_TYPE_FORBIDDEN_SUBSTITUTION by error<PsiElement> {
parameter<ConeKotlinType>("type")
}
val DEFINITELY_NON_NULLABLE_AS_REIFIED by error<PsiElement>()
val FINAL_UPPER_BOUND by warning<KtTypeReference> {
parameter<ConeKotlinType>("type")
@@ -413,6 +413,7 @@ object FirErrors {
val TYPE_PARAMETER_AS_REIFIED by error1<PsiElement, FirTypeParameterSymbol>()
val TYPE_PARAMETER_AS_REIFIED_ARRAY by deprecationError1<PsiElement, FirTypeParameterSymbol>(ProhibitNonReifiedArraysAsReifiedTypeArguments)
val REIFIED_TYPE_FORBIDDEN_SUBSTITUTION by error1<PsiElement, ConeKotlinType>()
val DEFINITELY_NON_NULLABLE_AS_REIFIED by error0<PsiElement>()
val FINAL_UPPER_BOUND by warning1<KtTypeReference, ConeKotlinType>()
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error0<KtTypeReference>()
val BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER by error0<KtElement>()
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.KtRealSourceElementKind
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.chooseFactory
@@ -20,19 +21,27 @@ import org.jetbrains.kotlin.name.StandardClassIds
object FirReifiedChecker : FirQualifiedAccessExpressionChecker() {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
val calleReference = expression.calleeReference
val calleeReference = expression.calleeReference
val typeArguments = expression.typeArguments
val typeParameters = calleReference.toResolvedCallableSymbol()?.typeParameterSymbols ?: return
val typeParameters = calleeReference.toResolvedCallableSymbol()?.typeParameterSymbols ?: return
val count = minOf(typeArguments.size, typeParameters.size)
for (index in 0 until count) {
val typeArgumentProjection = typeArguments.elementAt(index)
val source = typeArgumentProjection.source ?: calleReference.source
val typeArgument = typeArgumentProjection.toConeTypeProjection().type
val source = typeArgumentProjection.source ?: calleeReference.source ?: continue
val typeArgument = typeArgumentProjection.toConeTypeProjection().type ?: continue
val typeParameter = typeParameters[index]
if (source != null && typeParameter.isReifiedTypeParameterOrFromKotlinArray()) {
checkArgumentAndReport(typeArgument, source, false, context, reporter)
if (typeParameter.isReifiedTypeParameterOrFromKotlinArray()) {
checkArgumentAndReport(
typeArgument,
source,
isExplicit = typeArgumentProjection.source?.kind == KtRealSourceElementKind,
isArray = false,
context,
reporter
)
}
}
}
@@ -44,34 +53,35 @@ object FirReifiedChecker : FirQualifiedAccessExpressionChecker() {
}
private fun checkArgumentAndReport(
typeArgument: ConeKotlinType?,
typeArgument: ConeKotlinType,
source: KtSourceElement,
isExplicit: Boolean,
isArray: Boolean,
context: CheckerContext,
reporter: DiagnosticReporter
) {
if (typeArgument?.classId == StandardClassIds.Array) {
checkArgumentAndReport(typeArgument.typeArguments[0].type, source, true, context, reporter)
if (typeArgument.classId == StandardClassIds.Array) {
val nestedTypeArgument = typeArgument.typeArguments[0].type ?: return
checkArgumentAndReport(nestedTypeArgument, source, isExplicit, isArray = true, context, reporter)
return
}
if (typeArgument is ConeTypeParameterType) {
val factory = if (isArray) {
FirErrors.TYPE_PARAMETER_AS_REIFIED_ARRAY.chooseFactory(context)
} else {
FirErrors.TYPE_PARAMETER_AS_REIFIED
}
val symbol = typeArgument.lookupTag.typeParameterSymbol
if (!symbol.isReified) {
reporter.reportOn(source, factory, symbol, context)
reporter.reportOn(
source,
if (isArray) FirErrors.TYPE_PARAMETER_AS_REIFIED_ARRAY.chooseFactory(context) else FirErrors.TYPE_PARAMETER_AS_REIFIED,
symbol,
context
)
}
} else if (typeArgument != null && typeArgument.cannotBeReified()) {
} else if (typeArgument is ConeDefinitelyNotNullType && isExplicit) {
// We sometimes infer type arguments to DNN types, which seems to be ok. Only report explicit DNN types written by user.
reporter.reportOn(source, FirErrors.DEFINITELY_NON_NULLABLE_AS_REIFIED, context)
} else if (typeArgument.isNothing || typeArgument.isNullableNothing || typeArgument is ConeCapturedType) {
reporter.reportOn(source, FirErrors.REIFIED_TYPE_FORBIDDEN_SUBSTITUTION, typeArgument, context)
return
}
}
private fun ConeKotlinType.cannotBeReified(): Boolean {
return this.isNothing || this.isNullableNothing || this is ConeCapturedType
}
}
@@ -147,6 +147,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_VARARG
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_WITHOUT_PARAMETERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DECLARATION_CANT_BE_INLINED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEFINITELY_NON_NULLABLE_AS_REIFIED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_PROPERTY_INSIDE_VALUE_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_PROPERTY_IN_INTERFACE
@@ -1219,6 +1220,10 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
"Cannot use ''{0}'' as reified type parameter",
RENDER_TYPE
)
map.put(
DEFINITELY_NON_NULLABLE_AS_REIFIED,
"Cannot use definitely-non-nullable type as reified type argument",
)
map.put(
FINAL_UPPER_BOUND,
"''{0}'' is a final type, and thus a value of the type parameter is predetermined",
@@ -1,8 +0,0 @@
// SKIP_TXT
// !LANGUAGE: +DefinitelyNonNullableTypes
inline fun <reified T : Any> foo() {}
inline fun <reified F> bar() {
foo<F & Any>()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// SKIP_TXT
// !LANGUAGE: +DefinitelyNonNullableTypes
@@ -6,3 +7,12 @@ inline fun <reified T : Any> foo() {}
inline fun <reified F> bar() {
foo<<!DEFINITELY_NON_NULLABLE_AS_REIFIED!>F & Any<!>>()
}
class KAnnotatedElement(val annotations: List<Any>)
inline fun <reified T : Any> Iterable<*>.firstIsInstanceOrNull(): T? {
return null
}
private inline fun <reified T> KAnnotatedElement.findAnnotation(): T? =
annotations.firstIsInstanceOrNull()