Nullability-related warnings for receivers

#KT-6723 In Progress
This commit is contained in:
Andrey Breslav
2015-02-03 18:18:41 +03:00
parent 5db6bb04e3
commit fcac449c70
14 changed files with 143 additions and 23 deletions
@@ -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
))
}
}
}
} }
@@ -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;
} }
@@ -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<*>
)
} }
@@ -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
} }
@@ -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
@@ -1,3 +1,5 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: p/J.java // FILE: p/J.java
package p; package p;
@@ -24,9 +26,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()
}
platformNN.bar() platformNN.bar()
platformN.bar() platformN.bar()
platformJ.bar() platformJ.bar()
@@ -34,3 +46,4 @@ fun test() {
fun J.foo() {} fun J.foo() {}
fun J?.bar() {} fun J?.bar() {}
fun <T> with(t: T, f: T.() -> Unit) {}
@@ -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
@@ -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,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()
} }
@@ -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
} }