[FIR] Add DNN checks

Merge-request: KT-MR-7000
Merged-by: Nikolay Lunyak <lunyak.kolya@mail.ru>
This commit is contained in:
Nikolay Lunyak
2022-09-01 14:25:07 +00:00
committed by Space
parent bb2bee74cf
commit 3d9c77efa8
16 changed files with 124 additions and 26 deletions
@@ -2015,6 +2015,24 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.INCORRECT_LEFT_COMPONENT_OF_INTERSECTION) { firDiagnostic ->
IncorrectLeftComponentOfIntersectionImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION) { firDiagnostic ->
IncorrectRightComponentOfIntersectionImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.NULLABLE_ON_DEFINITELY_NOT_NULLABLE) { firDiagnostic ->
NullableOnDefinitelyNotNullableImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic ->
ExtensionInClassReferenceNotAllowedImpl(
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a),
@@ -1435,6 +1435,18 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val causingTypes: String
}
abstract class IncorrectLeftComponentOfIntersection : KtFirDiagnostic<KtTypeReference>() {
override val diagnosticClass get() = IncorrectLeftComponentOfIntersection::class
}
abstract class IncorrectRightComponentOfIntersection : KtFirDiagnostic<KtTypeReference>() {
override val diagnosticClass get() = IncorrectRightComponentOfIntersection::class
}
abstract class NullableOnDefinitelyNotNullable : KtFirDiagnostic<KtTypeReference>() {
override val diagnosticClass get() = NullableOnDefinitelyNotNullable::class
}
abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class
abstract val referencedDeclaration: KtCallableSymbol
@@ -1722,6 +1722,21 @@ internal class InferredTypeVariableIntoPossibleEmptyIntersectionImpl(
override val token: KtLifetimeToken,
) : KtFirDiagnostic.InferredTypeVariableIntoPossibleEmptyIntersection(), KtAbstractFirDiagnostic<PsiElement>
internal class IncorrectLeftComponentOfIntersectionImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.IncorrectLeftComponentOfIntersection(), KtAbstractFirDiagnostic<KtTypeReference>
internal class IncorrectRightComponentOfIntersectionImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.IncorrectRightComponentOfIntersection(), KtAbstractFirDiagnostic<KtTypeReference>
internal class NullableOnDefinitelyNotNullableImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.NullableOnDefinitelyNotNullable(), KtAbstractFirDiagnostic<KtTypeReference>
internal class ExtensionInClassReferenceNotAllowedImpl(
override val referencedDeclaration: KtCallableSymbol,
override val firDiagnostic: KtPsiDiagnostic,
@@ -705,6 +705,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<String>("description")
parameter<String>("causingTypes")
}
val INCORRECT_LEFT_COMPONENT_OF_INTERSECTION by error<KtTypeReference>()
val INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION by error<KtTypeReference>()
val NULLABLE_ON_DEFINITELY_NOT_NULLABLE by error<KtTypeReference>()
}
val REFLECTION by object : DiagnosticGroup("Reflection") {
@@ -417,6 +417,9 @@ object FirErrors {
val PLATFORM_CLASS_MAPPED_TO_KOTLIN by warning1<PsiElement, FqName>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION by error4<PsiElement, String, Collection<ConeKotlinType>, String, String>()
val INFERRED_TYPE_VARIABLE_INTO_POSSIBLE_EMPTY_INTERSECTION by warning4<PsiElement, String, Collection<ConeKotlinType>, String, String>()
val INCORRECT_LEFT_COMPONENT_OF_INTERSECTION by error0<KtTypeReference>()
val INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION by error0<KtTypeReference>()
val NULLABLE_ON_DEFINITELY_NOT_NULLABLE by error0<KtTypeReference>()
// Reflection
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<KtExpression, FirCallableSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -12,6 +12,7 @@ object CommonTypeCheckers : TypeCheckers() {
FirTypeAnnotationChecker,
FirSuspendModifierChecker,
FirDeprecatedTypeChecker,
FirOptInUsageTypeRefChecker
FirOptInUsageTypeRefChecker,
FirDefinitelyNotNullableChecker,
)
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2022 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.type
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.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.types.*
object FirDefinitelyNotNullableChecker : FirTypeRefChecker() {
override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) {
val intersection = (typeRef as? FirResolvedTypeRef)?.delegatedTypeRef as? FirIntersectionTypeRef ?: return
if (intersection.isMarkedNullable) {
reporter.reportOn(intersection.source, FirErrors.NULLABLE_ON_DEFINITELY_NOT_NULLABLE, context)
}
if (!intersection.isLeftValidForDefinitelyNotNullable) {
reporter.reportOn(intersection.leftType.source, FirErrors.INCORRECT_LEFT_COMPONENT_OF_INTERSECTION, context)
}
if (!intersection.isRightValidForDefinitelyNotNullable) {
reporter.reportOn(intersection.rightType.source, FirErrors.INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION, context)
}
}
}
@@ -252,6 +252,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCONSISTENT_TYPE_PARAMETER_BOUNDS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCONSISTENT_TYPE_PARAMETER_VALUES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCORRECT_CHARACTER_LITERAL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCORRECT_LEFT_COMPONENT_OF_INTERSECTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INC_DEC_SHOULD_NOT_RETURN_UNIT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERENCE_ERROR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERENCE_UNSUCCESSFUL_FORK
@@ -359,6 +361,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_THIS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_TYPE_ARGUMENTS_ON_RHS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_VALUE_FOR_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_INLINE_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_ON_DEFINITELY_NOT_NULLABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_SUPERTYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_TYPE_IN_CLASS_LITERAL_LHS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER
@@ -982,6 +985,18 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
TO_STRING,
TO_STRING
)
map.put(
NULLABLE_ON_DEFINITELY_NOT_NULLABLE,
"'!!' type cannot be marked as nullable"
)
map.put(
INCORRECT_LEFT_COMPONENT_OF_INTERSECTION,
"Intersection types are only supported for definitely non-nullable types: left part should be a type parameter with nullable bounds"
)
map.put(
INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION,
"Intersection types are only supported for definitely non-nullable types: right part should be non-nullable Any"
)
map.put(TYPE_MISMATCH, "Type mismatch: inferred type is {1} but {0} was expected", TO_STRING, TO_STRING, NOT_RENDERED)
map.put(THROWABLE_TYPE_MISMATCH, "Throwable type mismatch: actual type is {0}", TO_STRING, NOT_RENDERED)
@@ -128,6 +128,7 @@ private fun ConeDiagnostic.toKtDiagnostic(
is ConeLocalVariableNoTypeOrInitializer -> runIf(variable.isLocalMember) {
FirErrors.VARIABLE_WITH_NO_TYPE_NO_INITIALIZER.createOn(source)
}
is ConeForbiddenIntersection -> null // reported in FirDefinitelyNotNullableChecker
is ConeUnderscoreIsReserved -> FirErrors.UNDERSCORE_IS_RESERVED.createOn(this.source)
is ConeUnderscoreUsageWithoutBackticks -> FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.createOn(this.source)
@@ -523,14 +523,11 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
is FirDynamicTypeRef -> ConeDynamicType.create(session) to null
is FirIntersectionTypeRef -> {
val leftType = typeRef.leftType.coneType
val rightType = typeRef.rightType.coneType
if (rightType.isAny && leftType is ConeTypeParameterType) {
if (leftType is ConeTypeParameterType) {
ConeDefinitelyNotNullType(leftType) to null
} else {
ConeErrorType(ConeUnsupported("Intersection types are not supported yet", typeRef.source)) to null
ConeErrorType(ConeForbiddenIntersection) to null
}
}
else -> error(typeRef.render())
}
@@ -237,3 +237,7 @@ class ConeAmbiguousAlteredAssign(val altererNames: List<String?>) : ConeDiagnost
override val reason: String
get() = "Assign altered by multiple extensions"
}
object ConeForbiddenIntersection : ConeDiagnostic {
override val reason: String get() = "Such an intersection type is not allowed"
}
@@ -191,3 +191,7 @@ val ConeKotlinType.canBeNull: Boolean
}
}
val FirIntersectionTypeRef.isLeftValidForDefinitelyNotNullable
get() = leftType.coneType.let { it is ConeTypeParameterType && it.canBeNull && !it.isMarkedNullable }
val FirIntersectionTypeRef.isRightValidForDefinitelyNotNullable get() = rightType.coneType.isAny
@@ -1,7 +0,0 @@
// !LANGUAGE: +DefinitelyNonNullableTypes
fun main(x: Collection<String>) {
if (x is List<!SYNTAX!><!> <!SYNTAX!><!SYNTAX!><!>& Any)<!> {}
val w: <!UNSUPPORTED!><!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>List<!> & Any<!> = null!!
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: +DefinitelyNonNullableTypes
fun main(x: Collection<String>) {
@@ -1,18 +1,18 @@
// !LANGUAGE: +DefinitelyNonNullableTypes
fun <T : Any> foo(x: T & Any, y: <!UNSUPPORTED!>List<<!UNSUPPORTED!>String & Any<!>> & Any<!>) {}
fun <T : Any> foo(x: <!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>T<!> & Any, y: <!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>List<<!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>String<!> & Any><!> & Any) {}
fun <F> bar1(x: F? & Any) {}
fun <F> bar2(x: <!UNSUPPORTED!>F & Any?<!>) {}
fun <F> bar3(x: (F?) & Any) {}
fun <F> bar4(x: (F & Any)?) {}
fun <F> bar1(x: <!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>F?<!> & Any) {}
fun <F> bar2(x: F & <!INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION!>Any?<!>) {}
fun <F> bar3(x: <!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>(F?)<!> & Any) {}
fun <F> bar4(x: <!NULLABLE_ON_DEFINITELY_NOT_NULLABLE!>(F & Any)?<!>) {}
fun <F> bar5(x: <!UNSUPPORTED!>F & String<!>) {}
fun <F> bar5(x: F & <!INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION!>String<!>) {}
fun <F> bar6(x: <!UNSUPPORTED!>F & (F & Any)<!>) {}
fun <F> bar7(x: <!UNSUPPORTED!>(F & Any) & Any<!>) {}
fun <F> bar6(x: F & <!INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION!>(F & Any)<!>) {}
fun <F> bar7(x: <!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>(F & Any)<!> & Any) {}
fun <F> bar8(x: (F & Any).() -> Unit) {}
fun <F> (F & Any).bar9(x: () -> Unit) {}
fun <F> bar10(x: <!UNSUPPORTED!>F & <!UNSUPPORTED!>Any & String<!><!>) {}
fun <F> bar11(x: <!UNSUPPORTED!>Double & <!UNSUPPORTED!>Any & String<!><!>) {}
fun <F> bar10(x: F & <!INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION!><!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>Any<!> & <!INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION!>String<!><!>) {}
fun <F> bar11(x: <!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>Double<!> & <!INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION!><!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>Any<!> & <!INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION!>String<!><!>) {}
@@ -1,11 +1,11 @@
// !LANGUAGE: +DefinitelyNonNullableTypes
fun <T : Comparable<T & Any>> sort1() {}
fun <T : Comparable<<!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>T<!> & Any>> sort1() {}
fun <T : Comparable<T & Any>?> sort2() {}
class A1<K : Comparable<K & Any>>
class A1<K : Comparable<<!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>K<!> & Any>>
class A2<K : Comparable<K & Any>?>
fun <R : T & Any, T> bar() {}
fun <<!CYCLIC_GENERIC_UPPER_BOUND!>E : E & Any<!>> baz() {}
fun <<!CYCLIC_GENERIC_UPPER_BOUND!>E : <!INCORRECT_LEFT_COMPONENT_OF_INTERSECTION!>E<!> & Any<!>> baz() {}