[FE 1.0] Report DUPLICATE_LABEL_IN_WHEN on proper branches

^KT-50385 Fixed
This commit is contained in:
Dmitriy Novozhilov
2021-12-19 22:34:09 +03:00
committed by teamcity
parent d9a3dd09ea
commit eb753eac83
9 changed files with 185 additions and 7 deletions
@@ -32929,6 +32929,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
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
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
public void testExhaustiveWithNullabilityCheck() throws Exception {
@@ -32929,6 +32929,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
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
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
public void testExhaustiveWithNullabilityCheck() throws Exception {
@@ -32929,6 +32929,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
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
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
public void testExhaustiveWithNullabilityCheck() throws Exception {
@@ -361,7 +361,30 @@ object WhenChecker {
if (expression.subjectExpression == null) return
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) {
if (entry.isElse) continue
@@ -372,10 +395,37 @@ object WhenChecker {
val constant = ConstantExpressionEvaluator.getConstant(
constantExpression, trace.bindingContext
) ?: continue@conditions
if (checkedConstants.contains(constant)) {
trace.report(Errors.DUPLICATE_LABEL_IN_WHEN.on(constantExpression))
} else {
checkedConstants.add(constant)
fun report(reportOn: KtExpression) {
trace.report(Errors.DUPLICATE_LABEL_IN_WHEN.on(reportOn))
}
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) {
+2 -2
View File
@@ -40,8 +40,8 @@ fun test() {
val s = "";
when (x) {
<!INCOMPATIBLE_TYPES!>s<!> -> 1
<!DUPLICATE_LABEL_IN_WHEN, INCOMPATIBLE_TYPES!>""<!> -> 1
<!DUPLICATE_LABEL_IN_WHEN, INCOMPATIBLE_TYPES!>s<!> -> 1
<!INCOMPATIBLE_TYPES!>""<!> -> 1
x -> 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
@@ -33025,6 +33025,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
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
@TestMetadata("ExhaustiveWithNullabilityCheck.kt")
public void testExhaustiveWithNullabilityCheck() throws Exception {