[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"); 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 @Test
@TestMetadata("equals.kt") @TestMetadata("equals.kt")
public void testEquals() throws Exception { public void testEquals() throws Exception {
@@ -4942,6 +4942,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/divideByZero.kt"); 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 @Test
@TestMetadata("equals.kt") @TestMetadata("equals.kt")
public void testEquals() throws Exception { public void testEquals() throws Exception {
@@ -4942,6 +4942,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/divideByZero.kt"); 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 @Test
@TestMetadata("equals.kt") @TestMetadata("equals.kt")
public void testEquals() throws Exception { 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.cfg.pseudocode.instructions.eval.MagicKind
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings 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.EventOccurrencesRange
import org.jetbrains.kotlin.contracts.description.canBeRevisited import org.jetbrains.kotlin.contracts.description.canBeRevisited
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
@@ -1321,7 +1322,11 @@ class ControlFlowProcessor(
builder.bindLabel(doneLabel) builder.bindLabel(doneLabel)
mergeValues(branches, expression) mergeValues(branches, expression)
WhenChecker.checkDuplicatedLabels(expression, trace) WhenChecker.checkDuplicatedLabels(
expression,
trace,
languageVersionSettings ?: LanguageVersionSettingsImpl.DEFAULT
)
} }
override fun visitObjectLiteralExpression(expression: KtObjectLiteralExpression) { override fun visitObjectLiteralExpression(expression: KtObjectLiteralExpression) {
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.cfg
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns 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.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
@@ -357,7 +359,11 @@ object WhenChecker {
fun containsNullCase(expression: KtWhenExpression, context: BindingContext) = fun containsNullCase(expression: KtWhenExpression, context: BindingContext) =
WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, true).isEmpty() 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 if (expression.subjectExpression == null) return
val checkedTypes = HashSet<Pair<KotlinType, Boolean>>() val checkedTypes = HashSet<Pair<KotlinType, Boolean>>()
@@ -407,7 +413,7 @@ object WhenChecker {
} }
false -> { false -> {
// already found bad constant in previous branches // already found bad constant in previous branches
val isTrivial = constant.isTrivial() val isTrivial = constant.isTrivial(constantExpression, languageVersionSettings)
if (isTrivial) { if (isTrivial) {
// this constant is trivial -> report on first non trivial constant // this constant is trivial -> report on first non trivial constant
val reportOn = notTrivialBranches.remove(constant)!! val reportOn = notTrivialBranches.remove(constant)!!
@@ -420,7 +426,7 @@ object WhenChecker {
} }
null -> { null -> {
// met constant for a first time // met constant for a first time
val isTrivial = constant.isTrivial() val isTrivial = constant.isTrivial(constantExpression, languageVersionSettings)
checkedConstants[constant] = isTrivial checkedConstants[constant] = isTrivial
if (!isTrivial) { if (!isTrivial) {
notTrivialBranches[constant] = constantExpression notTrivialBranches[constant] = constantExpression
@@ -446,8 +452,15 @@ object WhenChecker {
} }
} }
private fun CompileTimeConstant<*>.isTrivial(): Boolean { private fun CompileTimeConstant<*>.isTrivial(
return !usesVariableAsConstant 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) { 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.isBoolean
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import java.math.BigInteger import java.math.BigInteger
import java.util.* import java.util.*
@@ -369,6 +368,17 @@ class ConstantExpressionEvaluator(
KotlinBuiltIns.isArray(type) -> isTypeParameterOrArrayOfTypeParameter(type.arguments.singleOrNull()?.type) KotlinBuiltIns.isArray(type) -> isTypeParameterOrArrayOfTypeParameter(type.arguments.singleOrNull()?.type)
else -> type.constructor.declarationDescriptor is TypeParameterDescriptor 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 return null
} }
@Suppress("warnings")
private fun shouldSkipComplexBooleanValue( private fun shouldSkipComplexBooleanValue(
expression: KtExpression, expression: KtExpression,
constant: CompileTimeConstant<*> constant: CompileTimeConstant<*>
): Boolean { ): Boolean {
if (constant.isError) return false if (!ConstantExpressionEvaluator.isComplexBooleanConstant(expression, constant)) {
val constantValue = constant.toConstantValue(builtIns.booleanType) return false
if (!constantValue.getType(constantExpressionEvaluator.module).isBoolean()) return false }
if (expression is KtConstantExpression || constant.parameters.usesVariableAsConstant) return false
if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitSimplificationOfNonTrivialConstBooleanExpressions)) { if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitSimplificationOfNonTrivialConstBooleanExpressions)) {
return true return true
@@ -430,6 +438,7 @@ private class ConstantExpressionEvaluatorVisitor(
parent is KtWhenConditionWithExpression || parent is KtWhenConditionWithExpression ||
parent is KtContainerNode && (parent.parent is KtWhileExpression || parent.parent is KtDoWhileExpression) 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)) trace.report(Errors.NON_TRIVIAL_BOOLEAN_CONSTANT.on(expression, constantValue.value as Boolean))
} }
return false 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"); 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 @Test
@TestMetadata("equals.kt") @TestMetadata("equals.kt")
public void testEquals() throws Exception { public void testEquals() throws Exception {