SenselessComparisonChecker used for @NotNull values from Java
This commit is contained in:
+25
-7
@@ -62,6 +62,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability
|
|||||||
import org.jetbrains.kotlin.psi.JetPostfixExpression
|
import org.jetbrains.kotlin.psi.JetPostfixExpression
|
||||||
import org.jetbrains.kotlin.psi.JetBinaryExpression
|
import org.jetbrains.kotlin.psi.JetBinaryExpression
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.types.expressions.SenselessComparisonChecker
|
||||||
|
|
||||||
public object KotlinJvmCheckerProvider : AdditionalCheckerProvider(
|
public object KotlinJvmCheckerProvider : AdditionalCheckerProvider(
|
||||||
annotationCheckers = listOf(PlatformStaticAnnotationChecker(), LocalFunInlineChecker(), ReifiedTypeParameterAnnotationChecker(), NativeFunChecker()),
|
annotationCheckers = listOf(PlatformStaticAnnotationChecker(), LocalFunInlineChecker(), ReifiedTypeParameterAnnotationChecker(), NativeFunChecker()),
|
||||||
@@ -202,7 +203,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
|
|||||||
if (expression.getOperationToken() == JetTokens.EXCLEXCL) {
|
if (expression.getOperationToken() == JetTokens.EXCLEXCL) {
|
||||||
val baseExpression = expression.getBaseExpression()
|
val baseExpression = expression.getBaseExpression()
|
||||||
val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return
|
val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return
|
||||||
warnIfNotNull(
|
doIfNotNull(
|
||||||
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c.trace.getBindingContext()),
|
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c.trace.getBindingContext()),
|
||||||
c
|
c
|
||||||
) {
|
) {
|
||||||
@@ -210,25 +211,42 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is JetBinaryExpression ->
|
is JetBinaryExpression ->
|
||||||
if (expression.getOperationToken() == JetTokens.ELVIS) {
|
when (expression.getOperationToken()) {
|
||||||
|
JetTokens.ELVIS -> {
|
||||||
val baseExpression = expression.getLeft()
|
val baseExpression = expression.getLeft()
|
||||||
val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return
|
val baseExpressionType = c.trace.get(BindingContext.EXPRESSION_TYPE, baseExpression) ?: return
|
||||||
warnIfNotNull(
|
doIfNotNull(
|
||||||
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c.trace.getBindingContext()),
|
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c.trace.getBindingContext()),
|
||||||
c
|
c
|
||||||
) {
|
) {
|
||||||
c.trace.report(Errors.USELESS_ELVIS.on(expression.getOperationReference(), baseExpressionType))
|
c.trace.report(Errors.USELESS_ELVIS.on(expression.getOperationReference(), baseExpressionType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
JetTokens.EQEQ,
|
||||||
|
JetTokens.EXCLEQ,
|
||||||
|
JetTokens.EQEQEQ,
|
||||||
|
JetTokens.EXCLEQEQEQ -> {
|
||||||
|
if (expression.getLeft() != null && expression.getRight() != null) {
|
||||||
|
SenselessComparisonChecker.checkSenselessComparisonWithNull(
|
||||||
|
expression, expression.getLeft(), expression.getRight(), c.trace,
|
||||||
|
{ c.trace.get(BindingContext.EXPRESSION_TYPE, it) },
|
||||||
|
{
|
||||||
|
value ->
|
||||||
|
doIfNotNull(value, c) { Nullability.NOT_NULL } ?: Nullability.UNKNOWN
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun warnIfNotNull(dataFlowValue: DataFlowValue, c: ResolutionContext<*>, reportWarning: () -> Unit) {
|
private fun <T: Any> doIfNotNull(dataFlowValue: DataFlowValue, c: ResolutionContext<*>, body: () -> T): T? {
|
||||||
if (c.dataFlowInfo.getNullability(dataFlowValue).canBeNull()
|
if (c.dataFlowInfo.getNullability(dataFlowValue).canBeNull()
|
||||||
&& dataFlowValue.getType().mustNotBeNull() == NullabilityInformationSource.JAVA) {
|
&& dataFlowValue.getType().mustNotBeNull() == NullabilityInformationSource.JAVA) {
|
||||||
reportWarning()
|
return body()
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun checkReceiver(
|
override fun checkReceiver(
|
||||||
@@ -261,7 +279,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// TODO: Compiler bug
|
// TODO: Compiler bug
|
||||||
warnIfNotNull(dataFlowValue, c as ResolutionContext<*>) {
|
doIfNotNull(dataFlowValue, c as ResolutionContext<*>) {
|
||||||
c.trace.report(Errors.UNNECESSARY_SAFE_CALL.on(c.call.getCallOperationNode().getPsi(), receiverArgument.getType()))
|
c.trace.report(Errors.UNNECESSARY_SAFE_CALL.on(c.call.getCallOperationNode().getPsi(), receiverArgument.getType()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -1152,7 +1152,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
context.trace.report(EQUALITY_NOT_APPLICABLE.on(expression, expression.getOperationReference(), leftType, rightType));
|
context.trace.report(EQUALITY_NOT_APPLICABLE.on(expression, expression.getOperationReference(), leftType, rightType));
|
||||||
}
|
}
|
||||||
SenselessComparisonChecker.checkSenselessComparisonWithNull(
|
SenselessComparisonChecker.checkSenselessComparisonWithNull(
|
||||||
expression, left, right, context,
|
expression, left, right, context.trace,
|
||||||
new Function1<JetExpression, JetType>() {
|
new Function1<JetExpression, JetType>() {
|
||||||
@Override
|
@Override
|
||||||
public JetType invoke(JetExpression expression) {
|
public JetType invoke(JetExpression expression) {
|
||||||
@@ -1164,8 +1164,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
public Nullability invoke(DataFlowValue value) {
|
public Nullability invoke(DataFlowValue value) {
|
||||||
return context.dataFlowInfo.getNullability(value);
|
return context.dataFlowInfo.getNullability(value);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -24,13 +24,14 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability
|
|||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import kotlin.platform.platformStatic
|
import kotlin.platform.platformStatic
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
|
|
||||||
object SenselessComparisonChecker {
|
object SenselessComparisonChecker {
|
||||||
platformStatic fun checkSenselessComparisonWithNull(
|
platformStatic fun checkSenselessComparisonWithNull(
|
||||||
expression: JetBinaryExpression,
|
expression: JetBinaryExpression,
|
||||||
left: JetExpression,
|
left: JetExpression,
|
||||||
right: JetExpression,
|
right: JetExpression,
|
||||||
context: ExpressionTypingContext,
|
trace: BindingTrace,
|
||||||
getType: (JetExpression) -> JetType?,
|
getType: (JetExpression) -> JetType?,
|
||||||
getNullability: (DataFlowValue) -> Nullability
|
getNullability: (DataFlowValue) -> Nullability
|
||||||
) {
|
) {
|
||||||
@@ -43,10 +44,10 @@ object SenselessComparisonChecker {
|
|||||||
if (type == null || type.isError()) return
|
if (type == null || type.isError()) return
|
||||||
|
|
||||||
val operationSign = expression.getOperationReference()
|
val operationSign = expression.getOperationReference()
|
||||||
val value = DataFlowValueFactory.createDataFlowValue(expr, type, context.trace.getBindingContext())
|
val value = DataFlowValueFactory.createDataFlowValue(expr, type, trace.getBindingContext())
|
||||||
val nullability = getNullability(value)
|
|
||||||
|
|
||||||
val equality = operationSign.getReferencedNameElementType() == JetTokens.EQEQ || operationSign.getReferencedNameElementType() == JetTokens.EQEQEQ
|
val equality = operationSign.getReferencedNameElementType() == JetTokens.EQEQ || operationSign.getReferencedNameElementType() == JetTokens.EQEQEQ
|
||||||
|
val nullability = getNullability(value)
|
||||||
|
|
||||||
val expressionIsAlways =
|
val expressionIsAlways =
|
||||||
if (nullability == Nullability.NULL) equality
|
if (nullability == Nullability.NULL) equality
|
||||||
@@ -54,6 +55,6 @@ object SenselessComparisonChecker {
|
|||||||
else if (nullability == Nullability.IMPOSSIBLE) false
|
else if (nullability == Nullability.IMPOSSIBLE) false
|
||||||
else return
|
else return
|
||||||
|
|
||||||
context.trace.report(Errors.SENSELESS_COMPARISON.on(expression, expression, expressionIsAlways))
|
trace.report(Errors.SENSELESS_COMPARISON.on(expression, expression, expressionIsAlways))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
// !DIAGNOSTICS: -UNUSED_VARIABLE -SENSELESS_COMPARISON
|
||||||
|
|
||||||
// FILE: p/J.java
|
// FILE: p/J.java
|
||||||
package p;
|
package p;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// !DIAGNOSTICS: -SENSELESS_COMPARISON
|
||||||
|
|
||||||
// FILE: p/J.java
|
// FILE: p/J.java
|
||||||
package p;
|
package p;
|
||||||
|
|
||||||
|
|||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_EXPRESSION
|
||||||
|
|
||||||
|
// FILE: p/J.java
|
||||||
|
package p;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.*;
|
||||||
|
|
||||||
|
public class J {
|
||||||
|
@NotNull
|
||||||
|
public static J staticNN;
|
||||||
|
@Nullable
|
||||||
|
public static J staticN;
|
||||||
|
public static J staticJ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: k.kt
|
||||||
|
|
||||||
|
import p.*
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
// @NotNull platform type
|
||||||
|
val platformNN = J.staticNN
|
||||||
|
// @Nullable platform type
|
||||||
|
val platformN = J.staticN
|
||||||
|
// platform type with no annotation
|
||||||
|
val platformJ = J.staticJ
|
||||||
|
|
||||||
|
val a: Any? = null
|
||||||
|
|
||||||
|
if (<!SENSELESS_COMPARISON!>platformNN != null<!>) {}
|
||||||
|
if (<!SENSELESS_COMPARISON!>null != platformNN<!>) {}
|
||||||
|
if (<!SENSELESS_COMPARISON!>platformNN == null<!>) {}
|
||||||
|
if (<!SENSELESS_COMPARISON!>null == platformNN<!>) {}
|
||||||
|
|
||||||
|
if (a != null && platformNN != a) {}
|
||||||
|
|
||||||
|
if (platformN != null) {}
|
||||||
|
if (platformN == null) {}
|
||||||
|
if (a == null && platformN == a) {}
|
||||||
|
|
||||||
|
if (platformJ != null) {}
|
||||||
|
if (platformJ == null) {}
|
||||||
|
if (a == null && platformJ == a) {}
|
||||||
|
}
|
||||||
|
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
internal fun test(): kotlin.Unit
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_EXPRESSION
|
||||||
|
|
||||||
|
// FILE: p/J.java
|
||||||
|
package p;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.*;
|
||||||
|
|
||||||
|
public class J {
|
||||||
|
@NotNull
|
||||||
|
public static J staticNN;
|
||||||
|
@Nullable
|
||||||
|
public static J staticN;
|
||||||
|
public static J staticJ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: k.kt
|
||||||
|
|
||||||
|
import p.*
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
// @NotNull platform type
|
||||||
|
val platformNN = J.staticNN
|
||||||
|
// @Nullable platform type
|
||||||
|
val platformN = J.staticN
|
||||||
|
// platform type with no annotation
|
||||||
|
val platformJ = J.staticJ
|
||||||
|
|
||||||
|
val a: Any? = null
|
||||||
|
|
||||||
|
if (<!SENSELESS_COMPARISON!>platformNN !== null<!>) {}
|
||||||
|
if (<!SENSELESS_COMPARISON!>null !== platformNN<!>) {}
|
||||||
|
if (<!SENSELESS_COMPARISON!>platformNN === null<!>) {}
|
||||||
|
if (<!SENSELESS_COMPARISON!>null === platformNN<!>) {}
|
||||||
|
|
||||||
|
if (platformN !== null) {}
|
||||||
|
if (platformN === null) {}
|
||||||
|
if (a === null && platformN === a) {}
|
||||||
|
|
||||||
|
if (platformJ !== null) {}
|
||||||
|
if (platformJ === null) {}
|
||||||
|
if (a === null && platformJ === a) {}
|
||||||
|
}
|
||||||
|
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
internal fun test(): kotlin.Unit
|
||||||
@@ -8664,6 +8664,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("senselessComparisonEquals.kt")
|
||||||
|
public void testSenselessComparisonEquals() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonEquals.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("senselessComparisonIdentityEquals.kt")
|
||||||
|
public void testSenselessComparisonIdentityEquals() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/senselessComparisonIdentityEquals.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("throw.kt")
|
@TestMetadata("throw.kt")
|
||||||
public void testThrow() throws Exception {
|
public void testThrow() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/throw.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/throw.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user