[FE 1.0] Report DUPLICATE_LABEL_IN_WHEN on proper branches
^KT-50385 Fixed
This commit is contained in:
committed by
teamcity
parent
d9a3dd09ea
commit
eb753eac83
+6
@@ -32929,6 +32929,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
runTest("compiler/testData/diagnostics/tests/when/ExhaustiveVarOverConditionalInit.kt");
|
runTest("compiler/testData/diagnostics/tests/when/ExhaustiveVarOverConditionalInit.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("exhaustiveWhenWithConstVal.kt")
|
||||||
|
public void testExhaustiveWhenWithConstVal() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/when/exhaustiveWhenWithConstVal.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
|
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
|
||||||
public void testExhaustiveWithNullabilityCheck() throws Exception {
|
public void testExhaustiveWithNullabilityCheck() throws Exception {
|
||||||
|
|||||||
+6
@@ -32929,6 +32929,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/when/ExhaustiveVarOverConditionalInit.kt");
|
runTest("compiler/testData/diagnostics/tests/when/ExhaustiveVarOverConditionalInit.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("exhaustiveWhenWithConstVal.kt")
|
||||||
|
public void testExhaustiveWhenWithConstVal() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/when/exhaustiveWhenWithConstVal.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
|
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
|
||||||
public void testExhaustiveWithNullabilityCheck() throws Exception {
|
public void testExhaustiveWithNullabilityCheck() throws Exception {
|
||||||
|
|||||||
+6
@@ -32929,6 +32929,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/when/ExhaustiveVarOverConditionalInit.kt");
|
runTest("compiler/testData/diagnostics/tests/when/ExhaustiveVarOverConditionalInit.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("exhaustiveWhenWithConstVal.kt")
|
||||||
|
public void testExhaustiveWhenWithConstVal() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/when/exhaustiveWhenWithConstVal.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
|
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
|
||||||
public void testExhaustiveWithNullabilityCheck() throws Exception {
|
public void testExhaustiveWithNullabilityCheck() throws Exception {
|
||||||
|
|||||||
@@ -361,7 +361,30 @@ object WhenChecker {
|
|||||||
if (expression.subjectExpression == null) return
|
if (expression.subjectExpression == null) return
|
||||||
|
|
||||||
val checkedTypes = HashSet<Pair<KotlinType, Boolean>>()
|
val checkedTypes = HashSet<Pair<KotlinType, Boolean>>()
|
||||||
val checkedConstants = HashSet<CompileTimeConstant<*>>()
|
/*
|
||||||
|
* `true` in map means that constant can be removed and nothing breaks
|
||||||
|
* `false` means opposite
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* const val myF = false
|
||||||
|
* const val myT = true
|
||||||
|
*
|
||||||
|
* fun test_1(someBoolean: Boolean) {
|
||||||
|
* val s = when (someBoolean) {
|
||||||
|
* myT -> 1
|
||||||
|
* myF -> 2
|
||||||
|
* true -> 3 // DUPLICATE_LABEL_IN_WHEN
|
||||||
|
* false -> 4 // DUPLICATE_LABEL_IN_WHEN
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* In this case myT and myF actually are `true` and `false` correspondingly, but
|
||||||
|
* const vals are not treated by exhaustive checkers, so removal `true` or `false`
|
||||||
|
* branches will break code, so we need to report DUPLICATE_LABEL_IN_WHEN on `myT` and
|
||||||
|
* `myF`, not on `true` and `false`
|
||||||
|
*/
|
||||||
|
val checkedConstants = mutableMapOf<CompileTimeConstant<*>, Boolean>()
|
||||||
|
val notTrivialBranches = mutableMapOf<CompileTimeConstant<*>, KtExpression>()
|
||||||
for (entry in expression.entries) {
|
for (entry in expression.entries) {
|
||||||
if (entry.isElse) continue
|
if (entry.isElse) continue
|
||||||
|
|
||||||
@@ -372,10 +395,37 @@ object WhenChecker {
|
|||||||
val constant = ConstantExpressionEvaluator.getConstant(
|
val constant = ConstantExpressionEvaluator.getConstant(
|
||||||
constantExpression, trace.bindingContext
|
constantExpression, trace.bindingContext
|
||||||
) ?: continue@conditions
|
) ?: continue@conditions
|
||||||
if (checkedConstants.contains(constant)) {
|
|
||||||
trace.report(Errors.DUPLICATE_LABEL_IN_WHEN.on(constantExpression))
|
fun report(reportOn: KtExpression) {
|
||||||
} else {
|
trace.report(Errors.DUPLICATE_LABEL_IN_WHEN.on(reportOn))
|
||||||
checkedConstants.add(constant)
|
}
|
||||||
|
|
||||||
|
when (checkedConstants[constant]) {
|
||||||
|
true -> {
|
||||||
|
// already found trivial constant in previous branches
|
||||||
|
report(constantExpression)
|
||||||
|
}
|
||||||
|
false -> {
|
||||||
|
// already found bad constant in previous branches
|
||||||
|
val isTrivial = constant.isTrivial()
|
||||||
|
if (isTrivial) {
|
||||||
|
// this constant is trivial -> report on first non trivial constant
|
||||||
|
val reportOn = notTrivialBranches.remove(constant)!!
|
||||||
|
report(reportOn)
|
||||||
|
checkedConstants[constant] = true
|
||||||
|
} else {
|
||||||
|
// this constant is also not trivial -> report on it
|
||||||
|
report(constantExpression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null -> {
|
||||||
|
// met constant for a first time
|
||||||
|
val isTrivial = constant.isTrivial()
|
||||||
|
checkedConstants[constant] = isTrivial
|
||||||
|
if (!isTrivial) {
|
||||||
|
notTrivialBranches[constant] = constantExpression
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -394,7 +444,10 @@ object WhenChecker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CompileTimeConstant<*>.isTrivial(): Boolean {
|
||||||
|
return !usesVariableAsConstant
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkDeprecatedWhenSyntax(trace: BindingTrace, expression: KtWhenExpression) {
|
fun checkDeprecatedWhenSyntax(trace: BindingTrace, expression: KtWhenExpression) {
|
||||||
|
|||||||
+2
-2
@@ -40,8 +40,8 @@ fun test() {
|
|||||||
val s = "";
|
val s = "";
|
||||||
|
|
||||||
when (x) {
|
when (x) {
|
||||||
<!INCOMPATIBLE_TYPES!>s<!> -> 1
|
<!DUPLICATE_LABEL_IN_WHEN, INCOMPATIBLE_TYPES!>s<!> -> 1
|
||||||
<!DUPLICATE_LABEL_IN_WHEN, INCOMPATIBLE_TYPES!>""<!> -> 1
|
<!INCOMPATIBLE_TYPES!>""<!> -> 1
|
||||||
x -> 1
|
x -> 1
|
||||||
1 -> 1
|
1 -> 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// ISSUE: KT-50385
|
||||||
|
|
||||||
|
const val myF = false
|
||||||
|
const val myT = true
|
||||||
|
|
||||||
|
fun test_1(someBoolean: Boolean) {
|
||||||
|
val s = when (someBoolean) {
|
||||||
|
myT /* true */ -> 1
|
||||||
|
myF /* false */ -> 2
|
||||||
|
true -> 3
|
||||||
|
false -> 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_2(someBoolean: Boolean) {
|
||||||
|
val s = when (someBoolean) {
|
||||||
|
myT /* true */ -> 1
|
||||||
|
true -> 2
|
||||||
|
false -> 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_3(someBoolean: Boolean) {
|
||||||
|
val s = <!NO_ELSE_IN_WHEN!>when<!> (someBoolean) {
|
||||||
|
myT /* true */ -> 1
|
||||||
|
false -> 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_4(someBoolean: Boolean) {
|
||||||
|
val s = <!NO_ELSE_IN_WHEN!>when<!> (someBoolean) {
|
||||||
|
myT /* true */ -> 1
|
||||||
|
myT /* true */ -> 2
|
||||||
|
false -> 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_5(someBoolean: Boolean) {
|
||||||
|
val s = <!NO_ELSE_IN_WHEN!>when<!> (someBoolean) {
|
||||||
|
myT /* true */ -> 1
|
||||||
|
myT /* true */ -> 2
|
||||||
|
myT /* true */ -> 3
|
||||||
|
false -> 4
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// ISSUE: KT-50385
|
||||||
|
|
||||||
|
const val myF = false
|
||||||
|
const val myT = true
|
||||||
|
|
||||||
|
fun test_1(someBoolean: Boolean) {
|
||||||
|
val s = when (someBoolean) {
|
||||||
|
<!DUPLICATE_LABEL_IN_WHEN!>myT<!> /* true */ -> 1
|
||||||
|
<!DUPLICATE_LABEL_IN_WHEN!>myF<!> /* false */ -> 2
|
||||||
|
true -> 3
|
||||||
|
false -> 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_2(someBoolean: Boolean) {
|
||||||
|
val s = when (someBoolean) {
|
||||||
|
<!DUPLICATE_LABEL_IN_WHEN!>myT<!> /* true */ -> 1
|
||||||
|
true -> 2
|
||||||
|
false -> 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_3(someBoolean: Boolean) {
|
||||||
|
val s = <!NO_ELSE_IN_WHEN!>when<!> (someBoolean) {
|
||||||
|
myT /* true */ -> 1
|
||||||
|
false -> 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_4(someBoolean: Boolean) {
|
||||||
|
val s = <!NO_ELSE_IN_WHEN!>when<!> (someBoolean) {
|
||||||
|
myT /* true */ -> 1
|
||||||
|
<!DUPLICATE_LABEL_IN_WHEN!>myT<!> /* true */ -> 2
|
||||||
|
false -> 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test_5(someBoolean: Boolean) {
|
||||||
|
val s = <!NO_ELSE_IN_WHEN!>when<!> (someBoolean) {
|
||||||
|
myT /* true */ -> 1
|
||||||
|
<!DUPLICATE_LABEL_IN_WHEN!>myT<!> /* true */ -> 2
|
||||||
|
<!DUPLICATE_LABEL_IN_WHEN!>myT<!> /* true */ -> 3
|
||||||
|
false -> 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public const val myF: kotlin.Boolean = false
|
||||||
|
public const val myT: kotlin.Boolean = true
|
||||||
|
public fun test_1(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit
|
||||||
|
public fun test_2(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit
|
||||||
|
public fun test_3(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit
|
||||||
|
public fun test_4(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit
|
||||||
|
public fun test_5(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit
|
||||||
|
|
||||||
Generated
+6
@@ -33025,6 +33025,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/when/ExhaustiveVarOverConditionalInit.kt");
|
runTest("compiler/testData/diagnostics/tests/when/ExhaustiveVarOverConditionalInit.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("exhaustiveWhenWithConstVal.kt")
|
||||||
|
public void testExhaustiveWhenWithConstVal() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/when/exhaustiveWhenWithConstVal.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
|
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
|
||||||
public void testExhaustiveWithNullabilityCheck() throws Exception {
|
public void testExhaustiveWithNullabilityCheck() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user