Nullability-related warnings for receivers
#KT-6723 In Progress
This commit is contained in:
+64
-5
@@ -52,6 +52,13 @@ import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNotNull
|
|||||||
import org.jetbrains.kotlin.types.isFlexible
|
import org.jetbrains.kotlin.types.isFlexible
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.NullabilityInformationSource
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.NullabilityInformationSource
|
||||||
|
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability
|
||||||
|
|
||||||
public object KotlinJvmCheckerProvider : AdditionalCheckerProvider(
|
public object KotlinJvmCheckerProvider : AdditionalCheckerProvider(
|
||||||
annotationCheckers = listOf(PlatformStaticAnnotationChecker(), LocalFunInlineChecker(), ReifiedTypeParameterAnnotationChecker(), NativeFunChecker()),
|
annotationCheckers = listOf(PlatformStaticAnnotationChecker(), LocalFunInlineChecker(), ReifiedTypeParameterAnnotationChecker(), NativeFunChecker()),
|
||||||
@@ -152,16 +159,68 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun checkType(expression: JetExpression, expressionType: JetType, c: ResolutionContext<*>) {
|
private fun doCheckType(
|
||||||
if (TypeUtils.noExpectedType(c.expectedType)) return
|
expressionType: JetType,
|
||||||
|
expectedType: JetType,
|
||||||
|
dataFlowValue: DataFlowValue,
|
||||||
|
dataFlowInfo: DataFlowInfo,
|
||||||
|
reportWarning: (expectedMustNotBeNull: NullabilityInformationSource, actualMayBeNull: NullabilityInformationSource) -> Unit
|
||||||
|
) {
|
||||||
|
if (TypeUtils.noExpectedType(expectedType)) return
|
||||||
|
|
||||||
val expectedMustNotBeNull = c.expectedType.mustNotBeNull()
|
val expectedMustNotBeNull = expectedType.mustNotBeNull()
|
||||||
val actualNullabilityInKotlin = c.dataFlowInfo.getNullability(DataFlowValueFactory.createDataFlowValue(expression, expressionType, c.trace.getBindingContext()))
|
val actualNullabilityInKotlin = dataFlowInfo.getNullability(dataFlowValue)
|
||||||
val actualMayBeNull = if (!actualNullabilityInKotlin.canBeNull()) null else expressionType.mayBeNull()
|
val actualMayBeNull = if (actualNullabilityInKotlin == Nullability.NOT_NULL) null else expressionType.mayBeNull()
|
||||||
|
|
||||||
|
if (expectedMustNotBeNull == NullabilityInformationSource.KOTLIN && actualMayBeNull == NullabilityInformationSource.KOTLIN) {
|
||||||
|
// a type mismatch error will be reported elsewhere
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (expectedMustNotBeNull != null && actualMayBeNull != null) {
|
if (expectedMustNotBeNull != null && actualMayBeNull != null) {
|
||||||
|
reportWarning(expectedMustNotBeNull, actualMayBeNull)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkType(expression: JetExpression, expressionType: JetType, c: ResolutionContext<*>) {
|
||||||
|
doCheckType(
|
||||||
|
expressionType,
|
||||||
|
c.expectedType,
|
||||||
|
DataFlowValueFactory.createDataFlowValue(expression, expressionType, c.trace.getBindingContext()),
|
||||||
|
c.dataFlowInfo
|
||||||
|
) {
|
||||||
|
expectedMustNotBeNull,
|
||||||
|
actualMayBeNull ->
|
||||||
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(expression, expectedMustNotBeNull, actualMayBeNull))
|
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(expression, expectedMustNotBeNull, actualMayBeNull))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun checkReceiver(
|
||||||
|
receiverParameter: ReceiverParameterDescriptor,
|
||||||
|
receiverArgument: ReceiverValue,
|
||||||
|
safeAccess: Boolean,
|
||||||
|
c: CallResolutionContext<*>
|
||||||
|
) {
|
||||||
|
if (!safeAccess) {
|
||||||
|
doCheckType(
|
||||||
|
receiverArgument.getType(),
|
||||||
|
receiverParameter.getType(),
|
||||||
|
DataFlowValueFactory.createDataFlowValue(receiverArgument, c.trace.getBindingContext()),
|
||||||
|
c.dataFlowInfo
|
||||||
|
) {
|
||||||
|
expectedMustNotBeNull,
|
||||||
|
actualMayBeNull ->
|
||||||
|
val reportOn =
|
||||||
|
if (receiverArgument is ExpressionReceiver)
|
||||||
|
receiverArgument.getExpression()
|
||||||
|
else
|
||||||
|
c.call.getCalleeExpression() ?: c.call.getCallElement()
|
||||||
|
|
||||||
|
c.trace.report(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.on(
|
||||||
|
reportOn, expectedMustNotBeNull, actualMayBeNull
|
||||||
|
))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
|
|||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2;
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2;
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||||
import org.jetbrains.kotlin.psi.JetDeclaration;
|
import org.jetbrains.kotlin.psi.JetDeclaration;
|
||||||
import org.jetbrains.kotlin.psi.JetExpression;
|
import org.jetbrains.kotlin.psi.JetElement;
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.diagnostics.PositioningStrategies.*;
|
import static org.jetbrains.kotlin.diagnostics.PositioningStrategies.*;
|
||||||
import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
|
import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
|
||||||
@@ -63,7 +63,7 @@ public interface ErrorsJvm {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
DiagnosticFactory2<JetExpression, NullabilityInformationSource, NullabilityInformationSource> NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS = DiagnosticFactory2.create(WARNING);
|
DiagnosticFactory2<JetElement, NullabilityInformationSource, NullabilityInformationSource> NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS = DiagnosticFactory2.create(WARNING);
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
Object _initializer = new Object() {
|
Object _initializer = new Object() {
|
||||||
|
|||||||
@@ -644,6 +644,9 @@ public class CandidateResolver {
|
|||||||
if (safeAccess && !context.dataFlowInfo.getNullability(receiverValue).canBeNull()) {
|
if (safeAccess && !context.dataFlowInfo.getNullability(receiverValue).canBeNull()) {
|
||||||
context.tracing.unnecessarySafeCall(trace, receiverArgumentType);
|
context.tracing.unnecessarySafeCall(trace, receiverArgumentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.additionalTypeChecker.checkReceiver(receiverParameter, receiverArgument, safeAccess, context);
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
@@ -19,6 +19,9 @@ package org.jetbrains.kotlin.resolve.calls.checkers
|
|||||||
import org.jetbrains.kotlin.psi.JetExpression
|
import org.jetbrains.kotlin.psi.JetExpression
|
||||||
import org.jetbrains.kotlin.types.JetType
|
import org.jetbrains.kotlin.types.JetType
|
||||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||||
|
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext
|
||||||
|
|
||||||
public trait AdditionalTypeChecker {
|
public trait AdditionalTypeChecker {
|
||||||
|
|
||||||
@@ -28,7 +31,24 @@ public trait AdditionalTypeChecker {
|
|||||||
checker.checkType(expression, expressionType, c)
|
checker.checkType(expression, expressionType, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun checkReceiver(
|
||||||
|
receiverParameter: ReceiverParameterDescriptor,
|
||||||
|
receiverArgument: ReceiverValue,
|
||||||
|
safeAccess: Boolean,
|
||||||
|
c: CallResolutionContext<*>
|
||||||
|
) {
|
||||||
|
for (checker in checkers) {
|
||||||
|
checker.checkReceiver(receiverParameter, receiverArgument, safeAccess, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkType(expression: JetExpression, expressionType: JetType, c: ResolutionContext<*>)
|
fun checkType(expression: JetExpression, expressionType: JetType, c: ResolutionContext<*>)
|
||||||
|
fun checkReceiver(
|
||||||
|
receiverParameter: ReceiverParameterDescriptor,
|
||||||
|
receiverArgument: ReceiverValue,
|
||||||
|
safeAccess: Boolean,
|
||||||
|
c: CallResolutionContext<*>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
+10
-2
@@ -24,10 +24,11 @@ import org.jetbrains.kotlin.types.TypeUtils.noExpectedType
|
|||||||
import org.jetbrains.kotlin.types.getApproximationTo
|
import org.jetbrains.kotlin.types.getApproximationTo
|
||||||
import org.jetbrains.kotlin.types.Approximation
|
import org.jetbrains.kotlin.types.Approximation
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability
|
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import kotlin.properties.Delegates
|
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext
|
||||||
|
|
||||||
public class TypeApproximator : AdditionalTypeChecker {
|
public class TypeApproximator : AdditionalTypeChecker {
|
||||||
override fun checkType(expression: JetExpression, expressionType: JetType, c: ResolutionContext<*>) {
|
override fun checkType(expression: JetExpression, expressionType: JetType, c: ResolutionContext<*>) {
|
||||||
@@ -51,4 +52,11 @@ public class TypeApproximator : AdditionalTypeChecker {
|
|||||||
c.trace.record(BindingContext.EXPRESSION_RESULT_APPROXIMATION, expression, approximationInfo)
|
c.trace.record(BindingContext.EXPRESSION_RESULT_APPROXIMATION, expression, approximationInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun checkReceiver(
|
||||||
|
receiverParameter: ReceiverParameterDescriptor,
|
||||||
|
receiverArgument: ReceiverValue,
|
||||||
|
safeAccess: Boolean,
|
||||||
|
c: CallResolutionContext<*>
|
||||||
|
) { }
|
||||||
}
|
}
|
||||||
@@ -24,15 +24,15 @@ fun test() {
|
|||||||
var platformJ = J.staticJ
|
var platformJ = J.staticJ
|
||||||
|
|
||||||
+platformNN
|
+platformNN
|
||||||
+platformN
|
+<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>
|
||||||
+platformJ
|
+platformJ
|
||||||
|
|
||||||
++platformNN
|
++platformNN
|
||||||
++platformN
|
++<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>
|
||||||
++platformJ
|
++platformJ
|
||||||
|
|
||||||
platformNN++
|
platformNN++
|
||||||
platformN++
|
<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>++
|
||||||
platformJ++
|
platformJ++
|
||||||
|
|
||||||
1 + platformNN
|
1 + platformNN
|
||||||
@@ -40,7 +40,7 @@ fun test() {
|
|||||||
1 + platformJ
|
1 + platformJ
|
||||||
|
|
||||||
platformNN + 1
|
platformNN + 1
|
||||||
platformN + 1
|
<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!> + 1
|
||||||
platformJ + 1
|
platformJ + 1
|
||||||
|
|
||||||
1 plus platformNN
|
1 plus platformNN
|
||||||
@@ -48,11 +48,11 @@ fun test() {
|
|||||||
1 plus platformJ
|
1 plus platformJ
|
||||||
|
|
||||||
platformNN plus 1
|
platformNN plus 1
|
||||||
platformN plus 1
|
<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!> plus 1
|
||||||
platformJ plus 1
|
platformJ plus 1
|
||||||
|
|
||||||
platformNN += 1
|
platformNN += 1
|
||||||
platformN += 1
|
<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!> += 1
|
||||||
platformJ += 1
|
platformJ += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -22,5 +22,5 @@ public class J {
|
|||||||
import p.*
|
import p.*
|
||||||
|
|
||||||
var A by J.staticNN
|
var A by J.staticNN
|
||||||
var B by J.staticN
|
var B by <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS, NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>J.staticN<!>
|
||||||
var C by J.staticJ
|
var C by J.staticJ
|
||||||
+15
-2
@@ -1,3 +1,5 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
// FILE: p/J.java
|
// FILE: p/J.java
|
||||||
package p;
|
package p;
|
||||||
|
|
||||||
@@ -24,13 +26,24 @@ fun test() {
|
|||||||
val platformJ = J.staticJ
|
val platformJ = J.staticJ
|
||||||
|
|
||||||
platformNN.foo()
|
platformNN.foo()
|
||||||
platformN.foo()
|
<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>.foo()
|
||||||
platformJ.foo()
|
platformJ.foo()
|
||||||
|
|
||||||
|
with(platformNN) {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
with(platformN) {
|
||||||
|
<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>foo<!>()
|
||||||
|
}
|
||||||
|
with(platformJ) {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
|
||||||
platformNN.bar()
|
platformNN.bar()
|
||||||
platformN.bar()
|
platformN.bar()
|
||||||
platformJ.bar()
|
platformJ.bar()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun J.foo() {}
|
fun J.foo() {}
|
||||||
fun J?.bar() {}
|
fun J?.bar() {}
|
||||||
|
fun <T> with(t: T, f: T.() -> Unit) {}
|
||||||
|
|||||||
+1
@@ -1,5 +1,6 @@
|
|||||||
package
|
package
|
||||||
|
|
||||||
internal fun test(): kotlin.Unit
|
internal fun test(): kotlin.Unit
|
||||||
|
internal fun </*0*/ T> with(/*0*/ t: T, /*1*/ f: T.() -> kotlin.Unit): kotlin.Unit
|
||||||
internal fun p.J?.bar(): kotlin.Unit
|
internal fun p.J?.bar(): kotlin.Unit
|
||||||
internal fun p.J.foo(): kotlin.Unit
|
internal fun p.J.foo(): kotlin.Unit
|
||||||
|
|||||||
+17
-2
@@ -1,3 +1,5 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
// FILE: p/J.java
|
// FILE: p/J.java
|
||||||
package p;
|
package p;
|
||||||
|
|
||||||
@@ -26,6 +28,19 @@ fun test() {
|
|||||||
val platformJ = J.staticJ
|
val platformJ = J.staticJ
|
||||||
|
|
||||||
platformNN.foo()
|
platformNN.foo()
|
||||||
platformN.foo()
|
<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>.foo()
|
||||||
platformJ.foo()
|
platformJ.foo()
|
||||||
}
|
|
||||||
|
with(platformNN) {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
with(platformN) {
|
||||||
|
<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>foo<!>()
|
||||||
|
}
|
||||||
|
with(platformJ) {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> with(t: T, f: T.() -> Unit) {}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
package
|
package
|
||||||
|
|
||||||
internal fun test(): kotlin.Unit
|
internal fun test(): kotlin.Unit
|
||||||
|
internal fun </*0*/ T> with(/*0*/ t: T, /*1*/ f: T.() -> kotlin.Unit): kotlin.Unit
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ fun test() {
|
|||||||
val platformJ = J.staticJ
|
val platformJ = J.staticJ
|
||||||
|
|
||||||
for (x in platformNN) {}
|
for (x in platformNN) {}
|
||||||
for (x in platformN) {}
|
for (x in <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>) {}
|
||||||
for (x in platformJ) {}
|
for (x in platformJ) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import p.*
|
|||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
J.staticNN()
|
J.staticNN()
|
||||||
J.staticN()
|
J.<!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>staticN<!>()
|
||||||
J.staticJ()
|
J.staticJ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ fun test() {
|
|||||||
val platformJ = J.staticJ
|
val platformJ = J.staticJ
|
||||||
|
|
||||||
val (a1, b1) = platformNN
|
val (a1, b1) = platformNN
|
||||||
val (a2, b2) = platformN
|
val (a2, b2) = <!NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS, NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS!>platformN<!>
|
||||||
val (a3, b3) = platformJ
|
val (a3, b3) = platformJ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user