diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 7709a65aeea..1e6ccc47341 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -686,6 +686,8 @@ public interface Errors { DiagnosticFactory1 USELESS_ELVIS = DiagnosticFactory1.create(WARNING, PositioningStrategies.USELESS_ELVIS); DiagnosticFactory0 USELESS_ELVIS_ON_LAMBDA_EXPRESSION = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 USELESS_ELVIS_RIGHT_IS_NULL = + DiagnosticFactory0.create(WARNING, PositioningStrategies.USELESS_ELVIS); // Compile-time values diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 602c19ff51f..c46c63b193c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -418,6 +418,7 @@ public class DefaultErrorMessages { MAP.put(DYNAMIC_UPPER_BOUND, "Dynamic type can not be used as an upper bound"); MAP.put(USELESS_ELVIS, "Elvis operator (?:) always returns the left operand of non-nullable type {0}", RENDER_TYPE); MAP.put(USELESS_ELVIS_ON_LAMBDA_EXPRESSION, "Left operand of elvis operator (?:) is a lambda expression"); + MAP.put(USELESS_ELVIS_RIGHT_IS_NULL, "Right operand of elvis operator (?:) is useless if it is null"); MAP.put(CONFLICTING_UPPER_BOUNDS, "Upper bounds of {0} have empty intersection", NAME); MAP.put(UNSUPPORTED_TYPEALIAS, "Type aliases are unsupported (min Kotlin language level: 1.1)"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 17c5fd50ea3..8ac182d2d81 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -1198,6 +1198,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { assert rightTypeInfo != null : "Right expression was not processed: " + expression; boolean loopBreakContinuePossible = leftTypeInfo.getJumpOutPossible() || rightTypeInfo.getJumpOutPossible(); KotlinType rightType = rightTypeInfo.getType(); + if (rightType != null && KtPsiUtil.isNullConstant(right)) { + context.trace.report(USELESS_ELVIS_RIGHT_IS_NULL.on(expression)); + } // Only left argument DFA is taken into account here: we cannot be sure that right argument is joined // (we merge it with right DFA if right argument contains no jump outside) diff --git a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/uselessElvisRightIsNull.kt b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/uselessElvisRightIsNull.kt new file mode 100644 index 00000000000..e9210652925 --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/uselessElvisRightIsNull.kt @@ -0,0 +1,20 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +// FILE: J.java + +import org.jetbrains.annotations.*; + +public class J { + @Nullable + public static J staticN; +} + +// FILE: k.kt + +fun test() { + val a = J.staticN ?: null + foo(a) +} + +fun foo(a: Any?) { +} diff --git a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/uselessElvisRightIsNull.txt b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/uselessElvisRightIsNull.txt new file mode 100644 index 00000000000..0c664b0edb8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/uselessElvisRightIsNull.txt @@ -0,0 +1,14 @@ +package + +public fun foo(/*0*/ a: kotlin.Any?): kotlin.Unit +public fun test(): kotlin.Unit + +public open class J { + public constructor J() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + @org.jetbrains.annotations.Nullable() public final var staticN: J? +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 3d59db00e50..a60140aedb6 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -13855,6 +13855,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/uselessElvisInCall.kt"); doTest(fileName); } + + @TestMetadata("uselessElvisRightIsNull.kt") + public void testUselessElvisRightIsNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/uselessElvisRightIsNull.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/platformTypes/rawTypes") diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 5fae271190f..54054dd2219 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -98,6 +98,7 @@ class QuickFixRegistrar : QuickFixContributor { WRONG_GETTER_RETURN_TYPE.registerFactory(ChangeAccessorTypeFix) USELESS_ELVIS.registerFactory(RemoveUselessElvisFix) + USELESS_ELVIS_RIGHT_IS_NULL.registerFactory(RemoveUselessElvisFix) val removeRedundantModifierFactory = RemoveModifierFix.createRemoveModifierFactory(true) REDUNDANT_MODIFIER.registerFactory(removeRedundantModifierFactory)