[FE 1.0] Prohibit using non exhaustive if and when in rhs of elvis expression
^KT-44705 ^KT-49349 Fixed
This commit is contained in:
committed by
teamcityserver
parent
06a26a5a74
commit
a2b8493f47
+12
@@ -6289,6 +6289,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nestedLoopsWithMultipleLabels.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonExhaustiveIfInElvis_after.kt")
|
||||
public void testNonExhaustiveIfInElvis_after() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nonExhaustiveIfInElvis_after.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonExhaustiveIfInElvis_before.kt")
|
||||
public void testNonExhaustiveIfInElvis_before() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nonExhaustiveIfInElvis_before.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("notAFunctionLabel_after.kt")
|
||||
public void testNotAFunctionLabel_after() throws Exception {
|
||||
|
||||
+12
@@ -6289,6 +6289,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nestedLoopsWithMultipleLabels.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonExhaustiveIfInElvis_after.kt")
|
||||
public void testNonExhaustiveIfInElvis_after() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nonExhaustiveIfInElvis_after.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonExhaustiveIfInElvis_before.kt")
|
||||
public void testNonExhaustiveIfInElvis_before() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nonExhaustiveIfInElvis_before.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("notAFunctionLabel_after.kt")
|
||||
public void testNotAFunctionLabel_after() throws Exception {
|
||||
|
||||
+12
@@ -6289,6 +6289,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nestedLoopsWithMultipleLabels.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonExhaustiveIfInElvis_after.kt")
|
||||
public void testNonExhaustiveIfInElvis_after() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nonExhaustiveIfInElvis_after.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonExhaustiveIfInElvis_before.kt")
|
||||
public void testNonExhaustiveIfInElvis_before() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nonExhaustiveIfInElvis_before.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("notAFunctionLabel_after.kt")
|
||||
public void testNotAFunctionLabel_after() throws Exception {
|
||||
|
||||
+30
-5
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.cfg
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil.getParentOfType
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.cfg.TailRecursionKind.*
|
||||
@@ -43,14 +44,10 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.getEnclosingDescriptor
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsResultOfLambda
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.findDestructuredVariable
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getDispatchReceiverWithSmartCast
|
||||
import org.jetbrains.kotlin.resolve.calls.util.hasThisOrNoDispatchReceiver
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||
import org.jetbrains.kotlin.resolve.calls.util.isSingleUnderscore
|
||||
import org.jetbrains.kotlin.resolve.calls.util.*
|
||||
import org.jetbrains.kotlin.resolve.checkers.PlatformDiagnosticSuppressor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
@@ -943,6 +940,13 @@ class ControlFlowInformationProviderImpl private constructor(
|
||||
} else {
|
||||
checkImplicitCastOnConditionalExpression(element)
|
||||
}
|
||||
} else if (!languageVersionSettings.supportsFeature(LanguageFeature.ProhibitNonExhaustiveIfInRhsOfElvis)) {
|
||||
val parent = element.deparenthesizedParent
|
||||
if (parent is KtBinaryExpression) {
|
||||
if (parent.operationToken === KtTokens.ELVIS) {
|
||||
trace.report(INVALID_IF_AS_EXPRESSION_WARNING.on(element.getIfKeyword()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1036,6 +1040,18 @@ class ControlFlowInformationProviderImpl private constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
!usedAsExpression &&
|
||||
missingCases.isNotEmpty() &&
|
||||
!languageVersionSettings.supportsFeature(LanguageFeature.ProhibitNonExhaustiveIfInRhsOfElvis)
|
||||
) {
|
||||
val parent = element.deparenthesizedParent
|
||||
if (parent is KtBinaryExpression) {
|
||||
if (parent.operationToken === KtTokens.ELVIS) {
|
||||
trace.report(NO_ELSE_IN_WHEN_WARNING.on(element, missingCases))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1355,4 +1371,13 @@ class ControlFlowInformationProviderImpl private constructor(
|
||||
|| diagnosticFactory === UNUSED_ANONYMOUS_PARAMETER
|
||||
|| diagnosticFactory === UNUSED_CHANGED_VALUE
|
||||
}
|
||||
|
||||
private val PsiElement.deparenthesizedParent: PsiElement
|
||||
get() {
|
||||
var result = parent
|
||||
while (result is KtParenthesizedExpression || result is KtLabeledExpression || result is KtAnnotatedExpression) {
|
||||
result = result.parent
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,7 +346,10 @@ class ControlFlowProcessor(
|
||||
builder.jumpOnTrue(afterElvis, expression, builder.getBoundValue(left))
|
||||
generateInstructions(right)
|
||||
builder.bindLabel(afterElvis)
|
||||
mergeValues(listOf(left, right).filterNotNull(), expression)
|
||||
mergeValues(listOfNotNull(left, right), expression)
|
||||
if (right != null && languageVersionSettings?.supportsFeature(LanguageFeature.ProhibitNonExhaustiveIfInRhsOfElvis) == true) {
|
||||
trace.record(USED_AS_EXPRESSION, right, true)
|
||||
}
|
||||
} else {
|
||||
if (!generateCall(expression)) {
|
||||
generateBothArgumentsAndMark(expression)
|
||||
|
||||
@@ -997,6 +997,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtElement> SENSELESS_NULL_IN_WHEN = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory0<PsiElement> INVALID_IF_AS_EXPRESSION = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> INVALID_IF_AS_EXPRESSION_WARNING = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactoryForDeprecation0<PsiElement> CONFUSING_BRANCH_CONDITION = DiagnosticFactoryForDeprecation0.create(LanguageFeature.ProhibitConfusingSyntaxInWhenBranches);
|
||||
|
||||
// Nullability
|
||||
@@ -1079,6 +1080,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtWhenEntry> ELSE_MISPLACED_IN_WHEN = DiagnosticFactory0.create(ERROR, ELSE_ENTRY);
|
||||
DiagnosticFactory0<KtWhenEntry> REDUNDANT_ELSE_IN_WHEN = DiagnosticFactory0.create(WARNING, ELSE_ENTRY);
|
||||
DiagnosticFactory1<KtWhenExpression, List<WhenMissingCase>> NO_ELSE_IN_WHEN = DiagnosticFactory1.create(ERROR, WHEN_EXPRESSION);
|
||||
DiagnosticFactory1<KtWhenExpression, List<WhenMissingCase>> NO_ELSE_IN_WHEN_WARNING = DiagnosticFactory1.create(WARNING, WHEN_EXPRESSION);
|
||||
DiagnosticFactory1<KtWhenExpression, List<WhenMissingCase>> NON_EXHAUSTIVE_WHEN = DiagnosticFactory1.create(WARNING, WHEN_EXPRESSION);
|
||||
DiagnosticFactory2<KtWhenExpression, String, List<WhenMissingCase>> NON_EXHAUSTIVE_WHEN_STATEMENT = DiagnosticFactory2.create(WARNING, WHEN_EXPRESSION);
|
||||
DiagnosticFactory1<KtWhenExpression, List<WhenMissingCase>>
|
||||
|
||||
+2
@@ -632,6 +632,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(ILLEGAL_DECLARATION_IN_WHEN_SUBJECT, "Illegal variable declaration in 'when' subject: {0}. Should be a simple val with an initializer", STRING);
|
||||
|
||||
MAP.put(NO_ELSE_IN_WHEN, "''when'' expression must be exhaustive, add necessary {0}", RENDER_WHEN_MISSING_CASES);
|
||||
MAP.put(NO_ELSE_IN_WHEN_WARNING, "''when'' expression must be exhaustive, add necessary {0}", RENDER_WHEN_MISSING_CASES);
|
||||
MAP.put(NON_EXHAUSTIVE_WHEN, "''when'' expression on enum is recommended to be exhaustive, add {0}", RENDER_WHEN_MISSING_CASES);
|
||||
MAP.put(NON_EXHAUSTIVE_WHEN_STATEMENT, "Non exhaustive ''when'' statements on {0} will be prohibited in 1.7, add {1}", STRING, RENDER_WHEN_MISSING_CASES);
|
||||
MAP.put(NON_EXHAUSTIVE_WHEN_ON_SEALED_CLASS, "''when'' expression on sealed classes is recommended to be exhaustive, add {0}", RENDER_WHEN_MISSING_CASES);
|
||||
@@ -833,6 +834,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(SENSELESS_NULL_IN_WHEN, "Expression under 'when' is never equal to null");
|
||||
|
||||
MAP.put(INVALID_IF_AS_EXPRESSION, "'if' must have both main and 'else' branches if used as an expression");
|
||||
MAP.put(INVALID_IF_AS_EXPRESSION_WARNING, "'if' must have both main and 'else' branches if used as an expression");
|
||||
MAP.put(CONFUSING_BRANCH_CONDITION, "The logical expressions may be understood ambiguously in when with subject branches. Please wrap it with parenthesis");
|
||||
|
||||
MAP.put(OVERRIDING_FINAL_MEMBER, "''{0}'' in ''{1}'' is final and cannot be overridden", NAME, NAME);
|
||||
|
||||
@@ -56,8 +56,8 @@ fun g6(): Unit = <!TYPE_MISMATCH!><!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }<!>
|
||||
fun foo1(x: String?) {
|
||||
"" + <!INVALID_IF_AS_EXPRESSION!>if<!> (true) 42
|
||||
w@while (true) {
|
||||
x ?: if (true) break
|
||||
x ?: when { true -> break@w }
|
||||
x ?: <!INVALID_IF_AS_EXPRESSION_WARNING!>if<!> (true) break
|
||||
x ?: <!NO_ELSE_IN_WHEN_WARNING!>when<!> { true -> break@w }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// FIR_IDENTICAL
|
||||
// LANGUAGE: +ProhibitNonExhaustiveIfInRhsOfElvis
|
||||
// DIAGNOSTICS: -USELESS_ELVIS
|
||||
// ISSUE: KT-44705
|
||||
|
||||
fun test_1(x: String?, y: String?) {
|
||||
while (true) {
|
||||
x ?: <!INVALID_IF_AS_EXPRESSION!>if<!> (y == null) break
|
||||
}
|
||||
}
|
||||
|
||||
fun test_2(x: String?, y: String?) {
|
||||
while (true) {
|
||||
val z = x ?: <!INVALID_IF_AS_EXPRESSION!>if<!> (y == null) break
|
||||
}
|
||||
}
|
||||
|
||||
fun test_3(x: String?, y: String?) {
|
||||
while (true) {
|
||||
x ?: <!NO_ELSE_IN_WHEN!>when<!> { true -> break }
|
||||
x ?: <!NO_ELSE_IN_WHEN!>when<!> (y) { "hello" -> break }
|
||||
}
|
||||
}
|
||||
|
||||
fun test_4(x: String?, y: String?) {
|
||||
while (true) {
|
||||
val a = x ?: <!NO_ELSE_IN_WHEN!>when<!> { true -> break }
|
||||
val b = x ?: <!NO_ELSE_IN_WHEN!>when<!> (y) { "hello" -> break }
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package
|
||||
|
||||
public fun test_1(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Unit
|
||||
public fun test_2(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Unit
|
||||
public fun test_3(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Unit
|
||||
public fun test_4(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Unit
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// LANGUAGE: -ProhibitNonExhaustiveIfInRhsOfElvis
|
||||
// DIAGNOSTICS: -USELESS_ELVIS
|
||||
// ISSUE: KT-44705
|
||||
|
||||
fun test_1(x: String?, y: String?) {
|
||||
while (true) {
|
||||
x ?: <!INVALID_IF_AS_EXPRESSION!>if<!> (y == null) break
|
||||
}
|
||||
}
|
||||
|
||||
fun test_2(x: String?, y: String?) {
|
||||
while (true) {
|
||||
val z = x ?: <!INVALID_IF_AS_EXPRESSION!>if<!> (y == null) break
|
||||
}
|
||||
}
|
||||
|
||||
fun test_3(x: String?, y: String?) {
|
||||
while (true) {
|
||||
x ?: <!NO_ELSE_IN_WHEN!>when<!> { true -> break }
|
||||
x ?: <!NO_ELSE_IN_WHEN!>when<!> (y) { "hello" -> break }
|
||||
}
|
||||
}
|
||||
|
||||
fun test_4(x: String?, y: String?) {
|
||||
while (true) {
|
||||
val a = x ?: <!NO_ELSE_IN_WHEN!>when<!> { true -> break }
|
||||
val b = x ?: <!NO_ELSE_IN_WHEN!>when<!> (y) { "hello" -> break }
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// LANGUAGE: -ProhibitNonExhaustiveIfInRhsOfElvis
|
||||
// DIAGNOSTICS: -USELESS_ELVIS
|
||||
// ISSUE: KT-44705
|
||||
|
||||
fun test_1(x: String?, y: String?) {
|
||||
while (true) {
|
||||
x ?: <!INVALID_IF_AS_EXPRESSION_WARNING!>if<!> (y == null) break
|
||||
}
|
||||
}
|
||||
|
||||
fun test_2(x: String?, y: String?) {
|
||||
while (true) {
|
||||
val z = x ?: <!INVALID_IF_AS_EXPRESSION!>if<!> (y == null) break
|
||||
}
|
||||
}
|
||||
|
||||
fun test_3(x: String?, y: String?) {
|
||||
while (true) {
|
||||
x ?: <!NO_ELSE_IN_WHEN_WARNING!>when<!> { true -> break }
|
||||
x ?: <!NO_ELSE_IN_WHEN_WARNING!>when<!> (y) { "hello" -> break }
|
||||
}
|
||||
}
|
||||
|
||||
fun test_4(x: String?, y: String?) {
|
||||
while (true) {
|
||||
val a = x ?: <!NO_ELSE_IN_WHEN!>when<!> { true -> break }
|
||||
val b = x ?: <!NO_ELSE_IN_WHEN!>when<!> (y) { "hello" -> break }
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package
|
||||
|
||||
public fun test_1(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Unit
|
||||
public fun test_2(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Unit
|
||||
public fun test_3(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Unit
|
||||
public fun test_4(/*0*/ x: kotlin.String?, /*1*/ y: kotlin.String?): kotlin.Unit
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
public fun foo(x: String?, y: String?): Int {
|
||||
while (true) {
|
||||
x ?: if (y == null) break
|
||||
x ?: <!INVALID_IF_AS_EXPRESSION_WARNING!>if<!> (y == null) break
|
||||
// y is nullable if x != null
|
||||
y<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
|
||||
Generated
+12
@@ -6295,6 +6295,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nestedLoopsWithMultipleLabels.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonExhaustiveIfInElvis_after.kt")
|
||||
public void testNonExhaustiveIfInElvis_after() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nonExhaustiveIfInElvis_after.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonExhaustiveIfInElvis_before.kt")
|
||||
public void testNonExhaustiveIfInElvis_before() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlStructures/nonExhaustiveIfInElvis_before.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("notAFunctionLabel_after.kt")
|
||||
public void testNotAFunctionLabel_after() throws Exception {
|
||||
|
||||
@@ -235,6 +235,7 @@ enum class LanguageFeature(
|
||||
JvmPermittedSubclassesAttributeForSealed(KOTLIN_1_7),
|
||||
ForbidUsingExtensionPropertyTypeParameterInDelegate(KOTLIN_1_7, kind = BUG_FIX),
|
||||
ProhibitConfusingSyntaxInWhenBranches(KOTLIN_1_7, kind = BUG_FIX), // KT-48385
|
||||
ProhibitNonExhaustiveIfInRhsOfElvis(KOTLIN_1_7, kind = BUG_FIX), // KT-44705
|
||||
|
||||
// Temporarily disabled, see KT-27084/KT-22379
|
||||
SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX),
|
||||
|
||||
Reference in New Issue
Block a user