[FE 1.0] Support proper DUPLICATE_LABEL_IN_WHEN reporting for deprecated complex boolean expressions

^KT-50385
^KT-39883
This commit is contained in:
Dmitriy Novozhilov
2021-12-19 22:43:42 +03:00
committed by teamcity
parent eb753eac83
commit 8a58e98530
12 changed files with 162 additions and 12 deletions
@@ -4942,6 +4942,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/divideByZero.kt");
}
@Test
@TestMetadata("duplicateLabelWithNonTrivialCondition_disabled.kt")
public void testDuplicateLabelWithNonTrivialCondition_disabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.kt");
}
@Test
@TestMetadata("duplicateLabelWithNonTrivialCondition_enabled.kt")
public void testDuplicateLabelWithNonTrivialCondition_enabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.kt");
}
@Test
@TestMetadata("equals.kt")
public void testEquals() throws Exception {
@@ -4942,6 +4942,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/divideByZero.kt");
}
@Test
@TestMetadata("duplicateLabelWithNonTrivialCondition_disabled.kt")
public void testDuplicateLabelWithNonTrivialCondition_disabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.kt");
}
@Test
@TestMetadata("duplicateLabelWithNonTrivialCondition_enabled.kt")
public void testDuplicateLabelWithNonTrivialCondition_enabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.kt");
}
@Test
@TestMetadata("equals.kt")
public void testEquals() throws Exception {
@@ -4942,6 +4942,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/divideByZero.kt");
}
@Test
@TestMetadata("duplicateLabelWithNonTrivialCondition_disabled.kt")
public void testDuplicateLabelWithNonTrivialCondition_disabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.kt");
}
@Test
@TestMetadata("duplicateLabelWithNonTrivialCondition_enabled.kt")
public void testDuplicateLabelWithNonTrivialCondition_enabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.kt");
}
@Test
@TestMetadata("equals.kt")
public void testEquals() throws Exception {
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValu
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.contracts.description.canBeRevisited
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
@@ -1321,7 +1322,11 @@ class ControlFlowProcessor(
builder.bindLabel(doneLabel)
mergeValues(branches, expression)
WhenChecker.checkDuplicatedLabels(expression, trace)
WhenChecker.checkDuplicatedLabels(
expression,
trace,
languageVersionSettings ?: LanguageVersionSettingsImpl.DEFAULT
)
}
override fun visitObjectLiteralExpression(expression: KtObjectLiteralExpression) {
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.cfg
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
@@ -357,7 +359,11 @@ object WhenChecker {
fun containsNullCase(expression: KtWhenExpression, context: BindingContext) =
WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, true).isEmpty()
fun checkDuplicatedLabels(expression: KtWhenExpression, trace: BindingTrace) {
fun checkDuplicatedLabels(
expression: KtWhenExpression,
trace: BindingTrace,
languageVersionSettings: LanguageVersionSettings,
) {
if (expression.subjectExpression == null) return
val checkedTypes = HashSet<Pair<KotlinType, Boolean>>()
@@ -407,7 +413,7 @@ object WhenChecker {
}
false -> {
// already found bad constant in previous branches
val isTrivial = constant.isTrivial()
val isTrivial = constant.isTrivial(constantExpression, languageVersionSettings)
if (isTrivial) {
// this constant is trivial -> report on first non trivial constant
val reportOn = notTrivialBranches.remove(constant)!!
@@ -420,7 +426,7 @@ object WhenChecker {
}
null -> {
// met constant for a first time
val isTrivial = constant.isTrivial()
val isTrivial = constant.isTrivial(constantExpression, languageVersionSettings)
checkedConstants[constant] = isTrivial
if (!isTrivial) {
notTrivialBranches[constant] = constantExpression
@@ -446,8 +452,15 @@ object WhenChecker {
}
}
private fun CompileTimeConstant<*>.isTrivial(): Boolean {
return !usesVariableAsConstant
private fun CompileTimeConstant<*>.isTrivial(
expression: KtExpression,
languageVersionSettings: LanguageVersionSettings
): Boolean {
if (usesVariableAsConstant) return false
if (!languageVersionSettings.supportsFeature(LanguageFeature.ProhibitSimplificationOfNonTrivialConstBooleanExpressions)) {
return !ConstantExpressionEvaluator.isComplexBooleanConstant(expression, this)
}
return true
}
fun checkDeprecatedWhenSyntax(trace: BindingTrace, expression: KtWhenExpression) {
@@ -51,7 +51,6 @@ import org.jetbrains.kotlin.types.isError
import org.jetbrains.kotlin.types.typeUtil.isBoolean
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import java.math.BigInteger
import java.util.*
@@ -369,6 +368,17 @@ class ConstantExpressionEvaluator(
KotlinBuiltIns.isArray(type) -> isTypeParameterOrArrayOfTypeParameter(type.arguments.singleOrNull()?.type)
else -> type.constructor.declarationDescriptor is TypeParameterDescriptor
}
fun isComplexBooleanConstant(
expression: KtExpression,
constant: CompileTimeConstant<*>
): Boolean {
if (constant.isError) return false
val constantValue = constant.toConstantValue(constant.moduleDescriptor.builtIns.booleanType)
if (!constantValue.getType(constant.moduleDescriptor).isBoolean()) return false
if (expression is KtConstantExpression || constant.parameters.usesVariableAsConstant) return false
return true
}
}
}
@@ -409,15 +419,13 @@ private class ConstantExpressionEvaluatorVisitor(
return null
}
@Suppress("warnings")
private fun shouldSkipComplexBooleanValue(
expression: KtExpression,
constant: CompileTimeConstant<*>
): Boolean {
if (constant.isError) return false
val constantValue = constant.toConstantValue(builtIns.booleanType)
if (!constantValue.getType(constantExpressionEvaluator.module).isBoolean()) return false
if (expression is KtConstantExpression || constant.parameters.usesVariableAsConstant) return false
if (!ConstantExpressionEvaluator.isComplexBooleanConstant(expression, constant)) {
return false
}
if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitSimplificationOfNonTrivialConstBooleanExpressions)) {
return true
@@ -430,6 +438,7 @@ private class ConstantExpressionEvaluatorVisitor(
parent is KtWhenConditionWithExpression ||
parent is KtContainerNode && (parent.parent is KtWhileExpression || parent.parent is KtDoWhileExpression)
) {
val constantValue = constant.toConstantValue(builtIns.booleanType)
trace.report(Errors.NON_TRIVIAL_BOOLEAN_CONSTANT.on(expression, constantValue.value as Boolean))
}
return false
@@ -0,0 +1,20 @@
// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions
const val myF = false
const val myT = true
fun test(someBoolean: Boolean) {
val s = when (someBoolean) {
<!CONFUSING_BRANCH_CONDITION_WARNING!>true || true<!> -> 1
<!CONFUSING_BRANCH_CONDITION_WARNING!>false && false<!> -> 2
true -> 3
false -> 4
}
}
fun test_2(someBoolean: Boolean) {
val s = <!NO_ELSE_IN_WHEN!>when<!> (someBoolean) {
<!CONFUSING_BRANCH_CONDITION_WARNING!>true || true<!> -> 1
<!CONFUSING_BRANCH_CONDITION_WARNING!>false && false<!> -> 2
}
}
@@ -0,0 +1,20 @@
// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions
const val myF = false
const val myT = true
fun test(someBoolean: Boolean) {
val s = when (someBoolean) {
<!CONFUSING_BRANCH_CONDITION_WARNING, DUPLICATE_LABEL_IN_WHEN, NON_TRIVIAL_BOOLEAN_CONSTANT!>true || true<!> -> 1
<!CONFUSING_BRANCH_CONDITION_WARNING, DUPLICATE_LABEL_IN_WHEN, NON_TRIVIAL_BOOLEAN_CONSTANT!>false && false<!> -> 2
true -> 3
false -> 4
}
}
fun test_2(someBoolean: Boolean) {
val s = when (someBoolean) {
<!CONFUSING_BRANCH_CONDITION_WARNING, NON_TRIVIAL_BOOLEAN_CONSTANT!>true || true<!> -> 1
<!CONFUSING_BRANCH_CONDITION_WARNING, NON_TRIVIAL_BOOLEAN_CONSTANT!>false && false<!> -> 2
}
}
@@ -0,0 +1,7 @@
package
public const val myF: kotlin.Boolean = false
public const val myT: kotlin.Boolean = true
public fun test(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit
public fun test_2(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit
@@ -0,0 +1,21 @@
// FIR_IDENTICAL
// LANGUAGE: +ProhibitSimplificationOfNonTrivialConstBooleanExpressions
const val myF = false
const val myT = true
fun test(someBoolean: Boolean) {
val s = when (someBoolean) {
<!CONFUSING_BRANCH_CONDITION_WARNING!>true || true<!> -> 1
<!CONFUSING_BRANCH_CONDITION_WARNING!>false && false<!> -> 2
true -> 3
false -> 4
}
}
fun test_2(someBoolean: Boolean) {
val s = <!NO_ELSE_IN_WHEN!>when<!> (someBoolean) {
<!CONFUSING_BRANCH_CONDITION_WARNING!>true || true<!> -> 1
<!CONFUSING_BRANCH_CONDITION_WARNING!>false && false<!> -> 2
}
}
@@ -0,0 +1,7 @@
package
public const val myF: kotlin.Boolean = false
public const val myT: kotlin.Boolean = true
public fun test(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit
public fun test_2(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit
@@ -4948,6 +4948,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/divideByZero.kt");
}
@Test
@TestMetadata("duplicateLabelWithNonTrivialCondition_disabled.kt")
public void testDuplicateLabelWithNonTrivialCondition_disabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.kt");
}
@Test
@TestMetadata("duplicateLabelWithNonTrivialCondition_enabled.kt")
public void testDuplicateLabelWithNonTrivialCondition_enabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.kt");
}
@Test
@TestMetadata("equals.kt")
public void testEquals() throws Exception {