[FIR] Implement DEFINITELY_NON_NULLABLE_AS_REIFIED diagnostic
#KT-55646 Fixed
This commit is contained in:
committed by
Space Team
parent
509a97a7b5
commit
0a2477585a
+6
@@ -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),
|
||||
|
||||
+4
@@ -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
|
||||
|
||||
+5
@@ -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,
|
||||
|
||||
+1
@@ -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>()
|
||||
|
||||
+30
-20
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -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",
|
||||
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
// SKIP_TXT
|
||||
// !LANGUAGE: +DefinitelyNonNullableTypes
|
||||
|
||||
inline fun <reified T : Any> foo() {}
|
||||
|
||||
inline fun <reified F> bar() {
|
||||
foo<F & Any>()
|
||||
}
|
||||
compiler/testData/diagnostics/tests/explicitDefinitelyNotNullableViaIntersection/reifiedArguments.kt
Vendored
+10
@@ -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()
|
||||
Reference in New Issue
Block a user