Fix incorrect handling of @NotNull type parameters
^KT-36770 In progress ^KT-40114 Relates ^KT-28785 Relates
This commit is contained in:
@@ -41,8 +41,10 @@ import org.jetbrains.kotlin.load.kotlin.DeserializationComponentsForJava
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.TargetEnvironment
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.ImplicitsExtensionsResolutionFilter
|
||||
import org.jetbrains.kotlin.resolve.createContainer
|
||||
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmDiagnosticComponents
|
||||
import org.jetbrains.kotlin.resolve.jvm.multiplatform.OptionalAnnotationPackageFragmentProvider
|
||||
@@ -129,7 +131,10 @@ fun StorageComponentContainer.configureJavaSpecificComponents(
|
||||
|
||||
useInstance(javaClassTracker ?: JavaClassesTracker.Default)
|
||||
useInstance(
|
||||
JavaResolverSettings.create(isReleaseCoroutines = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines))
|
||||
JavaResolverSettings.create(
|
||||
isReleaseCoroutines = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines),
|
||||
correctNullabilityForNotNullTypeParameter = languageVersionSettings.supportsFeature(LanguageFeature.ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated),
|
||||
)
|
||||
)
|
||||
|
||||
useImpl<FilesByFacadeFqNameIndexer>()
|
||||
|
||||
+57
-2
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.resolve.jvm.checkers
|
||||
|
||||
import org.jetbrains.kotlin.cfg.WhenChecker
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -35,15 +36,30 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerContext
|
||||
import org.jetbrains.kotlin.types.expressions.SenselessComparisonChecker
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
|
||||
class JavaNullabilityChecker : AdditionalTypeChecker {
|
||||
|
||||
override fun checkType(expression: KtExpression, expressionType: KotlinType, expressionTypeWithSmartCast: KotlinType, c: ResolutionContext<*>) {
|
||||
override fun checkType(
|
||||
expression: KtExpression,
|
||||
expressionType: KotlinType,
|
||||
expressionTypeWithSmartCast: KotlinType,
|
||||
c: ResolutionContext<*>
|
||||
) {
|
||||
val dataFlowValue by lazy(LazyThreadSafetyMode.NONE) {
|
||||
c.dataFlowValueFactory.createDataFlowValue(expression, expressionType, c)
|
||||
}
|
||||
|
||||
if (isWrongTypeParameterNullabilityForSubtyping(expressionType, c) { dataFlowValue }) {
|
||||
c.trace.report(ErrorsJvm.NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER.on(expression, c.expectedType, expressionType))
|
||||
}
|
||||
|
||||
doCheckType(
|
||||
expressionType,
|
||||
c.expectedType,
|
||||
{ c.dataFlowValueFactory.createDataFlowValue(expression, expressionType, c) },
|
||||
{ dataFlowValue },
|
||||
c.dataFlowInfo
|
||||
) { expectedType, actualType ->
|
||||
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(expression, expectedType, actualType))
|
||||
@@ -103,6 +119,45 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
private fun isWrongTypeParameterNullabilityForSubtyping(
|
||||
expressionType: KotlinType,
|
||||
c: ResolutionContext<*>,
|
||||
dataFlowValueForWholeExpression: () -> DataFlowValue
|
||||
): Boolean {
|
||||
if (c.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitUsingNullableTypeParameterAgainstNotNullAnnotated)) return false
|
||||
if (TypeUtils.noExpectedType(c.expectedType)) return false
|
||||
|
||||
var metWrongNullabilityInsideArguments = false
|
||||
|
||||
val typeContext: AbstractTypeCheckerContext = object : ClassicTypeCheckerContext(errorTypeEqualsToAnything = true) {
|
||||
private var expectsTypeArgument = false
|
||||
override fun customIsSubtypeOf(subType: KotlinTypeMarker, superType: KotlinTypeMarker): Boolean {
|
||||
|
||||
if (isNullableTypeAgainstNotNullTypeParameter(subType as KotlinType, superType as KotlinType)) {
|
||||
// data flow value is only checked for top-level types
|
||||
if (expectsTypeArgument || c.dataFlowInfo.getStableNullability(dataFlowValueForWholeExpression()) != Nullability.NOT_NULL) {
|
||||
metWrongNullabilityInsideArguments = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (!expectsTypeArgument) {
|
||||
expectsTypeArgument = true
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
AbstractTypeChecker.isSubtypeOf(typeContext, expressionType, c.expectedType)
|
||||
|
||||
return metWrongNullabilityInsideArguments
|
||||
}
|
||||
|
||||
private fun isNullableTypeAgainstNotNullTypeParameter(
|
||||
subType: KotlinType,
|
||||
superType: KotlinType
|
||||
) = superType is NotNullTypeVariable && subType.isNullable()
|
||||
|
||||
override fun checkReceiver(
|
||||
receiverParameter: ReceiverParameterDescriptor,
|
||||
receiverArgument: ReceiverValue,
|
||||
|
||||
+4
@@ -82,6 +82,10 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
||||
MAP.put(SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC, "Using non-JVM static members protected in the superclass companion is unsupported yet");
|
||||
|
||||
MAP.put(NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS, "Type mismatch: inferred type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE);
|
||||
MAP.put(NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER,
|
||||
"Type mismatch: type parameter with nullable bounds is used {1} is used where {0} was expected. This warning will become an error soon",
|
||||
RENDER_TYPE, RENDER_TYPE
|
||||
);
|
||||
MAP.put(RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS,
|
||||
"Unsafe use of a nullable receiver of type {0}", RENDER_TYPE);
|
||||
|
||||
|
||||
@@ -121,6 +121,9 @@ public interface ErrorsJvm {
|
||||
DiagnosticFactory2<KtElement, KotlinType, KotlinType> NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS
|
||||
= DiagnosticFactory2.create(WARNING);
|
||||
|
||||
DiagnosticFactory2<KtElement, KotlinType, KotlinType> NULLABLE_TYPE_PARAMETER_AGAINST_NOT_NULL_TYPE_PARAMETER
|
||||
= DiagnosticFactory2.create(WARNING);
|
||||
|
||||
DiagnosticFactory1<KtElement, KotlinType> RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS
|
||||
= DiagnosticFactory1.create(WARNING);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user