Refine diagnostics for nullability migration warnings

#KT-24911 Fixed
This commit is contained in:
Denis Zharkov
2018-07-13 17:51:11 +03:00
parent f85030b1e6
commit 89d99e3989
34 changed files with 140 additions and 113 deletions
@@ -45,8 +45,8 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
c.expectedType,
{ c.dataFlowValueFactory.createDataFlowValue(expression, expressionType, c) } ,
c.dataFlowInfo
) { expectedMustNotBeNull, actualMayBeNull ->
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(expression, expectedMustNotBeNull, actualMayBeNull))
) { expectedType, actualType ->
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(expression, expectedType, actualType))
}
when (expression) {
@@ -121,12 +121,15 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
receiverParameter.type,
{ dataFlowValue },
c.dataFlowInfo
) { expectedMustNotBeNull,
actualMayBeNull ->
val reportOn = (receiverArgument as? ExpressionReceiver)?.expression ?: (c.call.calleeExpression ?: c.call.callElement)
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(
reportOn, expectedMustNotBeNull, actualMayBeNull
))
) { expectedType,
actualType ->
val receiverExpression = (receiverArgument as? ExpressionReceiver)?.expression
if (receiverExpression != null) {
c.trace.report(ErrorsJvm.RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(receiverExpression, actualType))
} else {
val reportOn = c.call.calleeExpression ?: c.call.callElement
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(reportOn, expectedType, actualType))
}
}
}
@@ -135,22 +138,21 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
expectedType: KotlinType,
dataFlowValue: () -> DataFlowValue,
dataFlowInfo: DataFlowInfo,
reportWarning: (expectedMustNotBeNull: ErrorsJvm.NullabilityInformationSource, actualMayBeNull: ErrorsJvm.NullabilityInformationSource) -> Unit
reportWarning: (expectedType: KotlinType, actualType: KotlinType) -> Unit
) {
if (TypeUtils.noExpectedType(expectedType)) {
return
}
val expectedMustNotBeNull = expectedType.mustNotBeNull()
val actualMayBeNull = expressionType.mayBeNull()
if (expectedMustNotBeNull == ErrorsJvm.NullabilityInformationSource.KOTLIN && actualMayBeNull == ErrorsJvm.NullabilityInformationSource.KOTLIN) {
val expectedMustNotBeNull = expectedType.mustNotBeNull() ?: return
val actualMayBeNull = expressionType.mayBeNull() ?: return
if (expectedMustNotBeNull.isFromKotlin && actualMayBeNull.isFromKotlin) {
// a type mismatch error will be reported elsewhere
return
}
if (expectedMustNotBeNull != null && actualMayBeNull != null &&
dataFlowInfo.getStableNullability(dataFlowValue()) != Nullability.NOT_NULL) {
reportWarning(expectedMustNotBeNull, actualMayBeNull)
if (dataFlowInfo.getStableNullability(dataFlowValue()) != Nullability.NOT_NULL) {
reportWarning(expectedMustNotBeNull.enhancedType, actualMayBeNull.enhancedType)
}
}
@@ -159,23 +161,30 @@ class JavaNullabilityChecker : AdditionalTypeChecker {
dataFlowValue: () -> DataFlowValue,
c: ResolutionContext<*>,
body: () -> T
) = if (type.mustNotBeNull() == ErrorsJvm.NullabilityInformationSource.JAVA &&
) = if (type.mustNotBeNull()?.isFromJava == true &&
c.dataFlowInfo.getStableNullability(dataFlowValue()).canBeNull())
body()
else
null
private fun KotlinType.mustNotBeNull(): ErrorsJvm.NullabilityInformationSource? = when {
!isError && !isFlexible() && !TypeUtils.acceptsNullable(this) -> ErrorsJvm.NullabilityInformationSource.KOTLIN
isFlexible() && !TypeUtils.acceptsNullable(asFlexibleType().upperBound) -> ErrorsJvm.NullabilityInformationSource.KOTLIN
this is TypeWithEnhancement && enhancement.mustNotBeNull() != null -> ErrorsJvm.NullabilityInformationSource.JAVA
private class EnhancedNullabilityInfo(val enhancedType: KotlinType, val isFromJava: Boolean) {
val isFromKotlin get() = !isFromJava
}
private fun KotlinType.enhancementFromKotlin() = EnhancedNullabilityInfo(this, isFromJava = false)
private fun TypeWithEnhancement.enhancementFromJava() = EnhancedNullabilityInfo(enhancement, isFromJava = true)
private fun KotlinType.mustNotBeNull(): EnhancedNullabilityInfo? = when {
!isError && !isFlexible() && !TypeUtils.acceptsNullable(this) -> enhancementFromKotlin()
isFlexible() && !TypeUtils.acceptsNullable(asFlexibleType().upperBound) -> enhancementFromKotlin()
this is TypeWithEnhancement && enhancement.mustNotBeNull() != null -> enhancementFromJava()
else -> null
}
private fun KotlinType.mayBeNull(): ErrorsJvm.NullabilityInformationSource? = when {
!isError && !isFlexible() && TypeUtils.acceptsNullable(this) -> ErrorsJvm.NullabilityInformationSource.KOTLIN
isFlexible() && TypeUtils.acceptsNullable(asFlexibleType().lowerBound) -> ErrorsJvm.NullabilityInformationSource.KOTLIN
this is TypeWithEnhancement && enhancement.mayBeNull() != null -> ErrorsJvm.NullabilityInformationSource.JAVA
private fun KotlinType.mayBeNull(): EnhancedNullabilityInfo? = when {
!isError && !isFlexible() && TypeUtils.acceptsNullable(this) -> enhancementFromKotlin()
isFlexible() && TypeUtils.acceptsNullable(asFlexibleType().lowerBound) -> enhancementFromKotlin()
this is TypeWithEnhancement && enhancement.mayBeNull() != null -> enhancementFromJava()
else -> null
}
}
@@ -87,8 +87,10 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
MAP.put(INTERFACE_CANT_CALL_DEFAULT_METHOD_VIA_SUPER, "Interfaces can call default methods via super only within @JvmDefault members. Please annotate the containing interface member with @JvmDefault");
MAP.put(SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC, "Using non-JVM static members protected in the superclass companion is unsupported yet");
MAP.put(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS,
"Expected type does not accept nulls in {0}, but the value may be null in {1}", Renderers.TO_STRING, Renderers.TO_STRING);
MAP.put(NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS, "Type mismatch: inferred type is {1} but {0} was expected", RENDER_TYPE, RENDER_TYPE);
MAP.put(RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS,
"Unsafe use of a nullable receiver of type {0}", RENDER_TYPE);
MAP.put(WHEN_ENUM_CAN_BE_NULL_IN_JAVA, "Enum argument can be null in Java, but exhaustive when contains no null branch");
MAP.put(JAVA_CLASS_ON_COMPANION,
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.resolve.jvm.diagnostics;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.diagnostics.*;
import org.jetbrains.kotlin.name.FqName;
@@ -125,26 +124,12 @@ public interface ErrorsJvm {
DiagnosticFactory0<KtAnnotationEntry> EXPLICIT_METADATA_IS_DISALLOWED = DiagnosticFactory0.create(ERROR);
enum NullabilityInformationSource {
KOTLIN {
@NotNull
@Override
public String toString() {
return "Kotlin";
}
},
JAVA {
@NotNull
@Override
public String toString() {
return "Java";
}
}
}
DiagnosticFactory2<KtElement, NullabilityInformationSource, NullabilityInformationSource> NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS
DiagnosticFactory2<KtElement, KotlinType, KotlinType> NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS
= DiagnosticFactory2.create(WARNING);
DiagnosticFactory1<KtElement, KotlinType> RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS
= DiagnosticFactory1.create(WARNING);
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
{