[FE 1.0] Support proper DUPLICATE_LABEL_IN_WHEN reporting for deprecated complex boolean expressions
^KT-50385 ^KT-39883
This commit is contained in:
committed by
teamcity
parent
eb753eac83
commit
8a58e98530
+12
@@ -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 {
|
||||||
|
|||||||
+12
@@ -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 {
|
||||||
|
|||||||
+12
@@ -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) {
|
||||||
|
|||||||
+15
-6
@@ -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
|
||||||
|
|||||||
+20
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -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
|
||||||
|
|
||||||
+21
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -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
|
||||||
|
|
||||||
Generated
+12
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user