[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,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.DEFINITELY_NON_NULLABLE_AS_REIFIED) { firDiagnostic ->
|
||||||
|
DefinitelyNonNullableAsReifiedImpl(
|
||||||
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirErrors.FINAL_UPPER_BOUND) { firDiagnostic ->
|
add(FirErrors.FINAL_UPPER_BOUND) { firDiagnostic ->
|
||||||
FinalUpperBoundImpl(
|
FinalUpperBoundImpl(
|
||||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||||
|
|||||||
+4
@@ -1474,6 +1474,10 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
abstract val type: KtType
|
abstract val type: KtType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class DefinitelyNonNullableAsReified : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = DefinitelyNonNullableAsReified::class
|
||||||
|
}
|
||||||
|
|
||||||
abstract class FinalUpperBound : KtFirDiagnostic<KtTypeReference>() {
|
abstract class FinalUpperBound : KtFirDiagnostic<KtTypeReference>() {
|
||||||
override val diagnosticClass get() = FinalUpperBound::class
|
override val diagnosticClass get() = FinalUpperBound::class
|
||||||
abstract val type: KtType
|
abstract val type: KtType
|
||||||
|
|||||||
+5
@@ -1772,6 +1772,11 @@ internal class ReifiedTypeForbiddenSubstitutionImpl(
|
|||||||
override val token: KtLifetimeToken,
|
override val token: KtLifetimeToken,
|
||||||
) : KtFirDiagnostic.ReifiedTypeForbiddenSubstitution(), KtAbstractFirDiagnostic<PsiElement>
|
) : KtFirDiagnostic.ReifiedTypeForbiddenSubstitution(), KtAbstractFirDiagnostic<PsiElement>
|
||||||
|
|
||||||
|
internal class DefinitelyNonNullableAsReifiedImpl(
|
||||||
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
|
override val token: KtLifetimeToken,
|
||||||
|
) : KtFirDiagnostic.DefinitelyNonNullableAsReified(), KtAbstractFirDiagnostic<PsiElement>
|
||||||
|
|
||||||
internal class FinalUpperBoundImpl(
|
internal class FinalUpperBoundImpl(
|
||||||
override val type: KtType,
|
override val type: KtType,
|
||||||
override val firDiagnostic: KtPsiDiagnostic,
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
|
|||||||
+1
@@ -666,6 +666,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
|||||||
val REIFIED_TYPE_FORBIDDEN_SUBSTITUTION by error<PsiElement> {
|
val REIFIED_TYPE_FORBIDDEN_SUBSTITUTION by error<PsiElement> {
|
||||||
parameter<ConeKotlinType>("type")
|
parameter<ConeKotlinType>("type")
|
||||||
}
|
}
|
||||||
|
val DEFINITELY_NON_NULLABLE_AS_REIFIED by error<PsiElement>()
|
||||||
|
|
||||||
val FINAL_UPPER_BOUND by warning<KtTypeReference> {
|
val FINAL_UPPER_BOUND by warning<KtTypeReference> {
|
||||||
parameter<ConeKotlinType>("type")
|
parameter<ConeKotlinType>("type")
|
||||||
|
|||||||
@@ -413,6 +413,7 @@ object FirErrors {
|
|||||||
val TYPE_PARAMETER_AS_REIFIED by error1<PsiElement, FirTypeParameterSymbol>()
|
val TYPE_PARAMETER_AS_REIFIED by error1<PsiElement, FirTypeParameterSymbol>()
|
||||||
val TYPE_PARAMETER_AS_REIFIED_ARRAY by deprecationError1<PsiElement, FirTypeParameterSymbol>(ProhibitNonReifiedArraysAsReifiedTypeArguments)
|
val TYPE_PARAMETER_AS_REIFIED_ARRAY by deprecationError1<PsiElement, FirTypeParameterSymbol>(ProhibitNonReifiedArraysAsReifiedTypeArguments)
|
||||||
val REIFIED_TYPE_FORBIDDEN_SUBSTITUTION by error1<PsiElement, ConeKotlinType>()
|
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 FINAL_UPPER_BOUND by warning1<KtTypeReference, ConeKotlinType>()
|
||||||
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error0<KtTypeReference>()
|
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error0<KtTypeReference>()
|
||||||
val BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER by error0<KtElement>()
|
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
|
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.KtRealSourceElementKind
|
||||||
import org.jetbrains.kotlin.KtSourceElement
|
import org.jetbrains.kotlin.KtSourceElement
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.diagnostics.chooseFactory
|
import org.jetbrains.kotlin.diagnostics.chooseFactory
|
||||||
@@ -20,19 +21,27 @@ import org.jetbrains.kotlin.name.StandardClassIds
|
|||||||
|
|
||||||
object FirReifiedChecker : FirQualifiedAccessExpressionChecker() {
|
object FirReifiedChecker : FirQualifiedAccessExpressionChecker() {
|
||||||
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
val calleReference = expression.calleeReference
|
val calleeReference = expression.calleeReference
|
||||||
val typeArguments = expression.typeArguments
|
val typeArguments = expression.typeArguments
|
||||||
val typeParameters = calleReference.toResolvedCallableSymbol()?.typeParameterSymbols ?: return
|
val typeParameters = calleeReference.toResolvedCallableSymbol()?.typeParameterSymbols ?: return
|
||||||
|
|
||||||
val count = minOf(typeArguments.size, typeParameters.size)
|
val count = minOf(typeArguments.size, typeParameters.size)
|
||||||
for (index in 0 until count) {
|
for (index in 0 until count) {
|
||||||
val typeArgumentProjection = typeArguments.elementAt(index)
|
val typeArgumentProjection = typeArguments.elementAt(index)
|
||||||
val source = typeArgumentProjection.source ?: calleReference.source
|
val source = typeArgumentProjection.source ?: calleeReference.source ?: continue
|
||||||
val typeArgument = typeArgumentProjection.toConeTypeProjection().type
|
|
||||||
|
val typeArgument = typeArgumentProjection.toConeTypeProjection().type ?: continue
|
||||||
val typeParameter = typeParameters[index]
|
val typeParameter = typeParameters[index]
|
||||||
|
|
||||||
if (source != null && typeParameter.isReifiedTypeParameterOrFromKotlinArray()) {
|
if (typeParameter.isReifiedTypeParameterOrFromKotlinArray()) {
|
||||||
checkArgumentAndReport(typeArgument, source, false, context, reporter)
|
checkArgumentAndReport(
|
||||||
|
typeArgument,
|
||||||
|
source,
|
||||||
|
isExplicit = typeArgumentProjection.source?.kind == KtRealSourceElementKind,
|
||||||
|
isArray = false,
|
||||||
|
context,
|
||||||
|
reporter
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,34 +53,35 @@ object FirReifiedChecker : FirQualifiedAccessExpressionChecker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkArgumentAndReport(
|
private fun checkArgumentAndReport(
|
||||||
typeArgument: ConeKotlinType?,
|
typeArgument: ConeKotlinType,
|
||||||
source: KtSourceElement,
|
source: KtSourceElement,
|
||||||
|
isExplicit: Boolean,
|
||||||
isArray: Boolean,
|
isArray: Boolean,
|
||||||
context: CheckerContext,
|
context: CheckerContext,
|
||||||
reporter: DiagnosticReporter
|
reporter: DiagnosticReporter
|
||||||
) {
|
) {
|
||||||
if (typeArgument?.classId == StandardClassIds.Array) {
|
if (typeArgument.classId == StandardClassIds.Array) {
|
||||||
checkArgumentAndReport(typeArgument.typeArguments[0].type, source, true, context, reporter)
|
val nestedTypeArgument = typeArgument.typeArguments[0].type ?: return
|
||||||
|
checkArgumentAndReport(nestedTypeArgument, source, isExplicit, isArray = true, context, reporter)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeArgument is ConeTypeParameterType) {
|
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
|
val symbol = typeArgument.lookupTag.typeParameterSymbol
|
||||||
if (!symbol.isReified) {
|
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)
|
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_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.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.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_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_INSIDE_VALUE_CLASS
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_PROPERTY_IN_INTERFACE
|
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",
|
"Cannot use ''{0}'' as reified type parameter",
|
||||||
RENDER_TYPE
|
RENDER_TYPE
|
||||||
)
|
)
|
||||||
|
map.put(
|
||||||
|
DEFINITELY_NON_NULLABLE_AS_REIFIED,
|
||||||
|
"Cannot use definitely-non-nullable type as reified type argument",
|
||||||
|
)
|
||||||
map.put(
|
map.put(
|
||||||
FINAL_UPPER_BOUND,
|
FINAL_UPPER_BOUND,
|
||||||
"''{0}'' is a final type, and thus a value of the type parameter is predetermined",
|
"''{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
|
// SKIP_TXT
|
||||||
// !LANGUAGE: +DefinitelyNonNullableTypes
|
// !LANGUAGE: +DefinitelyNonNullableTypes
|
||||||
|
|
||||||
@@ -6,3 +7,12 @@ inline fun <reified T : Any> foo() {}
|
|||||||
inline fun <reified F> bar() {
|
inline fun <reified F> bar() {
|
||||||
foo<<!DEFINITELY_NON_NULLABLE_AS_REIFIED!>F & Any<!>>()
|
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